一、优化打包构建速度
1.oneOf
使用:在webpack.config.js的module的rules中将一些loader的配置放在oneOf的数组中即可
module: { rules: [ {//匹配js文件,后面的babel也会匹配js文件,所以将eslint放在外面 test: /\.js$/, exclude: /node_modules/,//排除node_modules下的文件 loader: 'eslint-loader', options: { fix: true//自动修复eslint的错误 } }, { oneOf: [ { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader' ], }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader', ], }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env',//预设,只是babel做怎么样的兼容性处理 { useBuiltIns: 'usage',// 按需加载 corejs: {//指定core-js的版本 version: 3 }, targets: {//指定兼容性到浏览器的哪个版本 chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17' } } ] ], } } ] } ] },
注意:oneOf中的loader一种文件类型只会匹配一个loader,如果有两个不同的loader配置则后面的loader会失效,需要把其中一个放在oneOf的外面
2.babel缓存
设置babel里loader的配置:cacheDirectory:true
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env',//预设,只是babel做怎么样的兼容性处理 { useBuiltIns: 'usage',// 按需加载 corejs: { version: 3 },//指定core-js的版本 targets: {//指定兼容性到浏览器的哪个版本 chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17' } } ] ], cacheDirectory: true//设置babel缓存,第二次构建时会读取之前的缓存 } }
3.多进程打包
使用的loader:thread-loader
加上thread-loader,进程启动大概需要600ms,进程通信也需要开销,只有工作消耗时间长,才需要多进程打包,因此一般用于babel中
{ test: /\.js$/, use: [ { loader: 'thread-loader',//开启多进程打包,一定要注意顺序,在babel-loader后执行 options: { workers: 2 } }, { loader: 'babel-loader', options: { presets: [[ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: '3' }, targets: { chrome: '60', firefox: '60' } } ]] } } ] }
4.externals
拒绝一些库参与打包,但在页面使用cdn链接的方式引入(不然没有效果),比如jquery
externals:{ jquery:'jQuery'//拒绝jquery参与打包,从而使构建速度更快 }
5.dll
类似externals指示哪些库是不参与打包的,但是dll会对某些库单独进行打包,将多个库打包为一个chunk
比如将node_modules中的库拆分打包为不同的文件/chunk,更加利于性能优化
(1)新建一个webpack.dll.js文件进行配置(以打包jquery为例)
配置写好后使用webpack --config webpack.dll.js命名生成打包的库以及manifest.json文件到dll目录下
let { resolve } = require('path') const webpack = require('webpack') module.exports = { entry: { jquery: ['jquery']//最终打包生成的名字为jquery,要打包的库是数组中的 }, output: { filename: '[name].js',//打包输出的名字取的是entry的key值 path: resolve(__dirname, 'dll'),//打包输出的路径 library: '[name]_[hash]'//打包暴露出去的库的名字 }, plugins: [ new webpack.DefinePlugin({//打包生成一个mainfest.json文件,提供映射关系,告诉webpack下次打包的时候不要再打包其中的内容 name: '[name]_[hash]',//映射库的暴露的内容名称 path: resolve(__dirname, 'dll/manifest.json')//输出文件路径 }) ], mode: 'production', }
(2)在webpack.config.js中引入并配置插件
const AddAssetHtmlWebpackPlugin = require('add-asset-html-Webpack-plugin') new webpack.DllReferencePlugin({//告诉webpack哪些库不参与打包 manifest:resolve(__dirname,'dll/manifest.json') }) new AddAssetHtmlWebpackPlugin({//将某个文件打包输出,并在html中自动引入 filepath:resolve(__dirname,'dll/jquery.js') })
二、优化代码运行的性能
1.缓存(hash-chunkhash-contenthash)
(1)资源缓存出现的问题:
在缓存有效期间,如果代码有更新,重新打包构建刷新的话,浏览器的页面不会有任何变化
(2)解决方法:对打包文件的名字做一些操作
1)hash
每次webpack打包的时候都会生成一个hash值,取打包生成的hash值拼接在输出文件名后就会更新资源,html就会引入这个新的资源,浏览器就会去服务器重新请求这个新的资源
output: { filename: 'js/built.[hash:10].js',//打包输出后会输出到js目录下,取hash的前十位 path: resolve(__dirname, 'build') },
出现的问题:css和js同时使用一个hash值,如果只改动一个会重新打包,导致所有的缓存失效
2)chunkhash
根据chunk生成hash,如果资源来自于同一个chunk就使用同一个hash
output: { filename: 'js/built.[chunkhash:10].js',//打包输出后会输出到js目录下,取chunkhash的前十位 path: resolve(__dirname, 'build') },
出现的问题:js和css还是属于同一个chunk,因为css是引入到js中的
3)contenthash
根据文件的内容生成hash,不同的文件hash值一定不同
output: { filename: 'js/built.[contenthash:10].js',//打包输出后会输出到js目录下,取contenthash的前十位 path: resolve(__dirname, 'build') },
2.tree shaking
(1)作用
去除在程序中没有使用的代码,让代码体积变得更小
(2)前提
必须使用ES6模块化
开启production环境
(3)注意的问题
不同的版本可能会有一些差异,将css文件以及@babel/polyfill当做未使用的代码忽略
测试:在package.json中设置“sideEffects”:false,表示所有代码都可以进行tree shaking,这时候重新打包构建输出的资源就没有css文件了
解决:
"sideEffects":["*.css","*.less"]//指明哪些文件不用tree shaking
3.code split
(1)多入口代码分割
有几个入口,最终输出就有几个bundle
entry: { index: './src/index.js', a: './src/js/a.js' }, output: { filename: 'js/[name].[contenthash:10].js',//打包输出后会输出到js目录下 path: resolve(__dirname, 'build') },代码结果:
(2)optimization
1)作用:
可以将node_modules中的文件单独打包为一个chunk输出
如果是多入口,会自动分析多入口文件,有多个文件中使用同一份公共的资源的话,会将公共的资源打包为单独的chunk
2)使用:
在webpack.confiig,js中配置optimization
optimization: { splitChunks: { chunks: 'all' } },
(3)通过js代码让某个文件打包为一个单独的chunk
在入口js文件中将某个文件使用import单独打包
import('./js/a.js').then(() => { console.log('文件加载成功') }).catch(() => { console.log('文件加载失败') })单独打包后的文件,默认使用id命名,如果想要对打包后的文件重命名的话,使用注释的方式在文件路径前面加一个webpackChunkName:
import(/* webpackChunkName:'test' */'./js/a.js').then(() => { console.log('文件加载成功') }).catch(() => { console.log('文件加载失败') })
4.懒加载/预加载
正常的加载:并行加载,同一时间加载多个文件,所有的文件会全部先加载完
(1)懒加载:lazy loading
场景:比如在点击按钮的时候才想去加载某个文件
使用:在js文件中使用import(类似code split的第三种方式)
import(/* webpackChunkName:'test' */'./js/a.js').then(() => { console.log('文件加载成功') }).catch(() => { console.log('文件加载失败') })
(2)预加载:prefetch
在使用之前加载js文件,等其他资源加载完毕,浏览器空闲的时候加载webpackPrefetch的资源
import(/* webpackChunkName:'test',webpackPrefetch:true */'./js/a.js').then(() => { console.log('文件加载成功') }).catch(() => { console.log('文件加载失败') })
5.PWA
渐进式网络开发应用程序:由service worker+catch完成的,使网页像APP应用程序一样,离线也可以访问,性能会更好
使用的插件:workbox-webpack-plugin
(1)下载和引入插件
npm i workbox-webpack-plugin
const WorkboxWebpackPlugin = require('workbox-webpack-plugin') new WorkboxWebpackPlugin({//最终会生成一个service-worker.js文件 clientsClaim:true,//帮助serviceWorker快速启动 skipWaiting:true//删除旧的serviceWorker })
(2)注册serviceWorker
一般在入口js文件中注册:
if('serviceWorker' in navigator){ window.addEventListener('load',function(){ navigator.serviceWorker.register('service-worker.js').then(()=>{ console.log('注册成功'); }).catch(()=>{ console.log('注册失败') }) }) }
(3)serviceWorker代码必须运行在服务器上,本地测试可以先安装serve,再通过serve指令启动
npm i serve -g//全局安装serve serve -s build//将build下的资源作为静态资源暴露出去,启动服务器
(4)测试:浏览器设置为下图的offline后,刷新网页,仍然存在刷新前的网页