代码分离
代码分离是webpack最重要的特性,可将代码分离到不同的bundle中,方便资源的按需加载或并行加载,从而影响加载时间。 常用代码分割方法:
-
入口起点:使用
entry配置手动分离代码 -
防止重复:使用
Entry dependencied或者SplitChunksPlugin去重和分离chunk。 -
动态导入:通过模块的内联函数调用来分离代码。
入口起点
- index.js
import _ from 'lodash'
import { add } from './a.js'
function component() {
const element = document.createElement('div');
// lodash(目前通过一个 script 引入)对于执行这一行是必需的
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
console.log(add(2,5))
document.body.appendChild(component());
- another-module.js
import _ from 'lodash';
console.log(_.join(['Another', 'module', 'loaded!'], ' '));
- webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index:'./src/index.js', // 入口1
Another:'./src/another-module.js', // 入口2
},
devtool: 'inline-source-map', // 此处可准确定位出出错模块的信息(sourcemap)
plugins:[
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title:'管理输出'
})
],
output: {
filename: '[name].main.js',
path: path.resolve(__dirname, 'dist'),
},
};
以上方式存在一些隐患:
- 如果入口chunk之间包含一些重复的模块,那些重复的模块会被引入到各个不同的bundle中。
- 同时通过这样的方式不能动态的将核心应用程序从代码中分割 以上第一点隐患很容易看出,我们在index.js文件中引入了lodash,但是在another-module.js文件从还需要重复引入才可使用。
防止重复
通过在在文件中配置dependOn选项,这样可以在多个chunk之间共享模块。提取两个文件之间共同的依赖项,在打包时生成单独的模块文件,从而减少模块代码的大小。 如果我们要在一个 HTML 页面上使用多个入口时,还需设置 optimization.runtimeChunk: 'single'
- webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: 'lodash',
},
devtool: 'inline-source-map', // 此处可准确定位出出错模块的信息(sourcemap)
plugins:[
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title:'管理输出'
})
],
output: {
filename: '[name].main.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
runtimeChunk: 'single',
}
};
动态代码导入
当涉及到动态代码拆分时,webpack使用了两个类似的方式。
- 1.使用import语法进行动态导入,在打包后的代码,会将动态导入的包自动打包成一个vendors。此时不用去配置。
import('lodash').then(_ => {
// Do something with lodash (a.k.a '_')...
});
- 2.使用webpack特定的
require.ensure
require.ensure(['b'], function(require) {
var c = require('c');
// Do something special...
});
预获取/预加载模块(prefetch/preload)
结合import实现
function button(){
const ele = document.createElement('button')
ele.innerHTML = 'button'
ele.onclick = function(){
import(/* webpackPrefetch: true */ './a').then(res=>{
console.log(res)
})
}
return ele
}
指令配置会生成<link rel="prefetch" as="script" href="src_a_js.main.js"></head>并追加到页面头部,表示浏览器闲置时间预取src_a_js.main.js文件。
也可通过浏览器的performance查看资源获取的方式
与prefetch指令相比,preload有部分不同:
- preload chunk会在父chunk加载时,以并行的方式加载,prefetch chunk会在父chunk加载结束后开始加载;
- preload chunk具有中等优先级,并立即下载,prefetch chunk会在浏览器闲置时下载。
- preload chunk 会在父组件中立即请求,用于当下时刻。prefetch chunk 会用于未来某个时刻。
- 浏览器对prefetch的支持度约为70%,对于preload的约50%
缓存
从浏览器获取服务器上的资源通常是比较耗时的。浏览器通过命中缓存来降低网络流量,使网站加载速度更快,然而如果在部署时不更改资源的文件名,浏览器可能会被认为他没有更新,就会使用其缓存版本。
filename
在output.filename配置contenthash将根据资源内容创建出唯一hash,当资源内容发生变化时间,[contenthash]也会变化,在打包之后的文件中,文件名会包含一个唯一hash值。
提取引导模板
在代码分离中,splitchunksplugin可以用于将模块分离到单独的bundle中。可使用 optimization.runtimeChunk 选项将 runtime 代码拆分为一个单独的 chunk。将其设置为 single 来为所有 chunk 创建一个 runtime bundle
asset vendors-node_modules_lodash_lodash_js.0120508cd546f3f22c19.js 1.37 MiB [emitted] [immutable] (id hint: vendors)
asset runtime.c84f401aa9ba9c916849.js 34.1 KiB [emitted] [immutable] (name: runtime)
asset index.e429b554c829d01de78b.js 3.47 KiB [emitted] [immutable] (name: index)
asset src_a_js.eded08a4d8cac1861aca.js 939 bytes [emitted] [immutable]
asset index.html 303 bytes [emitted]
Entrypoint index 37.6 KiB = runtime.c84f401aa9ba9c916849.js 34.1 KiB index.e429b554c829d01de78b.js 3.47 KiB
runtime modules 9.86 KiB 13 modules
通常将第三方库例如lodash、Vue、React提取到单独的vendor chunk文件中,因为其很少进行改动,因此可利用科幻段的缓存机制,通过命中缓存来消除请求,并减少向服务器获取资源,同时保证代码的准确性。 这可以通过使用 SplitChunksPlugin 插件的 cacheGroups 选项来实现。我们在 optimization.splitChunks 添加 cacheGroups 参数并构建:
- 添加前:
asset vendors-node_modules_lodash_lodash_js.0120508cd546f3f22c19.js 1.37 MiB [emitted] [immutable] (id hint: vendors)
asset runtime.c84f401aa9ba9c916849.js 34.1 KiB [emitted] [immutable] (name: runtime)
asset index.c8aef78ae031df99c50e.js 5.85 KiB [emitted] [immutable] (name: index)
asset src_a_js.eded08a4d8cac1861aca.js 939 bytes [emitted] [immutable]
asset index.html 303 bytes [emitted]
Entrypoint index 40 KiB = runtime.c84f401aa9ba9c916849.js 34.1 KiB index.c8aef78ae031df99c50e.js 5.85 KiB
- 添加后:
asset vendorstest.a477574b3560f21a228f.js 1.37 MiB [emitted] [immutable] (name: vendorstest) (id hint: vendor)
asset runtime.2bba52c28f516bccc037.js 33.9 KiB [emitted] [immutable] (name: runtime)
asset index.2dfd9b3c84a3ec76ba9e.js 3.86 KiB [emitted] [immutable] (name: index)
asset src_a_js.eded08a4d8cac1861aca.js 939 bytes [emitted] [immutable]
asset index.html 362 bytes [emitted]
Entrypoint index 1.41 MiB = runtime.2bba52c28f516bccc037.js 33.9 KiB vendorstest.a477574b3560f21a228f.js 1.37 MiB index.2dfd9b3c84a3ec76ba9e.js 3.86 KiB
对比前后index文件的大小,在优化代码之前时5.85kb,优化后是3.56kb;
懒加载
懒加载或则按需加载,能够很好的优化网页。这种方式实际是在一些逻辑断点处分开,然后在一些代码块中完成某些操作。这样加快了应用的加载速度,减轻了提及,因为部分代码块可能永远不会被加载。
总结
webpack优化方式
- 1.为模块文件的名称设置hash值,以便缓存命中并更新;
- 2.抽离node_modules中的模块,通常node_modules在开发过程中的变更很少,因此可以利用浏览器的缓存,减少请求。
- 3.使用runtimeChunk抽离程序运行时刻的主代码 参考文档:
- webpack.docschina.org/blog/2020-1…
- webpack.docschina.org/guides/code…
- webpack.docschina.org/guides/deve…
- blog.fundebug.com/2019/04/11/…