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 插件還是很有用的??勺约簺Q定將那些模塊放在指定的 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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容