116194327106884275
前言
Webpack 是一个开源的前端打包工具。Webpack 提供了前端开发缺乏的模块化开发方式,将各种静态资源视为模块,并从它生成优化过的代码。(维基百科)
下载依赖
1
| npm install -D webpack webpack-cli
|
执行打包
- 如果项目根目录存在
webpack.config.js文件,则读取自定义配置,否则使用默认配置
--stats detailed:展示详细打包信息
--watch:观察者模式,实时侦测文件变化并自动重新打包
--env <key>、--env <key>=<value>:指定环境变量
-c <file>、--config <file>:指定配置文件,缺省值为./webpack.config.js
--entry <file>.js:指定入口文件,缺省值为./src/index.js
--ouput-filename <file_name>.js:指定打包后的文件名,缺省值为main.js
--output-path <dir_name>:指定打包后的文件所在目录,缺省值为./dist
配置文件
mode:打包环境
none:不指定打包环境
development:开发环境
production:生产环境
entry:入口配置
output:出口配置
devtool:SourceMap配置
module:解析文件的模块配置
resolve:解析规则配置
plugins:插件配置
optimization:代码优化配置
performance:性能配置
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13
| const path = require("path");
module.exports = { mode: "none", entry: {}, output: {}, devtool: false, module: {}, resolve: {}, plugins: [], optimization: {}, performance: {} };
|
入口配置
webpack.config.js1 2 3
| module.exports = { entry: "./src/index.js" };
|
index、another:定义多个入口
import:定义入口文件
dependOn:定义共享模块
<package_name>:定义共享模块名
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = { entry: { index: { import: "./src/index.js", dependOn: "shared", }, another: { import: "./src/another.js", dependOn: "shared", }, shared: ["<package_name>"] }, output: { filename: "[name].bundle.js" } };
|
出口配置
filename:定义文件名
chunkFilename:定义代码块文件名
path:指定打包后的文件所在目录,必须是绝对路径
clean:是否清理上一次打包的文件
publicPath:指定打包后的资源的URL前缀
webpack.config.js1 2 3 4 5 6 7 8 9
| module.exports = { output: { filename: "[name].bundle.js", chunkFilename: "[name].chunk.js", path: path.resolve(__dirname, "dist"), clean: true, publicPath: "http://127.0.0.1:80/", } };
|
SourceMap配置
false:不生成SourceMap
none:production环境下的缺省值,不生成SourceMap
eval:development环境下的缺省值,不生成SourceMap
source-map:production环境下的配置,生成SourceMap文件
cheap-source-map:development环境下的配置,生成SourceMap文件,不生成列映射,只生成行映射
cheap-module-source-map:development环境下的配置,生成SourceMap文件,不生成列映射,只生成行映射,使用Loader的情况下使用这种方式
eval-source-map:生成SourceMap,添加到eval()后面
inline-source-map:生成SourceMap,添加到源码文件末尾
hidden-source-map:生成SourceMap文件,但是源码文件末尾不会添加//# sourceMappingURL=注解
nosources-source-map:生成SourceMap文件,但是不会映射源码,只能查看到报错的源码位置,不能直接跳转查看源码
webpack.config.js1 2 3
| module.exports = { devtool: false };
|
解析器配置
手动指定导入的文件所使用的解析器
1
| import "<loader_name>!<file>";
|
通过配置文件配置解析器
test:使用正则表达式匹配需要解析的文件名
use:指定解析器
"":只有一个解析器,并且无其他配置,则可以直接指定解析器名
["", ""]:有多个解析器,但无其他配置,则可以直接指定多个解析器名,多个解析器调用顺序为从后往前
[{}, {}]:有多个解析器,且有其他配置,则可以指定多个解析器配置,多个解析器调用顺序为从后往前
type:指定资源类型
asset/resource:直接打包文件,并得到文件的URL,通过内置的file-loader实现
asset/inline:得到文件的的Base64编码值,通过内置的url-loader实现
asset/source:得到文件的原始内容,通过内置的raw-loader实现
asset:缺省值,根据parser.dataUrlCondition.maxSize配置的文件大小(单位字节),小于等于则切换为asset/inline,大于则切换为asset/resource
generator:指定打包后的文件配置
filename:指定打包后的文件路径及文件名
[id]:文件id
[path]:原始文件路径
[name]:原始文件名,不包含扩展名
[ext]:原始文件后缀名
[query]:原始文件查询字符串
[hash]:原始文件哈希值,任意文件名或文件内容发生变化,哈希值都会变化
[hash:<num>]:截取指定长度的文件哈希值
[contenthash]:原始文件哈希值,任意文件内容发生变化,哈希值都会变化
[chunkhash]:原始文件哈希值,任意代码块文件内容发生变化,哈希值都会发生变化
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| module.exports = { module: { rules: [ { test: /<file_name>/, use: "<loader_name>", type: "asset", parser: { dataUrlCondition: { maxSize: 10*1024 } }, generator: { filename: "[name]" }, } ] } };
|
解析规则配置
mainFiles:导入时自动匹配主文件名
extensions:导入时自动匹配扩展名
alias:定义路径别名
webpack.config.js1 2 3 4 5 6 7 8 9
| module.exports = { resolve: { mainFiles: ["index"], extensions: [".wasm", ".mjs", ".js", ".json"], alias: { "@src": path.resolve(__dirname, "src") } } };
|
插件配置
全局变量插件
定义全局变量
webpack.config.js1 2 3 4 5 6 7 8 9
| const { DefinePlugin } = require("webpack");
module.exports = { plugins: [ new DefinePlugin({ "key": JSON.stringify("value") }) ] };
|
使用全局变量
使用内置全局变量
1
| console.log(process.env.NODE_ENV);
|
作用域提升插件
webpack.config.js1 2 3 4 5
| module.exports = { plugins: [ new Webpack.optimize.ModuleConcatenationPlugin() ] };
|
压缩插件
webpack.config.js1 2 3 4 5 6 7 8 9 10
| const CompressionPlugin = require("compression-webpack-plugin");
module.exports = { plugins: [ new CompressionPlugin({ test: /\.(js|css)$/, algorithm: "gzip" }) ] };
|
代码优化配置
自动分包
- 通过内置的插件
split-chunks-plugin实现自动分包
chunks:指定分包范围
async:缺省值,异步的函数调用自动分包
all:所有函数调用自动分包
minSize:拆分后的每个代码块文件最小大小(单位字节)
maxSize:代码块文件达到指定大小时立即自动分包,对于一个整体不能再次拆分
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = { entry: { index: "./src/index.js", another: "./src/another.js", }, output: { filename: "[name].bundle.js", }, optimization: { splitChunks: { chunks: "all", minSize: 10*1024, maxSize: 10*1024 } } };
|
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| module.exports = { entry: { index: "./src/index.js", another: "./src/another.js", }, output: { filename: "[name].bundle.js", }, optimization: { splitChunks: { chunks: "all", cacheGroups: { "chunk-name-1": { test: /<file_name>/, minSize: 10*1024, maxSize: 10*1024 }, "chunk-name-2": { test: /<file_name>/, minSize: 10*1024, maxSize: 10*1024 } } } } };
|
代码简化
简化JS代码
extractComments:是否抽取注释,缺省值为true表示将注释抽取到单独的文件
parallel:是否使用多线程构建,缺省值为true表示使用多线程构建
terserOptions:Terser的配置
unused:是否保留没有使用过的函数,默认会优化
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const TerserPlugin = require("terser-webpack-plugin");
module.exports = { optimization: { minimize: true, minimizer: [ new TerserPlugin({ extractComments: true, parallel: true, terserOptions: { compress: { unused: false } } }) ] } };
|
简化CSS代码
传送门
通过动态导入实现手动分包
- 定义模块为懒加载,在访问先不加载这个资源,如果这个资源被调用,才会自动加载这个资源
1 2 3
| import("模块名").then(function (模块名) { 模块名.导出的函数名(); });
|
1 2 3
| import("模块名").then(function (模块名) { 模块名.default(); });
|
/* key: value */:魔法注释,多个参数之间用,隔开
webpackChunkName: "代码块文件名":定义代码块文件名
webpackPrefetch: true:定义模块为预获取,打包后的HTML中会引入<link rel="prefetch" as="script" href="">标签,在访问这个HTML时,先不加载这个资源,只有在网络空闲时才会自动加载这个资源
webpackPreload: true:定义模块为预加载,在访问先不加载这个资源,如果这个资源被调用,才会自动加载这个资源
1 2 3
| import("模块名").then(function (模块名) { 模块名.导出的函数名(); });
|
环境变量
修改配置文件
webpack.config.js1 2 3 4 5
| module.exports = function (env) { return { ... } };
|
执行打包时指定环境变量
- 执行打包时指定环境变量,通过环境变量添加了部分配置
1
| webpack --env <key>=<value>
|
利用环境变量来确定打包环境
修改配置文件
- 如果传递了环境变量
production,则以生产环境打包,否则以开发环境打包
webpack.config.js1 2 3 4 5
| module.exports = function (env) { return { mode: env.production ? "production" : "development", } };
|
打包时指定环境变量
1
| webpack --env production
|
多环境配置
下载依赖
1
| npm install -D webpack-merge
|
修改配置文件
- 拆分多个配置文件(
webpack.config.dev.js、webpack.config.prod.js)、抽离公共配置文件(webpack.config.common.js)
1 2 3 4 5 6
| + config - webpack.config.js - webpack.config.common.js - webpack.config.dev.js - webpack.config.prod.js - index.js
|
- 如果不需要判定是否为生产环境,可以直接导出对象
- 如果需要判定是否为生产环境,则需要将导出对象改为导出函数
webpack.config.common.js
webpack.config.dev.js1 2 3 4
| module.exports = function (env) { const isProduction = env.isProduction; return {}; };
|
webpack.config.prod.js1 2 3 4
| module.exports = function (env) { const isProduction = env.isProduction; return {}; };
|
webpack.config.js1 2 3 4 5 6 7 8 9
| const { merge } = require("webpack-merge");
const commonConfig = require("./webpack.config.common"); const devConfig = require("./webpack.config.dev"); const prodConfig = require("./webpack.config.prod");
module.exports = function (env) { return merge(commonConfig, env.isProduction ? prodConfig(env) : devConfig(env)); };
|
执行打包时指定环境
1
| webpack --config ./config/webpack.config.js --env production
|
DevServer配置
下载依赖
1
| npm install -D webpack-dev-server
|
修改配置文件
static:指定静态资源目录
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = { devServer: { static: ["public"], host: "0.0.0.0", port: 8080. proxy: { "/api": { target: "http://127.0.0.1:3000", changeOrigin: true, pathRewrite: { "^/api": "" } } } } };
|
启动服务器
实现TreeShaking
通过Terser对JS函数进行TreeShaking
optimization.usedExports:对从未使用的函数通过魔法函数unused harmony export null进行标记,Terser会自动忽略具有标记的函数的打包
webpack.config.js1 2 3 4 5 6 7 8 9 10 11
| const TerserPlugin = require("terser-webpack-plugin");
module.exports = { optimization: { usedExports: true }, minimize: true, minimizer: [ new TerserPlugin() ] }
|
通过Purgecss对CSS类进行TreeShaking
webpack.config.js1 2 3 4 5 6 7 8 9 10 11
| const TerserPlugin = require("terser-webpack-plugin");
module.exports = { optimization: { usedExports: true }, minimize: true, minimizer: [ new TerserPlugin() ] }
|
指定忽略TreeShaking的文件
sideEffects:指定需要Webpack忽略TreeShaking的文件,缺省值为false,表示没有需要忽略TreeShaking的文件
package.json1 2 3
| { "sideEffects": false }
|
- JS文件如果导出的模块有非纯函数,具有副作用,则需要忽略这个JS文件的TreeShaking
package.json1 2 3 4 5
| { "sideEffects": [ "./src/<file>.js" ] }
|
- 忽略所有CSS文件的TreeShaking
- 因为所有CSS文件的导入一定没有使用,都是
import "",所以需要忽略所有CSS文件的TreeShaking
package.json1 2 3 4 5
| { "sideEffects": [ "*.css" ] }
|
SourceMap文件
- 如果启用了SourceMap,打包后会生成
.js.map文件,.js文件的最后一行会自动配置指向的.js.map文件
bundle.js
- 浏览器需要开启SourceMap功能才会自动解析SourceMap文件,默认是开启状态
Settings->Preferences->Sources->勾选Enable JavaScript source maps->勾选Enable CSS source maps
完成