【译】webpack文档翻译 代码分离

 英文原文

翻译目录传送门

转载请注明来源(sunshine-boy)

Code Splitting(代码分离)

对于大型网页应用,把所有代码放进一个单一的文件里并不高效,特别是代码中的某些部分只在某些环境下才需要。webpack有个将你代码库分离成chunks(块)的特性,chunks在需要时被加载。一些其他绑定器叫他们"layers","rollups",或者"fragments"。这个特性叫做Code Splitting。

这是一个可配置的特性。你可以在你的代码库里定义split point(分离点)。webpack只关心模块的依赖,输出的文件,和运行环境。

澄清一个常见的误解:Code Splitting不仅仅是共同的代码放进一个可共享的chunk。更值得关注的特性是Code Splitting可以被用来将代码分离成需求加载chunk(译注:在需求时进行加载)。这使得初次下载文件更小,并且当应用请求时,下载需求的代码。

定义一个split point
AMD和commonJs定义了加载需求代码的不同方法。两种方法都被支持并作为分离点:

commonJs: require.ensure

require.ensure(dependencies, callback)

require.ensure方法确保了当调用回调函数时,dependencies(依赖)的每个依赖可以被同步require。回调函数作为require.ensure的参数,调用时以require作为实参>

例子:

require.ensure(["module-a", "module-b"], function(require) {
  var a = require("module-a");
  // ...
});

注意:require.ensure仅加载模块,并不执行他们。(译注:使用require("module-a");时才是执行模块)

AMD:require
AMD标准定义了一个异步的require方法,以如下方式:

require(dependencies, callback)

当调用该方法时,所有dependencies被加载,并且回调函数调用时以依赖作为实参
例子:

require(["module-a", "module-b"], function(a, b) {
  // ...
});

注意:AMD加载并执行了模块。在webpack中,模块以从左到右的顺序执行。
注意:该方法不允许不写回调函数。

ES6模块
webpack不支持es6模块,请直接使用require.ensure或require,使用哪个取决于你的transpiler(转译器)生成了哪种模块格式。

webpack 1.x.x(2.0.0即将到来!)无法原生支持或理解es6模块。但是,你可以通过使用一个transpiler来解决这个问题,比如babel,将es6的import语法转为commonJs或AMD模块。这种方法非常有效,但在动态加载时有个重要的警告。

这种模块语法的附加物(import x from 'foo')是以可静态分析来设计的,这意外着你不可以使用动态import。

// 无效!!!!
['lodash', 'backbone'].forEach(name => import name )

幸运的是,存在一个js api "loader"标准,它定义来处理动态情况:System.load(或System.import)。这个api将成为上面require变种(译注:require.ensure,require)的原生等价物。但是,绝大多数转译器不支持将System.load调用转为require.ensure,所以你不得不直接使用require变种来实现动态Code Splitting。

//静态输入
import _ from 'lodash'

// 动态输入
require.ensure([], function(require) {
  let contacts = require('./contacts')
})

chunk内容
所有在一个split point里的依赖进入一个新chunk,依赖是被逐个加入的。
如果你传一个函数表达式(或绑定后的函数表达式)作为split point的回调函数,webpack会自动把这个函数里require的依赖放进它所在的chunk里。

chunk优化(译注:下面的三个如果比较抽象,建议看代码后再来理解)
如果两个chunk里有相同的模块,他们(译注:相同的模块)会被合并成一个chunk(译注:从两个父chunk中抽出来,形成一个commons chunk)。这会导致一个chunk会有多个父chunk。
如果一个模块在一个chunk的所有父chunk里都存在,该模块将从那个chunk里移除
如果一个chunk里存在另一个chunk需求的所有模块,那这个chunk就是存储代码的chunk,它满足多个chunk。

chunk加载
取决于配置选项里的target属性的值,chunk加载的运行逻辑被加入到这个chunk里。例如,如果target值为'web',那么chunk通过jsonp来加载。一个chunk只被加载一次,并行(译注:require.ensure的第一个数组参数)请求也会合并成一个。运行环境会检测已加载的chunk是否满足多个chunk。

chunk类型
Entry chunk(输入chunk)
一个entry chunk里有运行环境和一大堆模块。(译注:遇到一个require变种语句时)如果这个chunk里存在该模块,运行环境会执行它。如果不存在,运行环境等待存在该模块的chunk,并且执行它(当某个chunk里存在那个模chunk时)。

Normal chunk(普通chunk)
一个normalchunk里没有运行环境。里面只有一堆模块,chunk的结构取决于模chunk加载算法。例如,对于jsonp,所有模块被封装到一个jsonp回调函数里。chunk里也包括了一个该chunk满足的所有chunk的id的数组。

Initial chunk (non-entry)(初始chunk,非输入)
initial chunk是一个normal chunk。唯一的不同是,优化手段更加重视它,因为加载它的时间算进了初次加载时间(像输入chunk一样)。这种chunk类型在使用插件CommonsChunkPlugin时出现。

分离app和供应商代码
为了把你的app分离成两个文件,叫app.js和vendor.js,你可以在vendor.js里require供应商文件。然后把供应商名字传给插件CommonsChunkPlugin,如下:

var webpack = require("webpack");

module.exports = {
  entry: {
  app: "./app.js",
  vendor: ["jquery", "underscore", ...],
  },
  output: {
  filename: "bundle.js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", 
    /* filename= */"vendor.bundle.js")   ] };

这将会移除app里所有在vendor里出现的模块。bundle.js将只会存有你的app代码,没有任何它的依赖。这些在vendor里require的依赖被从app里移除后放进vendor.bundle.js。
在你的html网页里在bundle.js之前加载vendor.bundle.js。

<script src="vendor.bundle.js"></script>
<script src="bundle.js"></script>

多个entry chunk
配置多个entry point是可能的,这会导致多个entry chunk。entry chunk包含运行环境,并且一个页面只能有一个运行环境(存在例外)

运行多个entry point
使用插件CommonsChunkPlugin后,运行环境被移动到commons chunk里。entry point现在在initial chunk里。只能加载一个entry chunk(译注:commons chunk属于entry chunk),虽然可以加载多个initial chunk。这暴露了一个页面加载多个entry point的可能性。
例子:

var webpack = require("webpack");
module.exports = {
  entry: { a: "./a", b: "./b" },
  output: { filename: "initial-[name].js" },
  plugins: [ new webpack.optimize.CommonsChunkPlugin("commons.js") ]
}
<script src="commons.js"></script>
<script src="initial-a.js"></script>
<script src="initial-b.js"></script>

Commons chunk
插件CommonsChunkPlugin可以移除出现在多个entry chunk里的模块,生成一个新chunk(the commons chunk)。运行环境也被移到了commons chunk。这意味着旧的entry chunk变成新的initial chunk。查看插件列表

优化
有一些优化插件可以根据特定的标准把chunks合并。查看插件列表
  ·LimitChunkCountPlugin
  ·MinChunkSizePlugin
  ·AggressiveMergingPlugin

Named chunk(命名式chunk)
require.ensure接受第三个参数。它必须是字符串。如果两个
split point传相同的字符串,那么他们将使用相同的chunk。

require.include

require.include(request)

require.include是一个webpack专属函数,它向所在的chunk增加一个模块,但不执行他(声明也不需要写在bundle(译注:指require.ensure第一个参数)里)。
例子:

require.ensure(["./file"], function(require) {
  require("./file2");
});

// 等价于

require.ensure([], function(require) {
  require.include("./file");
  require("./file2");
});

require.include在当一个模块在多个子chunk里是非常有用。require.include在父chunk里的话,父chunk会包含它,并且移除在子chunk里的实例(译注:移除模块的代码,但是可以正常使用)。

实例

简单

bundle-loader

上下文

AMD与上下文

消除重复

named chunk

多个entry chunk

多个commons chunk

 

翻译目录传送门

posted on 2016-06-29 15:10  sunshine-boy  阅读(580)  评论(1编辑  收藏  举报

导航