webpack-CommonChunkPlugin

webpack

Webpack 打包例子講解

CommonChunkPlugin 參數(shù)詳解

Webpack 的 HMR 原理分析

Compiler 和 Compilation 對象

{
  name: string, // or
  names: string[],
  // The chunk name of the commons chunk. An existing chunk can be selected by passing a name of an existing chunk.
  // If an array of strings is passed this is equal to invoking the plugin multiple times for each chunk name.
  // If omitted and `options.async` or `options.children` is set all chunks are used, otherwise `options.filename`
  // is used as chunk name.
  // When using `options.async` to create common chunks from other async chunks you must specify an entry-point
  // chunk name here instead of omitting the `option.name`.
  filename: string,
  //指定該插件產(chǎn)生的文件名稱烈菌,可以支持 output.filename 中那些支持的占位符宽菜,如 [hash]、[chunkhash]、[id] 等涩维。如果忽略這個這個屬性,那么原始的文件名稱不會被修改(一般是 output.filename 或者 output.chunkFilename,可以查看 compiler 和 compilation 部分第一個例子)。但是這個配置不允許和 `options.async` 一起使用
  minChunks: number|Infinity|function(module, count)  boolean,
  //至少有 minChunks 的 chunk 都包含指定的模塊饰豺,那么該模塊就會被移出到 common chunk 中。這個數(shù)值必須大于等于2允蜈,并且小于等于沒有使用這個插件應(yīng)該產(chǎn)生的 chunk 數(shù)量冤吨。如果傳入 `Infinity`,那么只會產(chǎn)生 common chunk饶套,但是不會有任何模塊被移到這個 chunk中 (沒有一個模塊會被依賴無限次)漩蟆。通過提供一個函數(shù),也可以添加自己的邏輯妓蛮,這個函數(shù)會被傳入一個參數(shù)表示產(chǎn)生的 chunk 數(shù)量
  chunks: string[],
  // Select the source chunks by chunk names. The chunk must be a child of the commons chunk.
  // If omitted all entry chunks are selected.
  children: boolean,
  // If `true` all children of the commons chunk are selected
  deepChildren: boolean,
  // If `true` all descendants of the commons chunk are selected

  async: boolean|string,
  // If `true` a new async commons chunk is created as child of `options.name` and sibling of `options.chunks`.
  // It is loaded in parallel with `options.chunks`.
  // Instead of using `option.filename`, it is possible to change the name of the output file by providing
  // the desired string here instead of `true`.
  minSize: number,
  //所有被移出到 common chunk 的文件的大小必須大于等于這個值
}
children 屬性

其中在 Webpack 中很多 chunk 產(chǎn)生都是通過 require.ensure 來完成的爆安。先看看下面的例子:

import asd from './req'
//home.js 
if( asd() ) {
    require.ensure([], () => {
        const Dabao = require('./dabao');
    });
} else {
    require.ensure([], () => {
        const Xiaobao = require('./xiaobao');
    });
}

//main.js
import React from 'react';
import common from './common'
import xiaobao from './xiaobao'

兩個文件一個是 通過require.ensure 引入,一個直接引入仔引,那么通過require.ensure引入也生成兩個文件 0.js 和 1.js.
通過配置 children扔仓,可以將動態(tài)產(chǎn)生的這些 chunk 的公共的模塊也抽取出來。如果配置了多個入口文件(假如還有一個 main1.js)咖耘,那么這些動態(tài)產(chǎn)生的 chunk 中可能也會存在相同的模塊(此時 main1翘簇、main 會產(chǎn)生四個動態(tài) chunk )。而這個 children 配置就是為了這種情況而產(chǎn)生的儿倒。通過配置 children版保,可以將動態(tài)產(chǎn)生的這些 chunk 的公共的模塊也抽取出來。

當(dāng)配置children后我們抽取公共模塊的chunks集合

這時候在插件commonChunkPlugin中的抽取公共chunk的代碼:

commonChunks.forEach(function processCommonChunk(commonChunk, idx) {
                    let usedChunks;
                    if(Array.isArray(selectedChunks)) {
                        usedChunks = chunks.filter(chunk => chunk !== commonChunk && selectedChunks.indexOf(chunk.name) >= 0);
                    } else if(selectedChunks === false || asyncOption) {
                        usedChunks = (commonChunk.chunks || []).filter((chunk) => {
                            // we can only move modules from this chunk if the "commonChunk" is the only parent
                            return asyncOption || chunk.parents.length === 1;
                        });
                     
                     //(1)
                     var util = require('util'); 
                    console.log('------------->commonChunk',util.inspect(commonChunk, {showHidden:true,depth:4})); 
                    //如果name=['main','main1']那么表示以入口文件開始單獨打包夫否,此時的commonChunk就是我們的main.js和main1.js
                    //其chunks屬性表示所有require.ensure的產(chǎn)生的chunk
                    } else {
                        if(commonChunk.parents.length > 0) {
                            compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + commonChunk.name + ")"));
                            return;
                        }
                        //如果found>=idx表示該chunk后續(xù)會作為一個獨立的chunk來處理(獨立打包)彻犁,所以此處不做修改
                        //這里的chunks集合是包括所有entry中配置的和在name數(shù)組中配置的,如果entry中不存在這個chunk而name中存在凰慈,直接創(chuàng)建一個空的chunk汞幢!
                        usedChunks = chunks.filter((chunk) => {
                            const found = commonChunks.indexOf(chunk);
                            if(found >= idx) return false;
                            return chunk.hasRuntime();
                        });
                    }
                    let asyncChunk;
                    if(asyncOption) {
                        asyncChunk = compilation.addChunk(typeof asyncOption === "string" ? asyncOption : undefined);
                        asyncChunk.chunkReason = "async commons chunk";
                        asyncChunk.extraAsync = true;
                        asyncChunk.addParent(commonChunk);
                        commonChunk.addChunk(asyncChunk);
                        commonChunk = asyncChunk;
                    }
                    const reallyUsedModules = [];
                    if(minChunks !== Infinity) {
                        const commonModulesCount = [];
                        const commonModules = [];
                        usedChunks.forEach((chunk) => {
                            chunk.modules.forEach((module) => {
                                const idx = commonModules.indexOf(module);
                                if(idx < 0) {
                                    commonModules.push(module);
                                    commonModulesCount.push(1);
                                } else {
                                    commonModulesCount[idx]++;
                                }
                            });
                        });
                        const _minChunks = (minChunks || Math.max(2, usedChunks.length));
                        commonModulesCount.forEach((count, idx) => {
                            const module = commonModules[idx];
                            if(typeof minChunks === "function") {
                                if(!minChunks(module, count))
                                    return;
                            } else if(count < _minChunks) {
                                return;
                            }
                            if(module.chunkCondition && !module.chunkCondition(commonChunk))
                                return;
                            reallyUsedModules.push(module);
                        });
                    }
                    if(minSize) {
                        const size = reallyUsedModules.reduce((a, b) => {
                            return a + b.size();
                        }, 0);
                        if(size < minSize)
                            return;
                    }
                    const reallyUsedChunks = new Set();
                    reallyUsedModules.forEach((module) => {
                        usedChunks.forEach((chunk) => {
                            if(module.removeChunk(chunk)) {
                                reallyUsedChunks.add(chunk);
                            }
                        });
                        commonChunk.addModule(module);
                        module.addChunk(commonChunk);
                    });
                    if(asyncOption) {
                        for(const chunk of reallyUsedChunks) {
                            if(chunk.isInitial()) continue;
                            chunk.blocks.forEach((block) => {
                                block.chunks.unshift(commonChunk);
                                commonChunk.addBlock(block);
                            });
                        }
                        asyncChunk.origins = Array.from(reallyUsedChunks).map((chunk) => {
                            return chunk.origins.map((origin) => {
                                const newOrigin = Object.create(origin);
                                newOrigin.reasons = (origin.reasons || []).slice();
                                newOrigin.reasons.push("async commons");
                                return newOrigin;
                            });
                        }).reduce((arr, a) => {
                            arr.push.apply(arr, a);
                            return arr;
                        }, []);
                    } else {
                        usedChunks.forEach((chunk) => {
                            chunk.parents = [commonChunk];
                            chunk.entrypoints.forEach((ep) => {
                                ep.insertChunk(commonChunk, chunk);
                            });
                            commonChunk.addChunk(chunk);
                        });
                    }
                    if(filenameTemplate)
                        commonChunk.filenameTemplate = filenameTemplate;
                });

我們看看其中的chunk.hasRuntime函數(shù):

hasRuntime() {
    if(this.entrypoints.length === 0) return false;
    return this.entrypoints[0].chunks[0] === this;
  }

我們看看chunk.entrypoints內(nèi)部表示(見data.js下名字為main的chunk):

entrypoints: 
   [ Entrypoint { name: 'main', chunks: [ [Circular], [length]: 1 ] },
     [length]: 1 ]

所以只有頂級chunk才會有執(zhí)行環(huán)境。我們順便看看在commonchunkplugin的處理方式:

//usedChunks是已經(jīng)抽取了公共模塊的chunk
   usedChunks.forEach(function(chunk) {
            chunk.parents = [commonChunk];
            chunk.entrypoints.forEach(function(ep) {
              ep.insertChunk(commonChunk, chunk);
              //在每一個移除了公共代碼的chunk之前插入commonChunk
            });
            //每一個移除了公共chunk的chunk.entrypoints添加一個chunk
            commonChunk.addChunk(chunk);
          });

我們順便也給出EntryPoint的代碼:

class Entrypoint {
  constructor(name) {
    this.name = name;
    this.chunks = [];
  }
  unshiftChunk(chunk) {
    this.chunks.unshift(chunk);
    chunk.entrypoints.push(this);
  }
  insertChunk(chunk, before) {
    const idx = this.chunks.indexOf(before);
    if(idx >= 0) {
      this.chunks.splice(idx, 0, chunk);
    } else {
      throw new Error("before chunk not found");
    }
    chunk.entrypoints.push(this);
  }
  getFiles() {
    let files = [];
    for(let chunkIdx = 0; chunkIdx < this.chunks.length; chunkIdx++) {
      for(let fileIdx = 0; fileIdx < this.chunks[chunkIdx].files.length; fileIdx++) {
        if(files.indexOf(this.chunks[chunkIdx].files[fileIdx]) === -1) {
          files.push(this.chunks[chunkIdx].files[fileIdx]);
        }
      }
    }

    return files;
  }
}
module.exports = Entrypoint;
 usedChunks.forEach(function(chunk,index) {
           var util = require('util'); 
               console.log('------------->before'+chunk.name,util.inspect(chunk.entrypoints, {showHidden:true,depth:2})); 
            chunk.parents = [commonChunk];
            chunk.entrypoints.forEach(function(ep) {
              ep.insertChunk(commonChunk, chunk);
            });
              var util = require('util'); 
          console.log('------------->end'+chunk.name,util.inspect(chunk.entrypoints, {showHidden:true,depth:2})); 
            commonChunk.addChunk(chunk);
          });

commonChunkPlugin抽取之前的chunk

Chunk {
  id: null,
  ids: null,
  debugId: 1000,
  name: 'main',
  //chunk對應(yīng)的name
  modules: [],
  //該chunk來自于哪些module,main這個chunk來自于src/index.js,該module包含兩個RequireEnsureDependenciesBlock
  entrypoints: 
   [ Entrypoint { name: 'main', chunks: [ [Circular], [length]: 1 ] },
     [length]: 1 ],
  //入口文件為main:'./src/index.js',而entryPoint對應(yīng)的chunk為對當(dāng)前chunk的循環(huán)引用
 chunks:[],//當(dāng)前chunk的子級chunks有哪些微谓,如require.ensure都是當(dāng)前chunk的子級chunk
 parents: [ [length]: 0 ],
 //當(dāng)前chunk的父級chunk集合森篷,沒有經(jīng)過commonChunkPlugin處理main是頂級chunk
 blocks: [ [length]: 0 ],
 //module.blocks表示模塊包含的塊RequireEnsureDependenciesBlock等的個數(shù),chunk.block表示當(dāng)前chunk包含的block的個數(shù)
 origins: 
 //當(dāng)前chunk從哪些模塊得到
   [ { module: 
        NormalModule {
          dependencies: [ [Object], [length]: 1 ],
          blocks: [ [Object], [Object], [length]: 2 ],
          variables: [ [length]: 0 ],
          context: '/Users/klfang/Desktop/webpack-chunkfilename/src',
          reasons: [ [length]: 0 ],
          debugId: 1000,
          lastId: null,
          id: null,
          portableId: null,
          index: 0,
          index2: 12,
          depth: 0,
          used: true,
          usedExports: true,
          providedExports: true,
          chunks: [ [Circular], [length]: 1 ],
          warnings: [ [Object], [length]: 1 ],
          dependenciesWarnings: [ [length]: 0 ],
          errors: [ [length]: 0 ],
          dependenciesErrors: [ [length]: 0 ],
          strict: true,
          meta: {},
          request: '/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
          userRequest: '/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
          rawRequest: './src/index.js',
          parser: 
           Parser {
             _plugins: [Object],
             options: undefined,
             scope: undefined,
             state: undefined },
          resource: '/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
          loaders: [ [Object], [Object], [length]: 2 ],
          //module.fileDependencies: An array of source file paths included into a module. This includes the source JavaScript file itself (ex: index.js), and all dependency asset files (stylesheets, images, etc) that it has required. Reviewing dependencies is useful for seeing what source files belong to a module.
          //這個module沒有引入相應(yīng)的css/html/image等
          fileDependencies: 
           [ '/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
             [length]: 1 ],
          contextDependencies: [ [length]: 0 ],
          error: null,
          _source: 
           OriginalSource {
             _value: '\'use strict\';\n\n// var $ = require(\'jquery\');\n\n// $(\'body\').html(\'Hello\');\n\n\n// import $ from \'jquery\';\n// $(\'body\').html(\'Hello\');\n\n\n// import Button from \'./Components/Button\';\n// const button = new Button(\'google.com\');\n//  button.render(\'a\');\n\n//code splitting\nif (document.querySelectorAll(\'a\').length) {\n    require.ensure([], function () {\n        var Button = require(\'./Components/Button\').default;\n        var button = new Button(\'google.com\');\n        button.render(\'a\');\n    });\n}\n\nif (document.querySelectorAll(\'h1\').length) {\n    require.ensure([], function () {\n        var Header = require(\'./Components/Header\').default;\n        new Header().render(\'h1\');\n    });\n}',
             _name: '/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js' },
          assets: {},
          built: true,
          _cachedSource: null,
          issuer: null,
          building: undefined,
          buildTimestamp: 1487137260364,
          cacheable: true },
       loc: undefined,
       name: 'main' },
     [length]: 1 ],
  files: [ [length]: 0 ],
  // An array of output filenames generated by the chunk. 
  //You may access these asset sources from the compilation.assets table.
  //表示這個chunk產(chǎn)生的輸出文件豺型,此處為頂級chunk沒有輸出文件產(chǎn)生
  _removeAndDo:{},
  addChunk:{},
  addParent:{},
  //入口模塊
  entryModule: 
   NormalModule {
     dependencies: 
      [ ConstDependency {},
        [length]: 1 ],
     blocks: 
      [ RequireEnsureDependenciesBlock {
          dependencies: [ [Object], [Object], [Object], [length]: 3 ],
          blocks: [ [length]: 0 ],
          variables: [ [length]: 0 ],
          chunkName: null,
          chunks: [ [Object], [length]: 1 ],
          module: [Circular],
          loc: SourceLocation { start: [Object], end: [Object] },
          expr: 
           Node {
             type: 'CallExpression',
             start: 313,
             end: 488,
             loc: [Object],
             range: [Object],
             callee: [Object],
             arguments: [Object] },
          range: [ 345, 486, [length]: 2 ],
          chunkNameRange: null,
          parent: [Circular] },
        RequireEnsureDependenciesBlock {
          dependencies: [ [Object], [Object], [Object], [length]: 3 ],
          blocks: [ [length]: 0 ],
          variables: [ [length]: 0 ],
          chunkName: null,
          chunks: [ [Object], [length]: 1 ],
          module: [Circular],
          loc: SourceLocation { start: [Object], end: [Object] },
          expr: 
           Node {
             type: 'CallExpression',
             start: 543,
             end: 678,
             loc: [Object],
             range: [Object],
             callee: [Object],
             arguments: [Object] },
          range: [ 575, 676, [length]: 2 ],
          chunkNameRange: null,
          parent: [Circular] },
        [length]: 2 ],
     variables: [ [length]: 0 ],
     context: '/Users/klfang/Desktop/webpack-chunkfilename/src',
     reasons: [ [length]: 0 ],
     debugId: 1000,
     lastId: null,
     id: null,
     portableId: null,
     index: 0,
     index2: 12,
     depth: 0,
     used: true,
     usedExports: true,
     providedExports: true,
     chunks: [ [Circular], [length]: 1 ],
     warnings: [],
     dependenciesWarnings: [ [length]: 0 ],
     errors: [ [length]: 0 ],
     dependenciesErrors: [ [length]: 0 ],
     strict: true,
     meta: {},
     request: '/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
     userRequest: '/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
     rawRequest: './src/index.js',
     parser: 
      Parser {
        _plugins: {},
        options: undefined,
        scope: undefined,
        state: undefined },
     resource: '/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
     loaders: 
      [ { loader: '/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js' },
        { loader: '/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js' },
        [length]: 2 ],
     fileDependencies: 
      [ '/Users/klfang/Desktop/webpack-chunkfilename/src/index.js',
        [length]: 1 ],
     contextDependencies: [ [length]: 0 ],
     error: null,
     _source: 
      OriginalSource {
        _value: '\'use strict\';\n\n// var $ = require(\'jquery\');\n\n// $(\'body\').html(\'Hello\');\n\n\n// import $ from \'jquery\';\n// $(\'body\').html(\'Hello\');\n\n\n// import Button from \'./Components/Button\';\n// const button = new Button(\'google.com\');\n//  button.render(\'a\');\n\n//code splitting\nif (document.querySelectorAll(\'a\').length) {\n    require.ensure([], function () {\n        var Button = require(\'./Components/Button\').default;\n        var button = new Button(\'google.com\');\n        button.render(\'a\');\n    });\n}\n\nif (document.querySelectorAll(\'h1\').length) {\n    require.ensure([], function () {\n        var Header = require(\'./Components/Header\').default;\n        new Header().render(\'h1\');\n    });\n}',
        _name: '/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js' },
     assets: {},
     built: true,
     _cachedSource: null,
     issuer: null,
     building: undefined,
     buildTimestamp: 1487137260364,
     cacheable: true } }
}

我們看看commonchunkplugin中的處理方式(else部分):

if(Array.isArray(selectedChunks)) {
          usedChunks = chunks.filter(function(chunk) {
            if(chunk === commonChunk) return false;
            //此時commonChunk的內(nèi)容是已經(jīng)存在于最終的文件中了仲智,如果它不是手動創(chuàng)建的chunk
            //去掉下例的jquery,得到usedChunks集合
            return selectedChunks.indexOf(chunk.name) >= 0;
          });
        } else if(selectedChunks === false || asyncOption) {
          usedChunks = (commonChunk.chunks || []).filter(function(chunk) {
            // we can only move modules from this chunk if the "commonChunk" is the only parent
            //只是把一級子chunk的公共內(nèi)容提取出來,如果有一個子chunk的父級chunk有兩個那么不會被提取出來姻氨。
            return asyncOption || chunk.parents.length === 1;
          });
        } else {
          //如果當(dāng)前的這個chunk有多個父級chunk钓辆,那么不會提取的
          if(commonChunk.parents.length > 0) {
            compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + commonChunk.name + ")"));
            return;
          }
          usedChunks = chunks.filter(function(chunk) {
            var found = commonChunks.indexOf(chunk);
            if(found >= idx) return false;
            return chunk.hasRuntime();
          });
        }
chunks

通過 chunks 參數(shù)來選擇來源的 chunk。這些 chunk 必須是 common-chunk 的子級 chunk肴焊。如果沒有指定前联,那么默認(rèn)選中所有的入口 chunk。下面給出一個例子:

module.exports = {
    entry : {
        main : './src/main.js',
        home : './src/home.js',
        common : ['jquery'],
        common2 : ['react']
    },
    output : {
        path: path.join(__dirname, 'build'),
        filename: '[name].js'
    },
     plugins: [
        new CommonsChunkPlugin({
            name: "common",
            minChunks: 2,
            chunks : ["main","home"]
        })
    ]
}

main","home" 公共模塊會被打包到 common里面

minChunks 為函數(shù)

可以給 minChunks 傳入一個函數(shù)抖韩。CommonsChunkPlugin 將會調(diào)用這個函數(shù)并傳入 module 和 count 參數(shù)蛀恩。這個 module 參數(shù)用于指定某一個 chunks 中所有的模塊,而這個 chunk 的名稱就是上面配置的 name/names 參數(shù)茂浮。這個 module 是一個 NormalModule 實例双谆,有如下的常用屬性:
module.context:表示存儲文件的路徑,比如 '/my_project/node_modules/example-dependency'
module.resource:表示被處理的文件名稱席揽,比如 '/my_project/node_modules/example-dependency/index.js'
而 count 參數(shù)表示指定的模塊出現(xiàn)在多少個 chunk 中顽馋。這個函數(shù)對于細(xì)粒度的操作 CommonsChunk 插件還是很有用的』闲撸可自己決定將那些模塊放在指定的 common chunk 中寸谜,下面是官網(wǎng)給出的一個例子:

new webpack.optimize.CommonsChunkPlugin({
  name: "my-single-lib-chunk",
  filename: "my-single-lib-chunk.js",
  minChunks: function(module, count) {
    //如果一個模塊的路徑中存在 somelib 部分,而且這個模塊出現(xiàn)在 3 個獨立的 chunk 或者 entry 中属桦,那么它就會被抽取到一個獨立的 chunk 中熊痴,而且這個 chunk 的文件名稱為 "my-single-lib-chunk.js"他爸,而這個 chunk 本身的名稱為 "my-single-lib-chunk"
    return module.resource && (/somelib/).test(module.resource) && count === 3;
  }
});

第二個例子

new webpack.optimize.CommonsChunkPlugin({
  name: "vendor",
  minChunks: function (module) {
    // this assumes your vendor imports exist in the node_modules directory
    return module.context && module.context.indexOf("node_modules") !== -1;
  }
})

其中 CommonsChunkPlugin 插件還有一個更加有用的配置,即用于將 Webpack 打包邏輯相關(guān)的一些文件抽取到一個獨立的 chunk 中果善。但是此時配置的 name 應(yīng)該是 entry 中不存在的诊笤,這對于線上緩存很有作用。因為如果文件的內(nèi)容不發(fā)生變化巾陕,那么 chunk 的名稱不會發(fā)生變化讨跟,所以并不會影響到線上的緩存。比如下面的例子:

new webpack.optimize.CommonsChunkPlugin({
  name: "manifest",
  minChunks: Infinity
})

但是你會發(fā)現(xiàn)抽取 manifest 文件和配置 vendor chunk 的邏輯不一樣鄙煤,所以這個插件需要配置兩次:

[
  new webpack.optimize.CommonsChunkPlugin({
    name: "vendor",
    minChunks: function(module){
      return module.context && module.context.indexOf("node_modules") !== -1;
    }
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: "manifest",
    minChunks: Infinity
  }),
]
runtime

當(dāng)代碼在瀏覽器中運行的時候晾匠,Webpack 使用 runtime 和 manifest 來處理應(yīng)用中的模塊化關(guān)系。其中包括在模塊存在依賴關(guān)系的時候梯刚,加載和解析特定的邏輯凉馆,而解析的模塊包括已經(jīng)在瀏覽中加載完成的模塊和那些需要懶加載的模塊本身。

manifest

一旦應(yīng)用程序中乾巧,如 index.html 文件句喜、一些 bundle 和各種靜態(tài)資源被加載到瀏覽器中,會發(fā)生什么沟于?精心安排的 /src 目錄的文件結(jié)構(gòu)現(xiàn)在已經(jīng)不存在咳胃,所以 Webpack 如何管理所有模塊之間的交互呢?這就是 manifest 數(shù)據(jù)用途的由來……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旷太,一起剝皮案震驚了整個濱河市展懈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌供璧,老刑警劉巖存崖,帶你破解...
    沈念sama閱讀 223,207評論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異睡毒,居然都是意外死亡来惧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評論 3 400
  • 文/潘曉璐 我一進店門演顾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來供搀,“玉大人,你說我怎么就攤上這事钠至「鹋埃” “怎么了?”我有些...
    開封第一講書人閱讀 170,031評論 0 366
  • 文/不壞的土叔 我叫張陵棉钧,是天一觀的道長屿脐。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么的诵? 我笑而不...
    開封第一講書人閱讀 60,334評論 1 300
  • 正文 為了忘掉前任万栅,我火速辦了婚禮,結(jié)果婚禮上奢驯,老公的妹妹穿的比我還像新娘申钩。我一直安慰自己,他們只是感情好瘪阁,可當(dāng)我...
    茶點故事閱讀 69,322評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著邮偎,像睡著了一般管跺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上禾进,一...
    開封第一講書人閱讀 52,895評論 1 314
  • 那天豁跑,我揣著相機與錄音,去河邊找鬼泻云。 笑死艇拍,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宠纯。 我是一名探鬼主播卸夕,決...
    沈念sama閱讀 41,300評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼婆瓜!你這毒婦竟也來了快集?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,264評論 0 277
  • 序言:老撾萬榮一對情侶失蹤廉白,失蹤者是張志新(化名)和其女友劉穎个初,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猴蹂,經(jīng)...
    沈念sama閱讀 46,784評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡院溺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,870評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了磅轻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珍逸。...
    茶點故事閱讀 40,989評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瓢省,靈堂內(nèi)的尸體忽然破棺而出弄息,到底是詐尸還是另有隱情,我是刑警寧澤勤婚,帶...
    沈念sama閱讀 36,649評論 5 351
  • 正文 年R本政府宣布摹量,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缨称。R本人自食惡果不足惜凝果,卻給世界環(huán)境...
    茶點故事閱讀 42,331評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睦尽。 院中可真熱鬧器净,春花似錦、人聲如沸当凡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沿量。三九已至浪慌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間朴则,已是汗流浹背权纤。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乌妒,地道東北人汹想。 一個月前我還...
    沈念sama閱讀 49,452評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像撤蚊,于是被迫代替她去往敵國和親古掏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,995評論 2 361

推薦閱讀更多精彩內(nèi)容