webpack常見面試點(持續更新中)

webpack打包原理

模塊化機制

webpack並不強制你使用某種模塊化方案,而是經過兼容全部模塊化方案讓你無痛接入項目。有了webpack,你能夠隨意選擇你喜歡的模塊化方案,至於怎麼處理模塊之間的依賴關係及如何按需打包,webpack會幫你處理好的。css

關於模塊化的一些內容,能夠看看我以前的文章:js的模塊化進程html

核心思想:

  • 一切皆模塊:

正如js文件能夠是一個「模塊(module)」同樣,其餘的(如css、image或html)文件也可視做模 塊。所以,你能夠require(‘myJSfile.js’)亦能夠require(‘myCSSfile.css’)。這意味着咱們能夠將事物(業務)分割成更小的易於管理的片斷,從而達到重複利用等的目的。前端

  • 按需加載:

傳統的模塊打包工具(module bundlers)最終將全部的模塊編譯生成一個龐大的bundle.js文件。可是在真實的app裏邊,「bundle.js」文件可能有10M到15M之大可能會致使應用一直處於加載中狀態。所以Webpack使用許多特性來分割代碼而後生成多個「bundle」文件,並且異步加載部分代碼以實現按需加載。node

文件管理

每一個文件都是一個資源,能夠用require/import導入js 每一個入口文件會把本身所依賴(即require)的資源所有打包在一塊兒,一個資源屢次引用的話,只會打包一份 對於多個入口的狀況,其實就是分別獨立的執行單個入口狀況,每一個入口文件不相干(可用CommonsChunkPlugin優化)webpack

打包原理

webpack只是一個打包模塊的機制,只是把依賴的模塊轉化成能夠表明這些包的靜態文件。並非什麼commonjs或者amd之類的模塊化規範。webpack就是識別你的 入口文件。識別你的模塊依賴,來打包你的代碼。至於你的代碼使用的是commonjs仍是amd或者es6的import。webpack都會對其進行分析。來獲取代碼的依賴。webpack作的就是分析代碼,轉換代碼,編譯代碼,輸出代碼。webpack自己是一個node的模塊,因此webpack.config.js是以commonjs形式書寫的(node中的模塊化是commonjs規範的)es6

webpack中每一個模塊有一個惟一的id,是從0開始遞增的。整個打包後的bundle.js是一個匿名函數自執行。參數則爲一個數組。數組的每一項都爲個function。function的內容則爲每一個模塊的內容,並按照require的順序排列。web

// webpack.config.js
module.exports = {
  entry:'./a.js',
  output:{
    filename:'bundle.js'
  }
};
複製代碼
// a.js
var b = require('./b.js');

console.log('a');

b.b1();
複製代碼
// b.js
exports.b1 = function () {
  console.log('b1')
};

exports.b2 = function () {
  console.log('b2')
};
複製代碼

以上代碼咱們打包處理的js爲express

// bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // Check if module is in cache
/******/        if(installedModules[moduleId])
/******/            return installedModules[moduleId].exports;

/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var b = __webpack_require__(1);

    console.log('a');

    b.b1();


/***/ },
/* 1 */
/***/ function(module, exports) {

    exports.b1 = function () {
      console.log('b1')
    };

    exports.b2 = function () {
      console.log('b2')
    };

/***/ }
/******/ ]);
複製代碼

咱們看到_webpack_require是模塊加載函數,接收模塊id(對,webpack中每一個模塊都會有一個獨一無二的id,其實也就是在IIFE傳參數組中的索引值(0,1,2.....) a依賴b,因此在a中調用webpack加載模塊的函數npm

// 1是模塊b的id
var b = __webpack_require__(1);
複製代碼

上面是使用的commonjs規範書寫的json

不管什麼模塊規範書寫。咱們的webpack進行識別後打包的內容不會相差不少,webpack有優秀的語法分析能力,支持 CommonJs AMD 等規範。

webpack爲何能把任何形式的資源都視做模塊呢?

由於loader機制。不一樣的資源採用不一樣的loader進行轉換。CMD、AMD 、import、css 、等都有相應的loader去進行轉換。那爲何咱們平時寫的es6的模塊機制,不用增長import的loader呢。由於咱們使用了babel把import轉換成了require。而且Webpack 2 將增長對 ES6 模塊的原生支持而且混用 ES六、AMD 和 CommonJS 模塊。這意味着 Webpack 如今能夠識別 import 和 export 了,不須要先把它們轉換成 CommonJS 模塊的格式:

webpack對於es模塊的實現,也是基於本身實現的webpack_require 和webpack_exports ,裝換成相似於commonjs的形式。es6 module是靜態的依賴,因此在運行前進行代碼轉換,這裏的實現是將全部導出項做爲一個對象的屬性,在入口文件執行時,去遞歸的加載模塊。

loader原理

在解析對於文件,會自動去調用響應的loader,loader 本質上是一個函數,輸入參數是一個字符串,輸出參數也是一個字符串。固然,輸出的參數會被當成是 JS 代碼,從而被 esprima 解析成 AST,觸發進一步的依賴解析。

webpack會按照從右到左的順序執行loader。

利用webpack解決跨域問題

假設前端在3000端口,服務端在4000端口,咱們經過 webpack 配置的方式去實現跨域。 首先,咱們在本地建立一個 server.js:

let express = require('express');

let app = express();

app.get('/api/user', (req, res) => {
    res.json({name: '劉小夕'});
});

app.listen(4000);
複製代碼

執行代碼(run code),如今咱們能夠在瀏覽器中訪問到此接口: http://localhost:4000/api/user。

在 index.js 中請求 /api/user,修改 index.js 以下:

//須要將 localhost:3000 轉發到 localhost:4000(服務端) 端口
fetch("/api/user")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(err => console.log(err));
複製代碼

咱們但願經過配置代理的方式,去訪問 4000 的接口。 配置代理 修改 webpack 配置:

//webpack.config.js
module.exports = {
    //...
    devServer: {
        proxy: {
            "/api": "http://localhost:4000"
        }
    }
}
複製代碼

從新執行 npm run dev,能夠看到控制檯打印出來了 {name: "劉小夕"},實現了跨域。

大多狀況,後端提供的接口並不包含 /api,即:/user,/info、/list 等,配置代理時,咱們不可能羅列出每個api。

修改咱們的服務端代碼,並從新執行。

//server.js
let express = require('express');

let app = express();

app.get('/user', (req, res) => {
    res.json({name: '劉小夕'});
});

app.listen(4000);
複製代碼

儘管後端的接口並不包含 /api,咱們在請求後端接口時,仍然以 /api 開頭,在配置代理時,去掉 /api,修改配置:

//webpack.config.js
module.exports = {
    //...
    devServer: {
        proxy: {
            '/api': {
                target: 'http://localhost:4000',
                pathRewrite: {
                    '/api': ''
                }
            }
        }
    }
}
複製代碼

從新執行 npm run dev,在瀏覽器中訪問: http://localhost:3000/,控制檯中也打印出了{name: "劉小夕"},跨域成功,

webpack 如何區分不一樣環境

目前爲止咱們 webpack 的配置,都定義在了 webpack.config.js 中,對於須要區分是開發環境仍是生產環境的狀況,咱們根據 process.env.NODE_ENV 去進行了區分配置,可是配置文件中若是有多處須要區分環境的配置,這種顯然不是一個好辦法。

更好的作法是建立多個配置文件,如: webpack.base.js、webpack.dev.js、webpack.prod.js。

  • webpack.base.js 定義公共的配置
  • webpack.dev.js:定義開發環境的配置
  • webpack.prod.js:定義生產環境的配置

webpack-merge 專爲 webpack 設計,提供了一個 merge 函數,用於鏈接數組,合併對象。

npm install webpack-merge -D
複製代碼
const merge = require('webpack-merge');
merge({
    devtool: 'cheap-module-eval-source-map',
    module: {
        rules: [
            {a: 1}
        ]
    },
    plugins: [1,2,3]
}, {
    devtool: 'none',
    mode: "production",
    module: {
        rules: [
            {a: 2},
            {b: 1}
        ]
    },
    plugins: [4,5,6],
});
//合併後的結果爲
{
    devtool: 'none',
    mode: "production",
    module: {
        rules: [
            {a: 1},
            {a: 2},
            {b: 1}
        ]
    },
    plugins: [1,2,3,4,5,6]
}
複製代碼

webpack.config.base.js 中是通用的 webpack 配置,以 webpack.config.dev.js 爲例,以下:

//webpack.config.dev.js
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.config.base');

module.exports = merge(baseWebpackConfig, {
    mode: 'development'
    //...其它的一些配置
});
複製代碼

而後修改咱們的 package.json,指定對應的 config 文件:

//package.json
{
    "scripts": {
        "dev": "cross-env NODE_ENV=development webpack-dev-server --config=webpack.config.dev.js",
        "build": "cross-env NODE_ENV=production webpack --config=webpack.config.prod.js"
    },
}
你可使用 merge 合併,也可使用 merge.smart 合併,merge.smart 在合併loader時,會將同一匹配規則的進行合併,webpack-merge 的說明文檔中給出了詳細的示例。
複製代碼

按需加載

webpack打包圖片原理

todo

webpack怎樣在首頁輸出項目版本,分支信息

todo

參考 blog.csdn.net/weixin_3378…

webpack優化

參考【前端必知】Webpack性能優化

相關文章
相關標籤/搜索