一、 开发优化(development)
1. 文件监听
缺点: 需要手动刷新
启动方式:
- 方式1
启动 webpack 命令式 带上--watch 参数,启动监听
// package.json
"scripts": {
"watch":"webpack --watch"
}
npm run watch
npx webpack --watch
- 方法式2
在配置⽂件⾥设置 watch:true
// webpack.config.js
module.exports = {
watch: true, //默认false,不开启
//配合watch,只有开启才有作⽤
watchOptions: {
//默认为空,不监听的⽂件或者⽬录,⽀持正则
ignored: /node_modules/,
//监听到⽂件变化后,等300ms再去执⾏,默认300ms,
aggregateTimeout: 300,
//判断⽂件是否发⽣变化是通过不停的询问系统指定⽂件有没有变化,默认每秒问1次
poll: 1000 //ms
}
// 轮询 1s查看1次
}
2. webpack-dev-server 自动打开浏览器,保存自动刷新
2.1 安装
注意: webpack-dev-server 和 webpack-cli 4.x.x有兼容性问题,会报错, webpack-cli 需要安装 3.x.x,
npm install webpack-dev-server -D
启动
// package.json
"scripts": {
"server": "webpack-dev-server"
}
npm run server
2.2 配置
// webpack.config.js
var path = require('path');
module.exports = {
//...
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000
}
};
-
devServer: 打包的模块是放在内存中的,从而提升打包速度.
-
contentBase:告诉服务器内容的来源。仅在需要提供静态文件时才进行配置.
-
port: 端口
-
open: 是否打开浏览器
-
proxy: 解决开发环境的跨域问题
// webpack.config.js
devserver: {
proxy: {
'/api': { // 请求
target: 'http://localhost:9092'
}
}
}
// index.js
axios.get("/api/info").then(res => {
console.log(res);
});
- mock server
before 和 after 是中间件 提供的两个钩子, 一个在中间件启动之前,一个在中间件启动之后
before(app, server) {
app.get('/api/mock', (req, res) => {
res.json({
hello: 'express'
})
})
}
3. Hot Module Replacement (HMR:热模块替换)
3.1 热更新配置
热更新: 新代码生效, 网页不刷新, 状态不丢失
// 引入 webpack
const webpack = require('webpack');
module.exports = {
plugins: [
// 使用 webpack 插件
new webpack.HotModuleReplacementPlugin(),
],
devServer: {
hot: true,
//即便HMR不⽣效,浏览器也不要⾃动刷新,就开启hotOnly
hotOnly: true,
}
}
注意:CSS 热更新 不⽀持抽离出的 CSS, 不建议开发环境使用 mini-css-extract-plugin,还有不⽀持contenthash,chunkhash
3.2 JS 热模块替换
需要使⽤ module.hot.accept 来观察模块更新 从⽽更新
//number.js
function number() {
var div = document.createElement("div");
div.setAttribute("id", "number");
div.innerHTML = 13000;
document.body.appendChild(div);
}
export default number;
import number from "./number";
number();
// 是否开启热更新
if (module.hot) {
// 模块是否发生变化
module.hot.accept("./number", function () {
// 移除该元素
document.body.removeChild(document.getElementById("number"));
// 重新添加该元素
number();
});
}
3.3 React
React Hot Loader: 实时调整 react 组件。
3.4 Vue
Vue Loader: 此 loader 支持 vue 组件的 HMR,提供开箱即用体验
4. 缩小 Loader 处理范围
4.1 include: 只处理那
module.exports = {
module: {
rules: [{
test: /\.css$/,
// 只处理src 目录下的文件
include: path.resolve(__dirname, './src'),
}]
}
}
4.2 exclude: 不处理那
module.exports = {
module: {
rules: [{
test: /\.css$/,
// 不去处理那
exclude: path.resolve(__dirname, './node_modules')
}]
}
}
include 和 exclude 二选一,推荐使用 include
5. 优化 resolve 配置
5.1 resolve.modules
寻找第三⽅模块,默认是在当前项⽬⽬录下的node_modules⾥⾯去找,如果没有找到,就会去上⼀级⽬录 ../node_modules 找,再没有会去 ../../node_modules 中找,以此类推,和Node.js的模块寻找机制 很类似。
// webpack.config.js
module.exports={
resolve: {
// 指定路径只去这里查找,没有就是没有了
modules: [
path.resolve(__dirname, "./node_modules")
]
}
}
5.2 resolve.alias
默认情况下,webpack会从⼊⼝⽂件./node_modules/bin/react/index开始递归解析和处理依赖的⽂件。我们可以直接指定⽂件,避免这处的耗时。
// webpack.config.js
module.exports={
resolve: {
alias: {
"@": path.join(__dirname, "./pages"),
// 指定 react 文件路径,节约查找时间
react: path.resolve(__dirname, "./node_modules/react/umd/react.production.min.js"),
// 指定 react-dom 文件路径,节约查找时间
"react-dom": path.resolve(__dirname,"./node_modules/react-dom/umd/react-dom.production.min.js")
}
}
5.3 resolve.extensions
- 后缀尝试列表尽量的⼩
- 导⼊语句尽量的带上后缀。
// webpack.config.js
module.exports={
resolve: {
// 默认配置
extensions:['.js','.json','.jsx','.ts']
}
}
6. 使⽤⼯具量化
6.1 speed-measure-webpack-plugin:可以测量各个插件和 loader 所花费的时间
安装:
npm i speed-measure-webpack-plugin -D
//webpack.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const config = {
//...webpack配置
}
module.exports = smp.wrap(config)
6.2 webpack-bundle-analyzer:分析webpack打包后的模块依赖关系
安装:
npm install webpack-bundle-analyzer -D
启动webpack 构建,会默认打开⼀个窗⼝
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
//....
plugins: [
//...
new BundleAnalyzerPlugin(),
]
}
7. DllPlugin插件打包第三⽅类库 优化构建性能
Dll动态链接库 其实就是做缓存
webpack已经内置了对动态链接库的⽀持
- DllPlugin: ⽤于打包出⼀个个单独的动态链接库⽂件
- DllReferencePlugin:⽤于在主要的配置⽂件中引⼊DllPlugin插件打包好的动态链接库⽂件
新建webpack.dll.config.js⽂件,打包基础模块
我们在 index.js 中使⽤了第三⽅库 react 、 react-dom ,接下来,我们先对这两个库先进⾏打包。
7.1 打包出动态链接库
// webpack.dll.config.js
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"
})
]
};
运行
npx webpack --config webpack.dll.config.js
会生成⼀个dll⽂件夹,⾥边有dll.js⽂件,这样我们就把我们的React这些已经单独打包了
- dll⽂件包含了⼤量模块的代码,这些模块被存放在⼀个数组⾥。⽤数组的索引号为ID,通过变量讲⾃⼰暴露在全局中,就可以在 window.xxx访问到其中的模块
- Manifest.json 描述了与其对应的dll.js包含了哪些模块,以及ID和路径。
7.2 引入动态连接库
- 将⽹⻚依赖的基础模块抽离,打包到单独的动态链接库,⼀个动态链接库是可以包含多个模块的。
- 当需要导⼊的模块存在于某个动态链接库中时,不要再次打包,直接使⽤构建好的动态链接库即可。
module.exports = {
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 插件,它会将我们打包后的 dll.js ⽂件注⼊到我们⽣成的 index.html 中.在webpack.base.config.js ⽂件中进⾏更改
安装: npm i add-asset-html-webpack-plugin
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll/react.dll.js') // 对应的 dll ⽂件路径
}),
8. HardSourceWebpackPlugin, DllPlugin 的替换方案
DllPlugin 这个理解起来不费劲,操作起来很费劲。所幸,在Webpack5中已经不⽤它了,⽽是⽤ HardSourceWebpackPlugin ,⼀样的优化效果,但是使⽤却及其简单
- 提供中间缓存的作⽤
- ⾸次构建没有太⼤的变化
- 第⼆次构建时间就会有较⼤的节省
安装:
npm i hard-source-webpack-plugin -D
// webpack.config.js
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
module.exports = {
plugins: [
new HardSourceWebpackPlugin()
]
}
9. IgnorePlugin 从 bundle 中排除某些模块
webpack 中文网
var webpack = require('webpack');
module.exports = {
plugins: {
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
}
}
10. 使⽤ happypack 并发执⾏任务
happypack 启动也需要一定时间,按需使用。
安装:npm i -D happypack
const path = require('path');
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
mode: 'development',
module: {
rules: [{
test: /\.css$/,
include: path.resolve(__dirname, './src'),
use: [
{
// 一个 loader 对应一个 id
loader: 'happypack/loader?id=css'
}
]
}]
},
//在plugins中增加
plugins: [
new HappyPack({
// ⽤唯⼀的标识符id,来代表当前的HappyPack是⽤来处理⼀类特定的⽂件
id: "css",
// 处理的 loader
loaders: ['style-loader', 'css-loader'],
threadPool: happyThreadPool
})
]
};
11. ParallelUglifyPlugin
- Webpack 内置 Uglify 工具压缩 JS 是单线程的
- ParallelUglifyPlugin插件则会开启多个子进程,把对多个文件压缩的工作分别给多个子进程去完成,但是每个子进程还是通过UglifyJS去压缩代码
安装:
npm i -D webpack-parallel-uglify-plugin
配置:
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
module.exports = {
plugins: {
// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
new ParallelUglifyPlugin({
// 传递给 UglifyJS 的参数
// (还是使用 UglifyJS 压缩, 只不过帮助开启多进程)
uglifyJS: {
output: {
beautify: false, // 最紧凑的输出
comments: false, // 删除所有的注释
},
compress: {
// 删除所有的 `console.log`语句, 可以兼容 ie 浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量的静态值
reduce_vars: true
}
}
})
}
}
二、生产优化(production)
1. 使⽤ externals 优化 cdn 静态资源
<body>
<div id="root">root</div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</body>
我们可以将⼀些JS⽂件存储在 CDN 上(减少 Webpack 打包出来的 js 体积),在 index.html 中通过标签引⼊
我们希望在使⽤时,仍然可以通过 import 的⽅式去引⽤(如 import $ from 'jquery' ),并且希望 webpack 不会对其进⾏打包,此时就可以配置 externals 。
//webpack.config.js
module.exports = {
//...
externals: {
//jquery通过script引⼊之后,全局中即有了 jQuery 变量
'jquery': 'jQuery'
}
}
2. 使⽤静态资源路径 publicPath (CDN)
// webpack.config.js
output:{
publicPath: '//cdnURL.com', //指定存放JS⽂件的CDN地址
}
确保静态资源⽂件的上传与否
3. css⽂件的处理
- 使⽤postcss为样式⾃动补⻬浏览器前缀 caniuse
- 借助 MiniCssExtractPlugin 抽离 css
- 压缩 css
3.1 压缩 css
借助 ptimize-css-assets-webpack-plugin 和 cssnano
安装:
npm i optimize-css-assets-webpack-plugin -D
npm i cssnano -D
配置:
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
plugins: [
new OptimizeCSSAssetsPlugin({
cssProcessor: require("cssnano"), //引⼊cssnano配置压缩选项
cssProcessorOptions: {
discardComments: { removeAll: true }
}
})
]
}
4. 压缩 HTML
借助 htmlWebpackPlugin 插件
安装:
npm i html-webpack-plugin -D
配置:
new htmlWebpackPlugin({
title: "京东商城",
template: "./index.html",
filename: "index.html",
minify: {
// 压缩HTML⽂件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空⽩符与换⾏符
minifyCSS: true // 压缩内联css
}
})
5. tree Shaking 清除⽆⽤ css, js (Dead Code)
Dead Code 特征:
- 代码不会被执⾏,不可到达
- 代码执⾏的结果不会被⽤到
- 代码只会影响死变量(只写不读)
5.1 Css tree shaking
借助 purgecss-webpack-plugin 插件清除无用的 css
借助 glob-all 处理路径
安装:
npm i purgecss-webpack-plugin -D
npm i glob-all -D
配置:
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const globAll = require("glob-all");
module.exports = {
plugins: [
// 清除无用 css
new PurgeCSSPlugin({
paths: globAll.sync([
// 要做 CSS Tree Shaking 的路径文件
path.resolve(__dirname, './src/*.html'),
// 请注意,我们同样需要对 html 文件进行tree shaking
path.resolve(__dirname, './src/*.js')
])
})
]
}
5.2 JavaScript tree shaking
生产模式自动开启,只⽀持 import ⽅式引⼊,不⽀持 commonjs 的⽅式引⼊
ES6 和 commonjs 的区别
commonjs 动态引入, 执行时引入,模块输出的是值的浅拷贝,模块输出后被改变,其他引用模块不会改变
ES6 静态引入, 编译时引入,模块输出的是值的引用,模块输出后被改变, 引用模块会改变
module.exports = {
mode: 'production'
}
- 自动开启代码压缩
- Vue React 等会删除掉调式代码(如开发环境的 warning)
- 自动启动 Tree-Shaking
6. 代码分割 code Splitting
单⻚⾯应⽤spa: 打包完后,所有⻚⾯只⽣成了⼀个bundle.js,代码体积变⼤,不利于下载,没有合理利⽤浏览器资源
多⻚⾯应⽤mpa: 如果多个⻚⾯引⼊了⼀些公共模块,那么可以把这些公共的模块抽离出来,单独打包。公共代码只需要下载⼀次就缓存起来了,避免了重复下载。
- 代码体积更小
- 创建函数作用域更少
code Splitting 概念与 webpack 并没有直接的关系,只不过 webpack 中提供了⼀种更加⽅便的⽅法供我们实现代码分割基于split-chunks-plugin
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
}
}
}
}
optimization: {
//帮我们⾃动做代码分割
splitChunks:{
chunks:"all",//默认是⽀持异步,我们使⽤all
}
}
7. 作⽤域提升(Scope Hoisting)
示例:
// hello.js
export default "hello webpack";
import str from "./hello.js";
console.log(str);
通过配置 optimization.concatenateModules=true 开启 Scope Hoisting
// webpack.config.js
module.exports = {
optimization: {
concatenateModules: true
}
}
三、development vs Production 模式区分打包
借助 npm install webpack-merge -D 合并公共配置
安装:
npm install webpack-merge -D
// webpack.dev.js
const merge = require("webpack-merge");
// 引入 Webpack 公共配置
const commonConfig = require("./webpack.common.js");
const devConfig = {
...
}
// 合并配置并导出
module.exports = merge(commonConfig,devConfig);
// package.json
"scripts":{
"dev":"webpack-dev-server --config webpack.dev.js",
"build":"webpack --config webpack.prod.js"
}
四、总结
1. 可用于生产环境
- 优化 loader 处理范围
- 优化 resolve 配置
- IgnorePlugin
- happyPack
- tree Shaking
- ParallelUglifyPlugin
- 代码分割 code Splitting
- 作⽤域提升(Scope Hoisting)
- 使用 CDN 优化
2. 不可用于生产环境
- Hot Module Replacement (HMR:热模块替换)
- 使⽤⼯具量化
- DllPlugin、HardSourceWebpackPlugin
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!