本文中module统一翻译为模块,chunk翻译为块。阅读时请注意。
对于大型应用,将所有代码置于一个文件中是低效的,特别在某些代码块没有被加载的情况下。Webpack具有能将基础代码切分进能按需调用的“块(chunks)”中的特性。其中“块(chunks)”的概念在一些其他的打包工具中,被称之为“ 层(layers)”,“卷(rollups)”, 或是“片段(fragments)”。而这种能拆分基础代码的特性被称为“代码切割”。
这是一个可选特性。你可以在自己的基础代码中定义切割点。Webpack 关注依赖,输出文件与运行时间。
先澄清一个普遍性的错误:代码切割并不仅仅只是将普通代码取出放进一个可以共享的“块”中。更重要的是利用代码切割这个特性将代码拆分成可以按需加载的块。这可以实现在初始化的时候下载一个很小的文件,其后当随着应用的需求再按需加载代码。
定义一个代码切割点
AMD 与 CommonJs 规定了两种不同的方式来按需加载。两者都支持实现了切割点。
CommonJs: require.ensure
require.ensure(dependencies, callback)
require.ensure
方法保证了当使用回调函数时,所有的依赖都可以实现异步加载。回调函数作为require
函数的参数被传入。
例子:
require.ensure(["module-a", "module-b"], function(require) {
var a = require("module-a");
// ...
});
请注意:require.ensure
只进行模块的加载,而并非执行。
AMD: require
AMD规范定义了一个异步的require
方法:
require(dependencies, callback)
当调用时,所有的依赖都会被加载,回调函数调用加载模块的接口。
例子:
require(["module-a", "module-b"], function(a, b) {
// ...
});
注意:AMD的require
方法加载并执行了模块。在webpack中模块的执行是从左向右的。
注意:webpack是允许移除回调的。
ES6 模块
Webpack 不支持ES6的模块;需要直接使用require.ensure
与require
方法调用由你的编译器生成的模版。
Webpack 1.x.x (2.0.0正在路上) 无法原生支持与理解ES6模块。但是,你可以使用诸如babel这样的编译器在CommonJs或AMD模块中实现ES6中的import语法。这是一个有效的方式,但在实现动态加载时会产生警告。
新增的模块语法(import x from 'foo'
)有意被设计成静态方式,这就意味着你不能动态引用。
// INVALID!!!!!!!!!
['lodash', 'backbone'].forEach(name => import name )
所幸的是,存在用来处理动态使用的JavaScript API “加载” 规范:System.load
(或 System.import
)。这个API是上述require
的一个变形。然而,大多数编译器并不支持将System.load
编译成require.ensure
,因此,如果你必须要使用动态功能,你要直接使用它来实现动态的代码切割。
//静态引用
import _ from 'lodash'
//动态引用
require.ensure([], function(require) {
let contacts = require('./contacts')
})
块的内容
所有的依赖在遇到切割点时就会被放进新的块。依赖也按照递归方式被加入。
如果你传人一个函数表达式(或绑定一个函数表达式)作为回调函数在切割点时。webpack 会自动将所有在函数表达式中需要加载的依赖也放入块中。
块的选项
如果两个块包含相同的模块,他们将被合并为一个。这样会使得块有多个父级。
如果一个模块在一个块的所有父级都可获取,那么它将被从这个块中删去。
如果一个块包含另一个块中的所有模块,这就是一个仓库。这能同时满足多个块。
块的加载
有赖于控制项,块加载的运行时逻辑可以被添加到打包中。好比,对于web块可以通过jsonp被加载。一个块只被加载一次,平行调用将会被合并到一起。运行时会检查加载的块是否满足多个块。
块的类型
入口块
一个入口块包含运行时外加一个模块束。如果块包含模块0,运行时执行模块0。如果没有,则会等待包含模块0的包并执行(每次当遇到模块0的时候)。
普通块
普通模块并不包含运行时。他只包含模块束。其结构依赖于块的加载算法。好比,对于jsonp,模块会被包在一个jsonp回调函数中。块也能包含其满足的块id的数组。
初始块(非入口块)
初始块是一类普通块。唯一的区别就是由于它能计算初始加载事件这一优化使他变得更重要。块的类型可以在combination中使用CommonsChunkPlugin
生成。
切割应用与vendor代码
将你的app切割进两个文件,分别叫做app.js
与vendor.j
s,你可以通过vendor.js
加载文件。然后将其传给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
现在只包含你的应用代码,而没有其他依赖。他们都在vendor.bundle.js里。
需要在你的HTML页面中先于bundle.js加载vendor.bundle.js。
多入口块
通过控制多个入口点产生多个入口块是可行的。一个页面中入口块有且仅有一个(否则会报错)。
运行多个入口点
通过CommonsChunkPlugin
,运行时可以被移入普通块中。现在入口点在初始块中。当只有初始块被加载时,多入口块可以被加载。这使在单页面中使用多个入口点成为可能。
例子:
var webpack = require("webpack");
module.exports = {
entry: { a: "./a", b: "./b" },
output: { filename: "[name].js" },
plugins: [ new webpack.optimize.CommonsChunkPlugin("init.js") ]
}
优化
存在一些类优化插件可以通过一些列的标准合并块。下面列出一些:
LimitChunkCountPlugin
MinChunkSizePlugin
AggressiveMergingPlugin
块的命名
require.ensure
函数接受一个额外的第三参数。这必须是一个字符串。如果两个切割点传了相同的字符串,那么他们将使用相同的块。
require.include
require.include(request)
require.include
是一个 webpack 的标准函数,能为当前块添加一个模块,但并不执行。
例子:
require.ensure(["./file"], function(require) {
require("./file2");
});
// 相当于
require.ensure([], function(require) {
require.include("./file");
require("./file2");
});
如果一个模块存在于大量的子块中,require.include
将变得极为有用。在父级中的require.include
将会包含模块与在子级中的模块的实例将会消失。
例子
简单的例子
与bundle-loader有关的例子
与上下文有关的例子
与amd和上下文有关的例子
与reduplication有关的例子
块的命名
多入口块
多普通块
点击查看应用实例