webpack核心模块
- mode 环境配置,production、development、none三个值,默认production
- entry 打包入口文件
- output 打包出口文件
- loader 文件解析
- plugins 插件配置
module.exports = {
mode: 'production' // 环境配置,production、development、none三个值,默认production
entry: './src/index.js', // 打包入口文件
output: { // 打包出口文件
filename: '[name]_[hash].js',
path: path.join(__dirname, './dist')
},
module: { // 文件loader配置
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
},
plugins: [] // 插件配置
}
复制代码
loader example
js | jsx
npm install babel-loader @babel/core @babel/preset-env @babel/preset-react –save-dev
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
options: {
loader: "babel-loader",
presets: [
"@babel/preset-env", // es6+ => es5
"@babel/preset-react", // jsx
],
},
},
],
},
};
复制代码
css | less
npm install style-loader css-loader less-loadr less –save-dev
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: ["style-loader", "css-loader"], // loader 从右往左执行
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"], // loader 从右往左执行
},
],
},
};
复制代码
watch
原理:轮询判断文件最后编辑时间是否变化,某个文件发生了变化,并不会立即告诉监听者,而生先缓存起来,等 aggregateTimeout 设置时间到了再执行
两种设置方式:
1、npm scripts 带 –watch 参数
"watch" : "webpack --watch"
复制代码
2、webpack 配置
module.exports = {
watch: true,
// watch设置true才有效
watchOptions: {
// 默认为空,不监听文件
ignored: /node_modules/,
// 监听到变化后会等300ms再执行
aggregateTimeout: 300,
// 轮询监听文件,默认每秒轮询次数1000
poll: 1000,
},
};
复制代码
热更新 HMR
热更新:使用 webpack-dev-server, WDS 不刷新浏览器,不输出文件,而是放在内存中;使用 webpack.HotModuleRepleacementPlugin 插件刷新浏览器
- webpack 配置
const webpack = require("webpack");
module.exports = {
plugins: [new webpack.HotModuleRepleacementPlugin()],
};
复制代码
- npm scripts 配置
webpack 4+
// package.json
{
"scripts": {
"dev": "webpack-dev-server --open --mode=development"
}
}
复制代码
webpack 5+
// package.json
{
"scripts": {
"dev": "webpack server --open --mode=development"
}
}
复制代码
热更新原理
热更新有两个阶段:
- 启动阶段
- 文件改变更新阶段
- Webpack Compile :将 js 编译成对应Bundle文件
- HMR Server: 将热更新的文件输出给 HMR Runtime
- Bundle Server: 提供文件在浏览器访问的服务
- HMR Runtime: 会被注入到浏览器,更新文件的变化
文件指纹
hash、 chunkhash、 contenthash
-
hash:和整个项目构建相关,项目相关文件发生变化就会改变
-
chunkhash: 和 webpack 入口 entr 入口文件有关,不同的 entry 会生成不同的 chunkhash 值
-
contenthash: 根据文件内容有关,只有文件改变才会改变
js 文件指纹设置
const path = require("path");
module.exports = {
output: {
filename: "[name]_[chunkhash:8].js",
path: path.join(__dirname, "./dist"),
},
};
复制代码
css 文件指纹设置
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: `[name]_[contenthash:8].css`,
}),
],
};
复制代码
图片、文件指纹设置
module.exports = {
module: {
rules: [
{
test: /\.(png|jpeg|jpg|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name]_[hash:8].[ext]",
},
},
},
],
},
};
复制代码
设置 file-loade 的 name,使用 [hash]
文件相关占位符含义
占位符名称 | 含义 |
---|---|
[ext] | 文件后缀名 |
[name] | 文件名 |
[path] | 文件相对路径 |
[folder] | 文件所在的文件夹 |
[contenthash] | 文件内容 hash, 默认 md5 生成 |
[hash] | 文件内容 hash, 默认 md5 生成 |
html、css、javascript 代码压缩
html
npm install html-webpack-plugin –save-dev
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
filename: "index.html",
}),
],
};
复制代码
css
webpack 4.+版本
npm install optimize-css-assets-webpack-plugin cssnano –save-dev
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const cssnano = require('cssnano');
module.exports = {
plugins: [
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/,
cssProcessor: cssnano,
}),
];
}
复制代码
webpack 5.+版本
npm install css-minimizer-webpack-plugin –save-dev
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
parallel: true, // 启用/禁用多进程并发执行
}),
],
},
};
复制代码
javascript
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true, // 启用/禁用多进程并发执行
}),
],
},
};
复制代码
mode: production 是开启 js 压缩
清楚构建目录
npm install clean-webpack-plugin –save-dev
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [new CleanWebpackPlugin()],
};
复制代码
webpack 5+ 使用 output.clean 设置
module.exports = {
output: {
clean: true,
},
};
复制代码
css3 自动添加厂商前缀
npm install postcss-loader autoprefixer -save-dev
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [["autoprefixer"]],
},
},
},
],
},
],
},
};
复制代码
抽取公共资源
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: "vendors",
test: /(react|teact-dom)/,
chunks: "all",
},
},
},
},
};
复制代码
Tree Shaking 原理和分析
分析
代码中以下情况的代码会被 Tree Shaking
- 代码不会被执行,不可到达
- 代码执行的结果不会被使用
- 代码只会影响死变量(只读不写)
原理
利用 es6 模块特点:
- 只能作为顶层语句的出现
- import 的模块名只能是字符串常量
- import binding 是 immutable 的
代码擦除:ugify 阶段删除无用代码
Scope Hoisting 原理和分析
分析
webpack 构建后对每个模块会包裹,增加构建后代码体积;构建后的代码存在大量闭包,运行时创建函数作用域,内存开销变得
原理
构建时将模块引用按照引用顺序放在一个函数作用域里,然后适当的重命名防止变量名冲突
通过 Scope Hoisting 可以减少函数声明代码和内存开发
webpack 4+版本默认已经集成,设置 mode: production, webpack 3 版本设置: new webpack.optimize.ModuleConcatenationPlugin()
const webpack = require("webpack");
module.exports = {
plugins: [new webpack.optimize.ModuleConcatenationPlugin()],
};
复制代码
代码分割和动态 import
npm install @babel/plugin-syntax-dynamic-import –save-dev
// .babbelrc
{
"presets": [],
"plugins": [
"@babel/plugin-syntax-dynamic-import"
];
}
复制代码
构建速度和体积优化策略
构建速度
分析 loader 和 plugin 耗时
npm install speed-measure-webpack-plugin –save-dev
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap({
// webpackConfig
});
复制代码
优化策略
- 多进程、多实例构建
推荐方案
npm install thread-loader –save-dev
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: "thread-loader",
options: [
workders: true
],
},
'babel-loader'
],
},
],
},
};
复制代码
可选方案
npm install happypack –save-dev
const Happypack = require("happypack");
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ["happypack/loader"],
},
],
},
plugins: [
new Happypack({
loaders: ["babel-loader"],
}),
],
};
复制代码
- 预编译
使用 Dllplugin 分离基础包
const webpack = require('path');
const path = require('path');
module.exports = {
entry: {
library: [
'react',
'react-dom' //分离的基础包
]
},
output: {
filename: '[name]_[chunkhash:8].dll.js',
path: path.join(__dirname, './library'),
library: '[name]'
},
plugins: [
new new webpackDllplugin({
name: '[name]',
path: path.join(__dirname, 'manifest.json')
})
]
}
复制代码
DllReferencePlugin 引入 manifest.json
module.exports = {
plugins: [
new webpack.DllReferencePlugin({
manifest: require(/* 预编译基础包路径 */),
}),
],
};
复制代码
缓存
- babel缓存 babel-loader?cacheDirectory=true
- 代码压缩缓存 zerser-webpack-plguin
- 模块缓存 hard-source-webpack-plugin
babel-loader
module.exports = {
module: {
rules: [
{
test: /\.js$/,
user: [{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}]
}
]
}
}
复制代码
terser-webpack-plugin
hard-source-webpack-plugin
缩小构建目标
构建体积
包大小分析
npm install webpack-bundle-analyzer –save-dev
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
module.exports = {
plugins: [new BundleAnalyzerPlugin()],
};
复制代码
优化策略
- 多进程并行压缩代码
npm install terser-webpack-plugin css-minimizer-webpack-plugin –save-dev
module.exports = {
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
parallel: true, // 启用多进程并行压缩代码
}),
new TerserPlugin({
parallel: true, // 启用/禁用多进程并行压缩代码
}),
],
},
};
复制代码