webpack 的 打包优化

  |  

webpack性能优化

主要从下面两个方面入手

  • 优化开发体验
  • 优化输出质量

优化开发体验

  • 提升效率
  • 优化构建速度
  • 优化使用体验

优化输出质量

  • 优化要发布到线上的代码, 减少用户能感知到的加载时间
  • 提升代码性能,性能好,执行就快

缩⼩⽂件范围 Loader

优化loader配置

  • test include exclude三个配置项来缩⼩loader的处理范围
  • 推荐include
{
test: /\.css$/,
include: path.resolve(__dirname, "./src"),
use: ["style-loader", "css-loader"],
},

优化resolve.modules配置

resolve.modules⽤于配置webpack去哪些⽬录下寻找第三⽅模块,默认是[‘node_modules’]

寻找第三⽅模块,默认是在当前项⽬⽬录下的node_modules⾥⾯去找,

如果没有找到,就会去上⼀级⽬录../node_modules找,再没有会去../../node_modules中找,

以此类推,和Node.js的模块寻找机制很类似。

如果我们的第三⽅模块都安装在了项⽬根⽬录下,就可以直接指明这个路径。

module.exports={
resolve:{
modules: [path.resolve(__dirname, "./node_modules")]
}
}

优化resolve.alias配置

resolve.alias配置通过别名来将原导⼊路径映射成⼀个新的导⼊路径

拿react为例,我们引⼊的react库,⼀般存在两套代码

  • cjs 采⽤commonJS规范的模块化代码

  • umd 已经打包好的完整代码,没有采⽤模块化,可以直接执⾏

默认情况下,webpack会从⼊⼝⽂件./node_modules/bin/react/index开始递归解析和处理依赖
的⽂件。我们可以直接指定⽂件,避免这处的耗时。

alias: {
//减少查找过程
//起别名
"@": path.resolve(__dirname, "./src"),
react: "./node_modules/react/umd/react.production.min.js",
"react-dom": "./node_modules/react-dom/umd/react-dom.production.min.js",
},

优化resolve.extensions配置

resolve.extensions在导⼊语句没带⽂件后缀时,webpack会⾃动带上后缀后,去尝试查找⽂件是否存
在。

默认值:

extensions:['.js','.json','.jsx','.ts']
  • 后缀尝试列表尽量的⼩
  • 导⼊语句尽量的带上后缀。

使⽤externals优化cdn静态资源

前提是公司有cdn, 不建议使用免费cdn

/公司有cdn
//静态资源有部署到cdn 有链接了
// 我想使⽤cdn!!!!!!!!
我的bundle⽂件⾥,就不⽤打包进去这个依赖了,体积会⼩

我们可以将⼀些JS⽂件存储在 CDN 上(减少 Webpack 打包出来的 js 体积),在 index.html 中通过
标签引⼊。

同时,我们希望在使⽤时,仍然可以通过 import 的⽅式去引⽤(如 import $ from ‘jquery’ ),并且希望
webpack 不会对其进⾏打包,此时就可以配置 externals 。

//webpack.config.js
module.exports = {
//...
externals: {
//jquery通过script引⼊之后,全局中即有了 jQuery 变量
'jquery': 'jQuery'
}
}

使⽤静态资源路径publicPath(CDN)

CDN通过将资源部署到世界各地,使得⽤户可以就近访问资源,加快访问速度。要接⼊CDN,需要把⽹
⻚的静态资源上传到CDN服务上,在访问这些资源时,使⽤CDN服务提供的URL。

// webpack.config.js
output:{
publicPath: '//cdnURL.com', //指定存放JS⽂件的CDN地址
}
  • 咱们公司得有cdn服务器地址
  • 确保静态资源⽂件的上传与否

借助MiniCssExtractPlugin 完成抽离css

如果不做抽取配置,我们的 css 是直接打包进 js ⾥⾯的,我们希望能单独⽣成 css ⽂件。 因为单独⽣
成css,css可以和js并⾏下载,提⾼⻚⾯加载效率

// npm install mini-css-extract-plugin -D
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
{
test: /\.scss$/,
use: [
// "style-loader", // 不再需要style-loader,⽤MiniCssExtractPlugin.loader代替
MiniCssExtractPlugin.loader,
"css-loader", // 编译css
"postcss-loader",
"sass-loader" // 编译scss
]
}
plugins: [
new MiniCssExtractPlugin({
filename: "css/[name]_[contenthash:6].css",
chunkFilename: "[id].css"
})
]

压缩css

  • 借助 optimize-css-assets-webpack-plugin
  • 借助cssnano
// npm install cssnano -D
// npm i optimize-css-assets-webpack-plugin -D

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

new OptimizeCSSAssetsPlugin({
cssProcessor: require("cssnano"), //引⼊cssnano配置压缩选项
cssProcessorOptions: {
discardComments: { removeAll: true }
}
})

压缩HTML

  • 借助html-webpack-plugin
new htmlWebpackPlugin({
title: "京东商城",
template: "./index.html",
filename: "index.html",
minify: {
// 压缩HTML⽂件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空⽩符与换⾏符
minifyCSS: true, // 压缩内联css
},
});

区分环境打包

webpack-merge, 用于合并配置

const merge = require("webpack-merge")
const baseConfig = require("./webpack.base.js")
const devConfig = {
...
}
module.exports = merge(commonConfig,devConfig)

修改package.json

//package.js
"scripts":{
"dev":"webpack-dev-server --config ./build/webpack.dev.js",
"build":"webpack --config ./build/webpack.prod.js"
}

借助cross-env 区分环境

npm i cross-env -D

修改 package.json文件

"dev": "cross-env NODE_ENV=development webpack --config ./webpack.config.dev.js",
"test": "cross-env NODE_ENV=test webpack --config ./webpack.config.test.js",

webpack.config.js中可以拿到变量

process.env.NODE_ENV

如果module.exports是一个函数的话,可以通过参数接收

//外部传⼊的全局变量
module.exports = (env)=>{
if(env && env.production){
return merge(commonConfig,prodConfig)
}else{
return merge(commonConfig,devConfig)
}
}

tree Shaking

webpack2.x开始⽀持 tree shaking概念,顾名思义,”摇树”,清除⽆⽤ css,js(Dead Code)

Dead Code ⼀般具有以下⼏个特征

  • 代码不会被执⾏,不可到达
  • 代码执⾏的结果不会被⽤到
  • 代码只会影响死变量(只写不读)
  • Js tree shaking只⽀持ES module的引⼊⽅式!!!!,

Css tree shaking

// npm i glob-all purify-css purifycss-webpack --save-dev

const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')

修改webpack.config.js

plugins: [
// 清除⽆⽤ css
new PurifyCSS({
paths: glob.sync([
// 要做 CSS Tree Shaking 的路径⽂件
path.resolve(__dirname, "./src/*.html"), // 请注意,我们同样需要对 html ⽂件进⾏ tree shaking
path.resolve(__dirname, "./src/*.js"),
]),
}),
];

js tree shaking

只⽀持import⽅式引⼊,不⽀持commonjs的⽅式引⼊

//webpack.config.js
optimization: {
usedExports: true // 哪些导出的模块被使⽤了,再做打包
}

只要mode是production就会⽣效,develpoment的tree shaking是不⽣效的,
因为webpack为了⽅便你的调试

⽣产模式不需要配置,默认开启

代码分割 code Splitting

为什么要代码分割 ?

单页面应用(spa)

打包完后,所有⻚⾯只⽣成了⼀个bundle.js

  • 代码体积变⼤,不利于下载
  • 没有合理利⽤浏览器资源

多页面应用(mpa)

如果多个⻚⾯引⼊了⼀些公共模块,那么可以把这些公共的模块抽离出来,单独打包。公共代码只需要
下载⼀次就缓存起来了,避免了重复下载。

import _ from "lodash";
console.log(_.join(['a','b','c','****']))
/**
假如我们引⼊⼀个第三⽅的⼯具库,体积为1mb,⽽我们的业务逻辑代码也有1mb,那么打包出来的体积⼤
⼩会在2mb
导致问题:
体积⼤,加载时间⻓
业务逻辑会变化,第三⽅⼯具库不会,所以业务逻辑⼀变更,第三⽅⼯具库也要跟着变。
*/

其实code Splitting概念 与 webpack并没有直接的关系,只不过webpack中提供了⼀种更加⽅便的⽅法
供我们实现代码分割

基于https://webpack.js.org/plugins/split-chunks-plugin/

optimization: {
splitChunks: {
chunks: "all", // 所有的 chunks 代码公共的部分分离出来成为⼀个单独的⽂件
},
},

所有的配置项,如下

optimization: {
splitChunks: {
chunks: "async", //对同步 initial,异步 async,所有的模块有效 all
minSize: 30000, //最⼩尺⼨,当模块⼤于30kb
maxSize: 0, //对模块进⾏⼆次分割时使⽤,不推荐使⽤
minChunks: 1, //打包⽣成的chunk⽂件最少有⼏个chunk引⽤了这个模块
maxAsyncRequests: 5, //最⼤异步请求数,默认5
maxInitialRequests: 3, //最⼤初始化请求书,⼊⼝⽂件同步请求,默认3
automaticNameDelimiter: "-", //打包分割符号
name: true, //打包后的名称,除了布尔值,还可以接收⼀个函数function
cacheGroups: {
//缓存组
vendors: {
test: /[\\/]node_modules[\\/]/,
name: "vendor", // 要缓存的 分隔出来的 chunk 名称
priority: -10, //缓存组优先级 数字越⼤,优先级越⾼
},
other: {
chunks: "initial", // 必须三选⼀: "initial" | "all" | "async"(默认就是async)
test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk,
name: "other",
minSize: 30000,
minChunks: 1,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true, //可设置是否重⽤该chunk
},
},
},
}

DllPlugin插件打包第三⽅类库 优化构建性能

Dll动态链接库 其实就是做缓存

.dll⽂件称为动态链接库,在windows系统会经常看到.

项⽬中引⼊了很多第三⽅库,这些库在很⻓的⼀段时间内,基本不会更新,打包的时候分开打包来提升
打包速度,⽽DllPlugin动态链接库插件,其原理就是把⽹⻚依赖的基础模块抽离出来打包到dll⽂件中,
当需要导⼊的模块存在于某个dll中时,这个模块不再被打包,⽽是去dll中获取。

  • 动态链接库只需要被编译⼀次,项⽬中⽤到的第三⽅模块,很稳定,例如react,react-dom,只要
    没有升级的需求

webpack已经内置了对动态链接库的⽀持

  • DllPlugin:⽤于打包出⼀个个单独的动态链接库⽂件
  • DllReferencePlugin:⽤于在主要的配置⽂件中引⼊DllPlugin插件打包好的动态链接库⽂件

新建webpack.dll.config.js⽂件,打包基础模块
我们在 index.js 中使⽤了第三⽅库 react 、 react-dom ,接下来,我们先对这两个库先进⾏打包。

const path = require("path");
const { DllPlugin } = require("webpack");
module.exports = {
mode: "development",
entry: {
react: ["react", "react-dom"] //! node_modules?
},
output: {
path: path.resolve(__dirname, "./dll"),
filename: "[name].dll.js",
library: "react"
},
plugins: [
new DllPlugin({
// manifest.json⽂件的输出位置
path: path.join(__dirname, "./dll", "[name]-manifest.json"),
// 定义打包的公共vendor⽂件对外暴露的函数名
name: "react"
})
]
};

在package.json中添加

"dev:dll": "webpack --config ./build/webpack.dll.config.js",

然后,运行

npm run dev:dll

你会发现多了⼀个dll⽂件夹,⾥边有dll.js⽂件,这样我们就把我们的React这些已经单独打包了

  • dll⽂件包含了⼤量模块的代码,这些模块被存放在⼀个数组⾥。⽤数组的索引号为ID,通过变量讲
    ⾃⼰暴露在全局中,就可以在window.xxx访问到其中的模块

  • Manifest.json 描述了与其对应的dll.js包含了哪些模块,以及ID和路径。

接下来怎么使⽤呢?

要给web项⽬构建介⼊动态链接库,需要完成以下事情:

  • 将⽹⻚依赖的基础模块抽离,打包到单独的动态链接库,⼀个动态链接库是可以包含多个模块的。

  • 当需要导⼊的模块存在于某个动态链接库中时,不要再次打包,直接使⽤构建好的动态链接库即
    可。

    ## webpack.dev.config.js 添加 下面plugin
    new DllReferencePlugin({
    manifest: path.resolve(__dirname,"./dll/react-manifest.json")
    }),
  • ⻚⾯依赖的所有动态链接库都需要被加载。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initialscale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>webpack</title>
<link href="css/main_e2bf39.css" rel="stylesheet"></head>
<body>
<div id="app"></div>
<script type="text/javascript" src="react.dll.js"></script>
<script type="text/javascript" src="js/main_142e6c.js"></script>
</body>
</html>

⼿动添加使⽤,体验不好,这⾥推荐使⽤add-asset-html-webpack-plugin插件帮助我们做这个事情。

安装⼀个依赖 npm i add-asset-html-webpack-plugin ,它会将我们打包后的 dll.js ⽂件注⼊到我

们⽣成的 index.html 中.在 webpack.base.config.js ⽂件中进⾏更改。

new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll/react.dll.js') // 对应的 dll ⽂件路径
}),

npm run dev

这个理解起来不费劲,操作起来很费劲。所幸,在Webpack5中已经不⽤它了,⽽是

⽤ HardSourceWebpackPlugin ,⼀样的优化效果,但是使⽤却及其简单

使⽤happypack并发执⾏任务

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. webpack性能优化
    1. 1.1. 优化开发体验
    2. 1.2. 优化输出质量
    3. 1.3. 缩⼩⽂件范围 Loader
    4. 1.4. 优化resolve.modules配置
    5. 1.5. 优化resolve.alias配置
    6. 1.6. 优化resolve.extensions配置
    7. 1.7. 使⽤externals优化cdn静态资源
    8. 1.8. 使⽤静态资源路径publicPath(CDN)
    9. 1.9. 借助MiniCssExtractPlugin 完成抽离css
    10. 1.10. 压缩css
    11. 1.11. 压缩HTML
    12. 1.12. 区分环境打包
      1. 1.12.1. webpack-merge, 用于合并配置
      2. 1.12.2. 借助cross-env 区分环境
    13. 1.13. tree Shaking
      1. 1.13.1. Css tree shaking
      2. 1.13.2. js tree shaking
    14. 1.14. 代码分割 code Splitting
      1. 1.14.1. 单页面应用(spa)
      2. 1.14.2. 多页面应用(mpa)
    15. 1.15. DllPlugin插件打包第三⽅类库 优化构建性能
    16. 1.16. 使⽤happypack并发执⾏任务
,