【译】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里的实例(译注:移除模块的代码,但是可以正常使用)。
实例
posted on 2016-06-29 15:10 sunshine-boy 阅读(580) 评论(1) 编辑 收藏 举报