【webpack】常用webpack配置
通用
区分环境与配置文件
区分不同环境的配置文件
将不同环境的配置文件抽离到单独的文件中:
- webpack.base.config.js 定义公共的配置。
- webpack.dev.config.js:定义开发环境的配置。
- webpack.prod.config.js:定义生产环境的配置。
用 webpack-merge
合并配置:
- 安装
npm install webpack-merge -D
- 配置
const merge = require('webpack-merge');
merge({
devtool: 'cheap-module-eval-source-map',
module: {
rules: [
{a: 1}
]
},
plugins: [1,2,3]
}, {
devtool: 'none',
mode: "production",
module: {
rules: [
{a: 2},
{b: 1}
]
},
plugins: [4,5,6],
});
//合并后的结果为
{
devtool: 'none',
mode: "production",
module: {
rules: [
{a: 1},
{a: 2},
{b: 1}
]
},
plugins: [1,2,3,4,5,6]
}
使用不同配置文件:
- 安装
npm install cross-env -D
- 配置启动命令
在启动命令中配置环境变量和要使用的配置文件。
// package.json
{
"scripts": {
"dev": "cross-env NODE_ENV=development webpack --config build/webpack.dev.config.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.config.js"
}
}
- 在 js 中读取环境变量
const isProdEnv = process.env.NODE_ENV === "production";
const isDevEnv = process.env.NODE_ENV === "development";
处理 JS:babel-loader
js 编译、降级。
安装
npm install babel-loader -D npm install @babel/core @babel/preset-env @babel/plugin-transform-runtime -D npm install @babel/runtime @babel/runtime-corejs3
webpack 配置
在 webpack 中配置 babel。
//webpack.config.js module.exports = { // mode: 'development', module: { rules: [ { test: /\.jsx?$/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"], plugins: [ [ "@babel/plugin-transform-runtime", { corejs: 3, }, ], ], }, }, exclude: /node_modules/, }, ], }, };
babel 配置
可以创建一个 .babelrc 文件单独放置 babel 的配置信息。
{ "presets": ["@babel/preset-env"], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 3 } ] ] }
处理 html:html-webpack-plugin
处理 html 文件,官网地址:html-webpack-plugin。
安装
npm install html-webpack-plugin -D
配置
// webpack.config.js const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { //... plugins: [ // 数组 放着所有的webpack插件 new HtmlWebpackPlugin({ template: "./public/index.html", filename: "index.html", //打包后的文件名 minify: { removeAttributeQuotes: false, //是否删除属性的双引号 collapseWhitespace: false, //是否折叠空白 config: { title: "webpack", }, }, // hash: true //是否加上hash,默认是 false }), ], };
HtmlWebpackPlugin 插件配置可以在 html 中用 htmlWebpackPlugin.options
来访问,需要用 ejs 模板语法来写:
<div><%= (htmlWebpackPlugin.options.config.title) %></div>
<div><%= (htmlWebpackPlugin.options.filename) %></div>
自定义 chunk 引入位置
使用 HtmlWebpackPlugin 插件处理 html 时,会根据 inject 配置来决定要将 chunk 插入到 html 的什么位置。
inject 的可选值为:
true
:script 标签位于 html 文件的 body 底部。false
:不插入生成的 js 文件。'body'
:script 标签位于 html 文件的 body 底部。'head'
:script 标签位于 html 文件的 head 底部。
如果 'body'
、'head'
不满足实际需求而你想要完全控制你的所有资源的话,可以将 inject 设置为 false
,然后手动在 html 文件中引入 chunk。
- 配置
在 ejs 模板中可以通过 htmlWebpackPlugin.files 直接访问编译时生成的文件:
publicPath: string;
js: string[];
css: string[];
manifest?: string;
favicon?: string;
ejs 模板中添加如下代码插入 css:
<% for (var chunk in htmlWebpackPlugin.files.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[chunk] %>" />
<% } %>
ejs 模板中添加如下代码插入 script:
<% for (var chunk in htmlWebpackPlugin.files.js) { %>
<script defer src="<%=htmlWebpackPlugin.files.js[chunk]%>"></script>
<% } %>
多入口时指定注入的 chunk
webpack 会根据入口配置生成若干个 chunk,使用 HtmlWebpackPlugin 插件处理 html 时,默认情况下会给每一个页面都引入所有的 chunk。
而多页应用中我们希望每个页面只应用自己的 chunk 和公共的 chunk,这个需求可以通过 HtmlWebpackPlugin 的 chunks 配置来实现。
- 配置
假设项目中有两个页面:home.html 和 order.html,目录结构如下:
- src
- page
- home
- index.js
- index.html
- order
- index.js
- index.html
webpack 配置如下:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
home: __dirname + "/src/page/home/index.js",
order: __dirname + "/src/page/order/index.js",
},
plugins: [
new HtmlWebpackPlugin({
title: "首页",
filename: "home.html",
template: __dirname + "/src/page/home/index.html",
chunks: ["home"],
}),
new HtmlWebpackPlugin({
title: "订单",
filename: "order.html",
template: __dirname + "/src/page/order/index.html",
chunks: ["order"],
}),
],
};
因为有两个入口,所以会生成 home、order 两个 chunk,在 HtmlWebpackPlugin 的配置中加入 chunks 配置,指定要引入的 chunk 名称,就可以避免在每个页面中引入所有 chunk。
处理样式
webpack 不能直接处理 css,需要借助 loader。如果是 .css,我们需要的 loader 通常有: style-loader、css-loader,考虑到兼容性问题,还需要 postcss-loader。
而如果是 less 或者是 sass 的话,还需要 less-loader 和 sass-loader,这里配置一下 less 和 css 文件(sass 的话,使用 sass-loader 即可):
- 安装
npm install style-loader less-loader css-loader postcss-loader autoprefixer less -D
- 配置
//webpack.config.js
module.exports = {
//...
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
plugins: function () {
return [
require("autoprefixer")({
overrideBrowserslist: [">0.25%", "not dead"],
}),
];
},
},
},
"less-loader",
],
exclude: /node_modules/,
},
],
},
};
style-loader 动态创建 style 标签,将 css 插入到 head 中。
css-loader 负责处理 @import 等语句。
postcss-loader 和 autoprefixer,自动生成浏览器兼容性前缀。
less-loader 负责处理编译 .less 文件,将其转为 css。
抽离 css:mini-css-extract-plugin
将 css 抽离到单独文件中,extract-text-webpack-plugin 插件也有同样的功能,相比而言 mini-css-extract-plugin 更好。
抽离 css 可以减少单个模块的大小,但是页面会增加新的 http 请求。
- 安装
npm install mini-css-extract-plugin -D
- 配置
//webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
//...
plugins: [
new MiniCssExtractPlugin({
filename: "css/[name].css",
}),
],
module: {
rules: [
{
test: /\.(c|le)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
reloadAll: true, // 解决开发环境修改css后页面不刷新问题
},
},
"css-loader",
"less-loader",
],
exclude: /node_modules/,
},
],
},
};
图片/文件处理:url-loader
- 安装
# 如果提示需要安装 file-loader 则执行 npm install file-loader -D
npm install url-loader -D
- 配置
module.exports = {
modules: {
rules: [
{
test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
use: [
{
loader: "url-loader",
options: {
limit: 10240, // 小于 10K 时将资源编译为 base64,超过 10K 时将文件拷贝到输出目录
esModule: false, // esModule 设置为 false,否则,<img src={require('XXX.jpg')} /> 会出现 <img src=[Module Object] />
name: '[name]_[hash:6].[ext]', // 修改编译后的资源文件名
outpath: 'assets', // 修改输出目录
},
},
],
},
];
}
}
注入全局变量
使用 ProvidePlugin 注入的全局变量可以在项目中的所有地方使用。
export default
导出的模块(vue.esm.js)必须要指定 default
。
module.exports
导出的模块(react)不需要写 default
。
- 配置
const webpack = require("webpack");
module.exports = {
//...
plugins: [
new webpack.ProvidePlugin({
React: "react",
Component: ["react", "Component"],
Vue: ["vue/dist/vue.esm.js", "default"],
$: "jquery",
_map: ["lodash", "map"],
}),
],
};
如果启动了 eslint 的话需要修改 eslint 的配置:
{
"globals": {
"React": true,
"Vue": true
//....
}
}
多页应用配置
开发多页应用时需要配置多个入口和 HtmlWebpackPlugin。
配置多个 HtmlWebpackPlugin 时 filename 字段不可缺省,否则默认生成的都是 index.html,同时需要配置 chunks 字段,指定每个页面需要引入的 chunk。
//webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
index: "./src/index.js",
login: "./src/login.js",
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[hash:6].js",
},
//...
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
filename: "index.html", //打包后的文件名
chunks: ["index"],
excludeChunks: ["jquery"], // 指定不想引入的 chunk
}),
new HtmlWebpackPlugin({
template: "./public/login.html",
filename: "login.html", //打包后的文件名
chunks: ["login"],
}),
],
};
路径别名
- 配置
//webpack.config.js
module.exports = {
//....
resolve: {
alias: {
"@": "src",
},
},
};
配置模块默认后缀
- 配置
//webpack.config.js
module.exports = {
//....
resolve: {
extensions: [".json", ".js"],
},
};
寻找模块时从左到右遍历后缀列表,因此最好将频率最高的后缀放在第一位,并且控制列表的长度,以减少尝试次数。
经过上面配置后,对于 import ('../dialog')
导入语句来说,会先寻找 ../dialog.json
然后再寻找 ../dialog.js
。
生产环境
打包前清空 dist 目录:clean-webpack-plugin
- 安装
npm install clean-webpack-plugin -D
- 配置
//webpack.config.js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ["**/*", "!dll", "!dll/**"], // 不删除dll目录下的文件
}),
],
};
静态资源拷贝:copy-webpack-plugin
- 安装
npm install copy-webpack-plugin -D
- 配置
//webpack.config.js
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
module.exports = {
//...
plugins: [
new CopyWebpackPlugin(
[
{
from: "public/js",
to: path.resolve(__dirname, "dist", "js"),
},
//还可以继续配置其它要拷贝的文件
],
{
ignore: ["other.js"], // 可设置要忽略的文件
}
),
],
};
压缩 css:optimize-css-assets-webpack-plugin
- 安装
npm install optimize-css-assets-webpack-plugin -D
- 配置
//webpack.config.js
const OptimizeCssPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
entry: "./src/index.js",
//....
plugins: [new OptimizeCssPlugin()],
};
开发环境
搭建开发环境:webpack-dev-server
- 安装
npm install webpack-dev-server -D
npm install cross-env -D
- 配置
// package.json
{
"scripts": {
"serve": "cross-env NODE_ENV=development webpack-dev-server",
"build": "cross-env NODE_ENV=production webpack"
}
}
热更新
- 首先配置 devServer 的 hot 为 true。
- 在 plugins 中增加 new webpack.HotModuleReplacementPlugin()。
//webpack.config.js
const webpack = require("webpack");
module.exports = {
//....
devServer: {
hot: true,
},
plugins: [
new webpack.HotModuleReplacementPlugin(), //热更新插件
],
};
我们配置了 HotModuleReplacementPlugin 之后,会发现,此时我们修改代码,仍然是整个页面都会刷新。不希望整个页面都刷新,还需要修改入口文件:
if (module && module.hot) {
module.hot.accept();
}
配置代理实现跨域
假设前端在 3000 端口,服务端在 4000 端口,可以通过配置代理来实现跨域。
- 配置
//webpack.config.js
module.exports = {
//...
devServer: {
proxy: {
"/api": "http://localhost:4000",
},
},
};
配置 mock 数据:mocker-api
开发时如果有 mock 接口可以提高对接效率,如果后端没有提供,可以使用 mocker-api
自己配置。
- 安装
npm install mocker-api -D
- webpack 配置
// webpack.config.dev.js
const apiMocker = require("mocker-api");
module.export = {
//...
devServer: {
before(app) {
apiMocker(app, path.resolve("./mock/mocker.js"));
},
},
};
/mock/mocker.js
配置
module.exports = {
"GET /user": { name: "刘小夕" },
"POST /login/account": (req, res) => {
const { password, username } = req.body;
if (password === "888888" && username === "admin") {
return res.send({
status: "ok",
code: 0,
token: "sdfsdfsdfdsf",
data: { id: 1, name: "刘小夕" },
});
} else {
return res.send({ status: "error", code: 403 });
}
},
};
在调用对应接口时会返回 mock 数据:
fetch("/login/account", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
username: "admin",
password: "888888",
}),
})
.then((response) => response.json())
.then((data) => console.log(data))
.catch((err) => console.log(err));
映射源代码
经过 webpack 打包之后在浏览器中查看源代码看到的是压缩编译后的代码,不方便调试,可以通过配置 devtool 来将编译后的代码映射会原始代码。
//webpack.config.js
module.exports = {
devtool: "cheap-module-eval-source-map", //开发环境下使用
};