文章首发于:github.com/USTB-musion…
写在前面
webpack可以说是当下最流行的打包库,当webpack处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每一个模块,然后将所有这些模块打包成一个或多个bundle。这篇文章将介绍webpack非常重要的一部分——性能优化。文章的代码的在此: github.com/USTB-musion…
本文将从以下几部分进行总结:
- noParse
- ignorePlugin
- dllPlugin
- happypack
- Tree-Shaking
- 抽离公共代码
- 懒加载
- 热更新
noParse
- noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析和处理,这样做的好处是能提高构建性能。 原因是一些库例如 jQuery 、ChartJS 它们庞大又没有采用模块化标准,让 Webpack 去解析这些文件耗时又没有意义。
启用noParse:
module: {
// 不去解析jquery的依赖关系
noParse: /jquery/
},
ignorePlugin
- moment 2.18 会将所有本地化内容和核心功能一起打包)。可以使用 IgnorePlugin 在打包时忽略本地化内容,经过实验,使用 ignorePlugin 之后 📦 之后的体积由 1.2M 降低至 800K
ignorePlugin启用方法:
// 用法:
new webpack.IgnorePlugin(requestRegExp, [contextRegExp]);
//eg.
plugins: [new webpack.IgnorePlugin(/\.\/local/, /moment/)];
DllPlugin
- DllPlugin 是基于 Windows 动态链接库(dll)的思想被创作出来的。这个插件会把第三方库单独打包到一个文件中,这个文件就是一个单纯的依赖库。这个依赖库不会跟着你的业务代码一起被重新打包,只有当依赖自身发生版本变化时才会重新打包。
用 DllPlugin 处理文件,要分两步走:
- 基于 dll 专属的配置文件,打包 dll 库
let path = require("path");
let webpack = require("webpack");
module.exports = {
mode: "development",
entry: {
react: ["react", "react-dom"]
},
output: {
filename: "_dll_[name].js", // 产生的文件名
path: path.resolve(__dirname, "dist"),
library: "_dll_[name]"
},
plugins: [
// name要等于library里的name
new webpack.DllPlugin({
name: "_dll_[name]",
path: path.resolve(__dirname, "dist", "manifest.json")
})
]
};
- 基于 webpack.config.js 文件,打包业务代码
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");
module.exports = {
mode: "development",
// 多入口
entry: {
home: "./src/index.js"
},
devServer: {
port: 3000,
open: true,
contentBase: "./dist"
},
module: {
// 不去解析jquery的依赖关系
noParse: /jquery/,
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.js$/,
exclude: /node_modules/,
include: path.resolve("src"),
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"]
}
}
}
]
},
output: {
// name -> home a
filename: "[name].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, "dist", "manifest.json")
}),
new webpack.IgnorePlugin(/\.\/local/, /moment/),
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
new webpack.DefinePlugin({
DEV: JSON.stringify("production")
})
]
};
Happypack——将 loader 由单进程转为多进程
- 大家知道,webpack 是单线程的,就算此刻存在多个任务,你也只能排队一个接一个地等待处理。这是 webpack 的缺点,好在我们的 CPU 是多核的,Happypack 会充分释放 CPU 在多核并发方面的优势,帮我们把任务分解给多个子进程去并发执行,大大提升打包效率。
happypack的使用方法:
将loader中的配置转移到happypack中就好:
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");
// 模块 happypack 可以实现多线程📦
let Happypack = require("happypack");
module.exports = {
mode: "development",
// 多入口
entry: {
home: "./src/index.js"
},
devServer: {
port: 3000,
open: true,
contentBase: "./dist"
},
module: {
// 不去解析jquery的依赖关系
noParse: /jquery/,
rules: [
{
test: /\.css$/,
use: "Happypack/loader?id=css"
},
{
test: /\.js$/,
exclude: /node_modules/,
include: path.resolve("src"),
use: "Happypack/loader?id=js"
}
]
},
output: {
// name -> home a
filename: "[name].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new Happypack({
id: "css",
use: ["style-loader", "css-loader"]
}),
new Happypack({
id: "js",
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"]
}
}
]
}),
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, "dist", "manifest.json")
}),
new webpack.IgnorePlugin(/\.\/local/, /moment/),
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
new webpack.DefinePlugin({
DEV: JSON.stringify("production")
})
]
};
Tree-Shaking
- 基于 import/export 语法,Tree-Shaking 可以在编译的过程中获悉哪些模块并没有真正被使用,这些没用的代码,在最后打包的时候会被去除。适合于处理模块级别的代码,所以尽量使用es6的import/export语法。
抽离公共代码
把公共代码抽离出来的好处:
- 减少网络传输流量,降低服务器成本;
- 虽然用户第一次打开网站的速度得不到优化,但之后访问其它页面的速度将大大提升。
启用抽离代码:
// webpack 4.x版本之前的commonChunkPlugins
optimization: {
// 分割代码块
splitChunks: {
// 缓存组
cacheGroups: {
// 公共模块
common: {
chunks: "initial",
minSize: 0,
// 最小公用模块次数
minChunks: 2
},
vendor: {
priority: 1,
// 抽离出来
test: /node_modules/,
chunks: "initial",
minSize: 0,
minChunks: 2
}
}
}
}
按需加载
按需加载的思想
- 一次不加载完所有的文件内容,只加载此刻需要用到的那部分(会提前做拆分)
- 当需要更多内容时,再对用到的内容进行即时加载
通过 es6 的 import 实现按需加载,在使用 import() 分割代码后,你的浏览器并且要支持 Promise API 才能让代码正常运行, 因为 import() 返回一个 Promise,它依赖 Promise。对于不原生支持 Promise 的浏览器,你可以注入 Promise polyfill。
let button = document.createElement("button");
button.innerHTML = "musion";
// vue,react的懒加载原理也是如此
button.addEventListener("click", function() {
// es6草案中的语法, jsonp实现动态加载文件
import("./source.js").then(data => {
console.log(data.default);
});
console.log("click");
});
document.body.appendChild(button);
热更新
模块热替换(HMR – Hot Module Replacement)是 webpack 提供的最有用的功能之一。它允许在运行时替换,添加,删除各种模块,而无需进行完全刷新重新加载整个页面,其思路主要有以下几个方面:
- 保留在完全重新加载页面时丢失的应用程序的状态
- 只更新改变的内容,以节省开发时间
- 调整样式更加快速,几乎等同于就在浏览器调试器中更改样式
启用HRM
- 引入了webpack库
- 使用了new webpack.HotModuleReplacementPlugin()
- 设置devServer选项中的hot字段为true
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");
module.exports = {
mode: "production",
// 多入口
entry: {
index: "./src/index.js",
other: "./src/other.js"
},
devServer: {
// 启用热更新
hot: true,
port: 3000,
open: true,
contentBase: "./dist"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
include: path.resolve("src"),
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
plugins: ["@babel/plugin-syntax-dynamic-import"]
}
}
}
]
},
output: {
// name -> home a
filename: "[name].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new webpack.NamedModulesPlugin(), // 打印更新的模块路径
new webpack.HotModuleReplacementPlugin() // 热更新插件
]
};
参考文章
你可以关注我的公众号「慕晨同学」,鹅厂码农,平常记录一些鸡毛蒜皮的点滴,技术,生活,感悟,一起成长。
今天的文章webpack4.x 性能优化分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/13584.html