前言
了解了webpack的基本配置之后,接下来应该关注其优化策略。webpack
的优化策略对我们的程序有着极大的性能提升。这是我记录学习webpack
优化的文章,纪录的同时也希望分享给大家。
DLL动态链接库
在开发的过程中,我们会引入像react
、react-dom
这样的库,这样的库基本上每一次打包时它们的内容都不会改变,所以我们引入动态链接库。将它们打包一次,在之后的构建过程中它们就不会被打包,打包的模块也会使用动态链接库里面的代码而不是去node_modules
中取,这样我们的构建速度就会大大提升。下面让我们来看看webpack
中怎么构建动态链接库。
打包
我们新建一个webpack.dll.config.js
的文件,配置如下
const path = require('path') const DllPlugin = require('webpack/lib/DllPlugin') module.exports = { entry:{ //将react、react-dom放到动态链接库中 react:['react','react-dom'] }, output:{ //输出的文件名称,[name]指的是当前动态链接库的名称,即react filename:'[name].dll.js', //输出到dist目录下 path:path.resolve(__dirname,'dist'), //存动态链接库的全局变量名称,即_dll_react,加上_dll_防止全局变量冲突 library:'_dll_[name]', }, plugins:[ new DllPlugin({ //动态链接库的全局变量名称,需要和library一致 name:'_dll_[name]', path:path.join(__dirname,'dist','[name].manifest.json') }) ] }
下面执行命令npx webpack --config webpack.dll.config.js
,dist目录下多了两个文件,分别是react.dll.js
和react.manifest.json
react.dll.js
里面包含React
的基础运行环境,即react
、react-dom
模块。react.manifest. json
用于描述在动态链接库文件中包含哪些模块。
然后再新建一个webpack.config.js
文件,在打包出来的chunk
块中声明需要引入的动态链接库,具体配置如下
const path = require('path') const DllReferencePlugin = require('webpack/lib/DllReferencePlugin') module.exports = { entry: { main: './main.js' }, output:{ filename:'bundle.js', path:path.resolve(__dirname,'dist') }, module:{ rules:[ ] }, plugins:[ new DllReferencePlugin({ manifest:require('./dist/react.manifest.json') }) ] }
执行命令npx webpack
,在dist
目录下生成bundle.js
。
引入
然后我们在dist
目录下新建一个index.html
文件,引入打包构建出来的文件和动态链接库。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script src="./react.dll.js"></script> <script src="./buncld.js"></script> </body> </html>
HappyPack
当文件数量变多时,webpack
构建速度慢的问题会变得特别明显。但是运行在node
之上的webpack
是单线程的,不能同时处理多个任务。而HappyPack
可以让webpack
做到这一点,它将任务分解成多个子进程去并发执行。由于JavaScript
是单线程模型,所以想要发挥多核CPU的作用只能通过多进程而不能通过多线程来实现。 接入HappyPack
的代码如下
const path = require('path') const HappyPack = require('happypack'); module.exports = { entry: { main: './main.js' }, output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') }, module: { rules: [{ test: /\.js$/, //以babel为id,转交给happypack处理 use: ['happypack/loader?id=babel'], }, { test: /\.css$/, //以css为id,转交给happypack处理 use: ['happypack/loader?id=css'] } ] }, plugins: [ new HappyPack({ id: 'babel', loaders: ['babel-loader'] }), new HappyPack({ id: 'css', loaders: ['style-loader','css-loader'] }) ] }
执行构建命令 npx webpack
从命令行的输出可以看出HappyPack
已经生效
Happy[babel]: Version: 5.0.1. Threads: 3 Happy[babel]: All set; signaling webpack to proceed. Happy[css]: Version: 5.0.1. Threads: 3 Happy[css]: All set; signaling webpack to proceed.
压缩代码
为了提升网页的加载速度,可以对资源进行压缩。
压缩JavaScript
这里我们会用到UglifyJS
,它通过去掉无效代码、去掉日志输出、缩短变量名,从而来优化我们的代码。 简单的配置如下
const UglifyJSPlugin = require('webpack/lib/optimize/UglifyJsPlugin') module.exports = { //以上省略。。。 plugins: [ //压缩输出的 JavaScript 代码 new UglifyJSPlugin({ compress: { //在 UglifyJS 删除没有用到的代码时不输出警告 warnings: false, //删除所有 console 语句, 可以兼容IE浏览器 drop_console: true, //内嵌己定义但是只用到一次 的变量 collapse_vars: true, //提取出现了多次但是没有定义成变量去 用的静态值 reduce_vars: true, output: { //最紧凑的输出 beautify: false, //删除所有注释 comments: false, } } }) ]
压缩CSS
CSS
也可以向JavaScript
一样被压缩,这里用到的工具是cssnano
。cssnano
的意义不仅仅是删除空格,它可以理解CSS
代码。例如 margin:10px 20px 10px 20px
会被压缩为 margin:10px 20px
基本配置如下
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const ExtractTextPlugin = require('extract-text-plugin') module.exports = { //以上省略。。。。 module: { rules: [{ test: /\.css$/, use: [ExtractTextPlugin.extract({ use: ['style-loader', 'css-loader?minimize'] })] }] }, plugins:[ new HtmlWebpackPlugin({ template:'./index.html', filename:'index.html' }), new ExtractTextPlugin({ filename: '[name]_[contenthash:8].css' }) ] }
提取公共代码
如果将多个页面的公共代码抽离成单独的文件,就能优化一些问题。例如相同的资源被重复加载,浪费用户的流量和服务器的成本。每个页面要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。 基本配置如下,此配置主要针对多页面。单页就不存在于公共代码这一说法了。
import 'react' import 'react-dom' import './index.css' //每个页面都用到的样式 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin') module.exports = { module: { entry: './main.js', rules: [{ test: /\.css$/, use: [ExtractTextPlugin.extract({ use: ['style-loader', 'css-loader?minimize'] })] }] }, plugins: [ new CommonsChunkPlugin({ //从已有的common和base两个现成的chunks中提取公共部分 chunks: ['common', 'base'], //将公共部分放到base中 这样配置之后 common会变小,因为公共部分都跑到了base里,而base不变 name: 'base' }) ] }
为了能使网页运行,以网页A为例,除了打包出来的A页面的JavaScrip
t代码还需引入公共部分代码
<script src= 'base.js'></script> <script src='common.js'></script> <script src='a.js'></script>
按需加载
单页应用首次渲染缓慢,一个很重要的原因是一次性加载了所有功能对应的代码。这个时候如果采用按需加载,我们网站的性能将会大大提升。 在webpac
k里,按需加载可以这样来写。例如我们只打包出了一个bundle.js
。在bundle.js
中,有这么一段代码
window.document.getElementById('button').addEventListener('click',()=>{ import('./show').then(show=>{ show('webpack') }) })
在show.js
中
module.exports= function (content) { window.aleat(`hello ${content}`) }
webpack
中内置了import
语句的支持,当遇到这样的语句时,首先会生成一个新的chunk
,然后触发import
的时候再去加载这个chunk
,返回的是一个Promise
对象,在加载成功时使用then
方法进行下面的操作,为了让webpack
正确打包chunk
,配置文件中需加入
output:{ //从entry打包生成的chunk filename:'[name].js', //动态加载生成的chunk chunkFileName:'[name].js' }
最后
webpack
的优化配置还有很多,这里只记录了我平时常用的,具体详情还请查阅官方文档。