最近面试了几家公司,其中有两家问到了webpack的性能优化方案。本人也就答出来了代码分割和懒加载,对于其他的一些优化方案不甚了解,所以回答得不满意。
于是在此总结了一下目前主要的性能优化方案。
温馨提示,本套方案基于webpack4。还有什么方案漏了的话,望补充,谢谢各位。
webpack性能优化
webpack性能优化主要分为两类,开发环境的性能优化和生产环境的性能优化。下面针对这两类来进行梳理。
开发环境性能优化
开发环境的性能优化主要是用来方便我们开发,节约我们的时间的,这主要是用在调试的阶段。主要会涉及到source-map的使用。
- 优化打包构建速度
- 优化调试功能
生产环境性能优化
生产环境的优化就比较多了,减小代码体积,减少首屏时间,优化构建速度,优化代码运行性,公共代码块提出等。
- 优化打包构建速度
- 优化代码运行的性能
- 减小代码体积
- 公共代码块提出
一、开发环境调试代码 source-map
sourcemap是一种技术,提供源代码到构建后代码的映射。(如果构建后代码出错了,通过映射关系,可以追踪到源代码错误,而不会只追踪到框架后面的)
source-map的配置非常简单,只需要在webpack中配置
{
devtool: 'source-map'
}
配置了之后,再构建,就会生成一个对应的map文件,用于保存代码的映射关系。这些东西看不懂,也不需要看懂。
并且注意,devtool不止source-map这一个值。还有inline-source-map等值,构建之后表现的效果又有所不同。
{
devtool: 'inline-source-map'
}
这样的配置会让构建后的文件夹中没有map文件。转而把之前生成的map文件直接加到对应js文件的后面。如下图所示:
这样做的好处就是,因为是内联的map文件,webpack少构建了资源,构建速度更快。
几种方式构建的效果如下图
这里要特地说一下, nosources-source-map,其可以让发生错误之后,点击错误信息追踪不到源码,可以隐藏源码信息。
例如代码中如果引入了一个没有的css文件ac.css
import './css/ac.css'
浏览器控制台报错如下:
但你点击错误信息,会提示你不能看到index.js内部的内容,
Could not load content for webpack:///./src/index.js (HTTP error: status code 404, net::ERR_UNKNOWN_URL_SCHEME)
于是,源码信息得到了隐藏。
总结
-
开发环境: source-map 和 inline-source-map
-
生产环境
- 隐藏源码用 nosources-source-map。
- 不隐藏源码 直接source-map或者inline-source-map。
二、 生产环境webpack缓存
webpack缓存可以通过给文件名添加hash值来实现,
hash: 每个文件都生成自己的hash
我们设置设置 output 的 filename 或者 MiniCssExtractPlugin 插件内的filename分别为
// 针对js文件
[name].[hash:10].js
// 针对css文件
[name].[hash:10].css
这样的配置,会让每个文件名都包含自己的hash值。
如果不去修改相应资源,打包后hash值不会改变。但是一旦有修改资源,就会触发hash值的变化。 这里我们修改了入口js文件之后再构建。
资源变化,反应到线上就是会重新请求对应的资源。
chunkhash: 每个代码块都生成一个hash
如果把上面的 [hash: 10] 改为 [chunkhash: 10],那么项目分为2个代码块,1.built 和 built。这两个代码会分别生成不同的hash值。
并且:
- 改1.built的话,built不会改变。
- 改built的话,1.built不会改变。
比如修改了入口文件index.js的代码,打包结果并不会影响到1.built.js的hash值。如下:
contenthash: 每个资源都会生成自己hash
这样做的好处是,如果js资源改变只生成js资源的hash 不会影响css资源,改谁谁变。
这个是webpack缓存的主要实现方式。
三、tree shaking
字面意思树摇,或者树震。 用来剔除无用的资源和那些没有用到的代码,减少打包体积。
条件:
- production模式打包,且用es6模块化
- 在package.json中实现
{
"sideEffects": [
"*.css",
"*.less"
]
}
-
sideEffects: false 代表所有代码都没有副作用 都可以进行tree shaking,问题:css打包文件会消失,慎用。
-
sideEffects: [".css", ".less"],表示除了css和less文件,其他文件都要tree-shaking。
四、code split 代码分割
主要研究js代码的代码分割
1.根据入口文件代码分割
{
// 多入口代码分割 可以进行多页面应用
entry: {
main: './src/js/index.js',
test: './scr/js/test.js',
}
}
2.optimization分割
webpack配置如下代码,主要用于将node_modules中的代码单独打包成1个chunk 最终输出 将别人的第三方的东西拿出来单独打包 而且会分析多入口文件中有没有公共的依赖,如果有,会打包成单独的一个chunk
optimization: {
splitChunks: {
chunks: 'all'
}
}
3.import()函数代码分割
import(/* webpackChunkName: 'test' */'./index2').then(() => {
console.log(333)
}).catch(() => {
console.log(222)
})
会将index2单独打包。
五、懒加载 预加载
懒加载
一般用import()函数进行懒加载 事件中加载,then方法再处理逻辑
document.getElementById("btn").onclick = function() {
// 懒加载
import(/* webpackChunkName: 'test' */'./test').then((mul) => {
console.log(mul(4, 5))
})
}
绑定事件中用import()函数,会在事件触发时才加载相应资源。
预加载
webpackPrefetch: true 预加载 在import函数中配置webpackPrefetch,会对资源进行预加载,提高事件触发的时候的效率。
document.getElementById("btn").onclick = function() {
// 懒加载
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then((mul) => {
console.log(mul(4, 5))
})
}
六、PWA 渐进式网络开发
利用workbox webpack中利用workbox-webpack-plugin
const workboxWebpackPlugin = require('workbox-webpack-plugin')
new workboxWebpackPlugin.GenerateSW({
clientsClaim: true, // 帮助serviceworker快速启动 删除旧的serviceworker
skipWaiting: true // 跳过等待
})
判断serviceworker是否可用
// 在项目入口
if ('serviceworker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceworker.register('/service-worker.js').then(() => {
console.log('注册成功');
}).catch(
console.log('注册失败');
)
})
}
七、多进程打包
webpack4 用 thread-loader
八、externals 防止某些依赖打包进我们的项目
比如jquery 我们需要用cdn 就用webpack的externals属性来让其不打包进项目
{
externals: {
jquery: 'jQuery'
}
}
九、oneof
作用:提升构建速度,避免每个文件都被所有loader过一遍,因为任何一个文件,构建过程中,在遇到第一个与之对应的loader后,不会再往下进行。
正常来讲,所有文件在执行的时候,都要将loader中的rules过一遍,如果符合,就被对应loader处理,不符合则直接过。这样对性能不好,为了解决这个问题,使用oneof。
oneOf里面的loader只匹配一个。不能有两个配置处理同一种类型的文件。
oneOf: [
{
test: /\.css$/,
use: [......]
},
{
test: /\.less$/,
use: [......]
}
]
配置loader为oneof,因为loader解析都是从下往上来的,一个css文件也要先经过less-loader解析,这就会降低构建速率。如果配置oneof,css文件就会只经过css-loader处理。