代码分离是 Webpack 最引人注目的特性之一。
(1)这个特性可以将代码分离到不同的bundle中,然后按需加载或并行加载这些文件
。
(2)所谓bundle,就是我们打包出来的文件。代码分离可以用于获取更小的bundle
,以及控制资源加载优先级
,如果使用合理,可以极大影响加载时间
。从而提高首屏加载速度。
(3)常用的代码分离方法:
1、在src下面建一个another-module.js,这个模块里面引入loadash
npm install loadash --save-dev
import _ from 'lodash'
console.log(_.join(['Another', 'module', 'loaded!'], ' '))
webpack.config.js:
module.exports = {
entry: { // *定义两个入口文件,分别key值为index和another
index: './src/index.js',
another: './src/another-module.js'
},
output: {
filename: '[name].bundle.js' // *这里的[name]就会去取entry的key值。例如, another.bundle.js
}
}
执行 npx webpack 可以看到 another.bundle.js 的大小有1.38MB,引入的 loadash.js 也打包在了 another.bundle.js 中。打开 app.html ,可以发现 script 里面引入了这两个 bundle.js.
2、这时候,修改 index.js,即另一个入口文件,也加上 another.bundle.js 的以下代码:
import _ from 'lodash'
console.log(_.join(['Another', 'module', 'loaded!'], ' '))
打包后发现 index.js 也变大了,同时也打包了 loadash.js。
3、因此,entry 入口起点这种方式存在一些问题:
(1)如果入口 chunk 之间包含重复的模块,这些重复模块会被引入各自的 bundle中;
(2)这个方法不够灵活,不能动态地将核心应用程序逻辑中的代码拆分出来。
1、入口依赖entry的方式
webpack.config.js
module.exports = {
entry: {
index: { // *不采用字符串,采用对象的方式
import: './src/index.js',
dependOn: 'shared'
},
another: {
import: './src/another-module.js',
dependOn: 'shared'
},
shared: 'loadash' // *表示当上面两个模块中有loadash这个模块的时候,就会把他抽离出来,并且把它取名为shared这样一个chunk
},
output: {
filename: '[name].bundle.js'
}
}
打包后可以看到 sahred.bundle.js 被打包出来了,说明把 loadash.js 打包到这个chunk 里面了;其他两个 js 的大小也减小了。并且在 app.html 里面也引入了这三个 js.
2、split-chunk-plugin
除了上面 entry 的方式,还可以使用 split-chunks-plugin 这个插件,这个插件也可以把模块中依赖的公共文件给抽离成单独的 chunk。
webpack.config.js:
module.exports = {
entry: {
index: './src/index.js', // *采用字符串
another: './src/another-module.js'
},
output: {
filename: '[name].bundle.js'
},
optimization: {
splitChunks: { // *使用split-chunks-plugin插件
chunks: 'all'
}
}
}
当涉及到动态代码拆分
时,webpack 提供了两个类似的技术。
第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案的import()
语法来实现动态导入。
第二种,则是 webpack 的遗留功能,使用 webpack 特定的 require.ensure
。
1、通过import() 语法动态引入
(1)创建 async-module.js 文件
function getComponent() {
// *采用import的方式引入
return import('lodash')
.then(({default: _}) => {
const element = document.createElement('div')
element.innerHTML = _.join(['Hello', 'webpack'], ' ')
return element
})
}
getComponent().then((element) => {
document.body.appendChild(element)
})
(2)导入模块
在入口文件 index.js 中 引入刚刚建的async-module.js
index.js:
import ./async-module.js
可以看到公共模块单独打包在了一个文件。
懒加载或者按需加载,是一种很好的优化网页或应用的方式。
这种方式实际上是,先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立引用或即将引用另外一些新的代码块。
这样加快了应用的初始加载速度,减轻了它的总体体积,因为有些代码可能永远不会加载。
创建一个math.js,主页面里面点击按钮调用其中的函数。
math.js
export const add = (x, y) => {
return x + y
}
export const minus = (x, y) => {
return x - y
}
index.js
const button = document.createElement('button')
button.textContent = '点击执行加法运算'
button.addEventListener('click', () => {
// *使用import动态加载的方式实现懒加载。在这里就是点击了按钮才加载 math.js 文件
// *import里面有一个魔法注释/**/,表示 webpack 打包后的文件名为 math.js
import(/* webpackChunkName: 'math' */'./math.js').then(({ add }) => {
console.log(add(4, 5))
})
})
document.body.appendChild(button)
上例中,采用 import 动态导入的方式,引入了 math.js。打包后发现生成了一个 src_math_js.bundle.js,且其他打包文件大小没有变大。刷新 html 页面,没有加载src_math_js.bundle.js,点击按钮后才加载了这个文件。说明我们懒加载成功了。
import里面有一个注释/**/,我们称它为魔法注释
,表示 webpack 打包后的文件名为 math.js。再次执行打包之后发现 src_math_js.bundle.js 变成了 math.js。
Webpack v4.6.0+ 增加了对预获取和预加载的支持。
在声明 import 时,使用下面这些内置指令,可以让 webpack 输出"resource hint(资源提示)",来告知浏览器:
prefetch
: 预获取。在将来某些导航下可能需要的资源;
preload
: 预加载。在当前导航下可能需要的资源。
(1)prefetch
编辑 index.js 文件,在上一个例子的魔法注释中加第二句webpackPrefetch: true
const button = document.createElement('button')
button.textContent = '点击执行加法运算'
button.addEventListener('click', () => {
// *添加预获取的魔法注释
import(/* webpackChunkName: 'math', webpackPrefetch: true */'./math.js').then(({ add }) => {
console.log(add(4, 5))
})
})
document.body.appendChild(button)
打包之后发现,浏览器中 html 页面头部追加了一个 ,这表示
浏览器会在闲置时间预获取 main.js 文件
。且在浏览器中可以观察到,还没有点击按钮就加载好了 math.js。
这种方式比刚刚的懒加载还要优秀一点。
(2) preload
也是改一哈魔法注释。效果和懒加载类似,点击按钮之后才加载文件。是并行加载。
/* webpackChunkName: 'math', webpackPreload: true */
prefetch 和 preload 的区别: