webpack 性能优化
生产环境性能优化
- 1.优化打包构建速度
- oneOf
- 都不需要安装
- oneOf里面 loader只会匹配一个
- 注意: 不能有两个配置处理同一种类型的文件
- 缓存
- babel缓存
- 安装 babel-loader @babel/preset-env(解决兼容 只针对文件中需要兼容的代码导入)
// 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true
- 多进程打包 (thread-loader)
- 安装thread-loader
- 进程启动大概600ms,进程通信也有开销.只有工作消耗时间比较长,才需要多进程打包
<!-- 一般使用在babel-loader 对其进行优化 --> { loader:'thread-loader', options: { workers: 2 // 进程数 } },
- oneOf
- 2.优化代码运行的性能
-
缓存(hash-chunkhash-contenthash)
- 都不需要安装
- 用法:使用不同的hash值给css和js文件命名 实现局部刷新
output: { filename:"js/built.[contenthash:10].js", path: resolve(__dirname,'build') }, // MiniCssExtractPlugin作用代替style-loader 提取单独的css文件 new MiniCssExtractPlugin({ filename: 'css/built.[contenthash:10].[ext].css' }),
- hash就是webpack每次构建成功时的生成的唯一hash值
- 缓存缺点:只要是webpack重新打包那么所有的文件都会刷新
- chunkhash来自于同一个入口就同属于同一个chunkhash
- 缓存缺点:只要同一个入口文件中的某个文件改变比如css 那么js文件也会刷新
- contenthash文件自身的hash值 文件改变contenthash才会变 能让js css更好的持久化缓存
-
tree-shaking
- 无需安装 去除无用代码 减少代码体积
- 前提: 1.必须使用es6模块化 2.开启production环境
<!-- 在package.json中配置 (不配置 可能有些版本原因导致代码被删) --> // 问题:可能会把css / @babel/polyfill (副作用)文件干掉 "sideEffects": false // 所有的代码都没有副作用 <!-- 如果使用json配置建议下面这种写法 --> "sideEffects": ["*.css","*.less""] // 标记不要树摇css文件
-
code split
- 都不需要安装
- 将代码分割成多个包,可并行加载,也可以实现按需加载
- 单入口
// 单个入口文件 entry: './src/js/index.js', output: { filename:"js/built.[contenthash:10].js", path: resolve(__dirname,'build') }, // 第一种 可以将node_modules中代码单独打包一个chunk最终输出 自动分析chunk中有没有公共的文件。 // 如果有会打包成单独一个chunk optimization: { splitChunks: { chunks: 'all' } }, // 第二种 通过js代码,让某个文件被单独打包成一个chunk。 import动态导入语法,能将某个文件单独打包 import(/* webpackChunkName: 'test'*/'./test') .then(({mul,count})=>{ console.log(mul(2,5)); }) .catch(()=>{ // eslint-disable-next-line console.log('文件加载失败'); })
- 多入口
// 多个入口文件 entry: { index: './src/js/index.js', test: './src/js/test.js', }, output: { // [name] 取文件名 filename:"js/[name].[contenthash:10].js", path: resolve(__dirname,'build') }, // 第一种 可以将node_modules中代码单独打包一个chunk最终出 自动分析chunk中有没有公共的文件。 // 如果有会打包成单独一chunk optimization: { splitChunks: { chunks: 'all' } }, // 第二种 通过js代码,让某个文件被单独打包成一个chunk。import动态导入语法,能将某个文件单独打包 import(/* webpackChunkName: 'test'*/'./test') .then(({mul,count})=>{ console.log(mul(2,5)); }) .catch(()=>{ // eslint-disable-next-line console.log('文件加载失败'); })
-
懒加载/预加载 (js代码)
- 不需要安装
- 懒加载 js文件懒加载 (必须生成多个文件)
document.getElementById('btn').onclick = function () { import( /* webpackChunkName: 'test'*/ './test') .then(({mul}) => { console.log(mul(2, 6)) }) .catch(() => { }) }
- 预加载 Prefetch(慎用,兼容性差,不能确保加载完毕)
document.getElementById('btn').onclick = function () { import( /* webpackChunkName: 'test',webpackPrefetch:true*/ './test') .then(({ mul }) => { console.log(mul(2, 6)) }) .catch(() => {}) }
-
PWA
- 安装workbox-webpack-plugin插件、配置eslint
- 离线可访问技术(渐进式网络开发应用程序)
- 由serviceworker 和workbox-webpack-plugin组成
- 处理兼容性问题
<!-- eslint不认识浏览器变量 需在package,json "eslintConfig"中设置 --> "env":{ "browser": true }
- 用法
// pwa插件 new WorkboxWebpackPlugin.GenerateSW({ /* 1.帮助serviceworker快速启动 2.删除旧的 serviceworker */ clientsClaim: true, skipWaiting: true }), if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then(() => { console.log('sw注册成功'); }) .catch(() => { console.log('sw注册失败'); }); }); }
-
externals
- 无需安装
- 彻底不打包 资源引用cdn
externals: { // 拒绝jQuery包被打包进来 然后通过在html中cdn引入jquery jquery: 'jquery' }
-
dll
- 安装插件 AddAssetHtmlWebpackPlugin 导入webpack
- 单独打包需要的资源 打包一次 优点:当单独打包后 以后打包就不需要重复打包
- 新建webpack.dll.js文件 运行webpack --config webapck.dll.js指令生成文件夹
const {resolve} = require('path') const webpack = require('webpack') module.exports = { entry:{ jquery: ['jquery'] }, output:{ filename: '[name].js', path: resolve(__dirname,'dll'), library: '[name]_[hash:10]' }, module:{ }, plugins:[ // 打包一个 manifest.json --> 提供jquery映射 new webpack.DllPlugin({ name: '[name]_[hash]', path: resolve(__dirname,'dll/manifest.json') }) ], mode: 'production' }
- 将dll文件夹中的资源导入 并在html自动引入
// 告诉webpack哪些库不参与打包 同时使用时的名称也变了 new webpack.DllReferencePlugin({ manifest: resolve(__dirname,['dll/manifest.json']) }), // 将某个文件打包输出,并在HTML中自动引入该资源 new AddAssetHtmlWebpackPlugin({ filepath: resolve(__dirname,'dll/jquery.js') })
-
降低图片质量
例如 JPG 格式的图片,100% 的质量和 90% 质量的通常看不出来区别,尤其是用来当背景图的时候。我经常用 PS 切背景图时, 将图片切成 JPG 格式,并且将它压缩到 60% 的质量,基本上看不出来区别。压缩方法有两种:一是通过 webpack 插件 image-webpack-loader,二是通过在线网站进行压缩。
npm i -D image-webpack-loader webpack 配置
-
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use:[
{
loader: 'url-loader',
options: {
limit: 10000, /* 图片大小小于1000字节限制时会自动转成 base64 码引用*/
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
/*对图片进行压缩*/
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
]
}
开发环境性能优化(主要提高编码效率)
-
优化打包速度
- HMR
- 作用:一个模块发生变化,只会重新打包这一个模块(而不是全部打包)
- 开启devServer里面设置 hot:true
devServer:{ contentBase: resolve(__dirname,'build'), compress: true, // 启动gzip port: 3000, open: true, hot:true // 开启HMR },
- css必须使用style-loader
- js 判断兼容性是否开启了HMR 功能
// 在入口js中使用 if (module.hot) { // 一旦 module.hot 为true,说明开启HMR功能 module.hot.accept('./print.js', () => { // 方法会监听 print.js文件的变化,一旦发生变化,其它模块不会重新打包构建 // 会执行后面的回调函数 print(); }); }
- HMR
-
优化代码调试
- soure-map
-
source-map: 提供源代码到构建后代码映射技术(如果代码出错了,通过映射关系可以追踪到源代码错误)
-
[inline- | hidden-| eval-][nosources-][cheap-[module-]]source-map
-
推荐配置 eval-source-map(vue-cli或react默认此) / eval-cheap-module-source-map
-
- soure-map
-
source-map详解:
提供源代码到构建后代码映射技术(如果代码出错了,通过映射关系可以追踪到源代码错误) cheap只确定行 module会将loader的source map加入 [inline- | hidden-| eval-][nosources-][cheap-[module-]]source-map
-
错误代码准确信息 准确位置
source-map 生成在一个单独的map文件中 外部 inline-source-map 生成在js内部文件中 只生成一个source-map 内联 eval-source-map 生成在js内部文件中 每一个文件后面都会追加一个放在eval函数中的source-map 内联 cheap-module-source-map: 外部 module会将loader的source map加入
-
以下两个都是为了隐藏源码而生
( 错误代码错误原因 但是没有错误位置 不能追踪源代码错误位置 只能提示到构建后代码的位置)hidden-source-map 外部 错误代码准确信息 但是没有任何源代码信息 nosources-source-map: 外部 错误代码准确信息 准确位置(但是只精确到行) cheap-source-map: 外部
-
内联 和 外部的区别:
1.外部生成文件 2.内联的构建速度是更快的
-
开发环境:
速度快,调试友好 速度快(eval>inline>cheap>...) eval-cheap-source-map 最快 eval-source-map 其次 调试友好 source-map cheap-module-source-map cheap-source-map -- eval-source-map(vue-cli或react默认此) / eval-cheap-module-source-map
-
生产环境:
源码隐藏 调试友好 内联会让代码体积很大 基本不考虑 nosources-source-map 全部隐藏 hidden-source-map 只隐藏源代码 会提示构建后的代码 --> source-map/ cheap-module-source-map
-