README
webpack 学习
Install
npm install -D webpack webpack-cli
npx webpack
核心概念
Entry
打包入口
// 单入口:字符串
entry: "./src/index.js",
// 多入口:对象
entry: {
app: "./src/app.js",
search: "./src/search.js",
},
webpack.config.js
module.exports = {
// 单入口:字符串
entry: "./src/index.js",
};
module.exports = {
// 多入口:对象
entry: {
app: "./src/app.js",
search: "./src/search.js",
},
};
Output
打包出口
// 单入口对应的出口
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist/"),
},
// 多入口对应的出口
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/"),
},
webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js",
// 单入口对应的出口
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist/"),
},
};
module.exports = {
entry: "./src/index.js",
// 多入口对应的出口
// [name] 是个文件名占位符,对应 output 对象的 key
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/"),
},
};
Loader
webpack 原生支持 js 和 json 两种文件类型的打包,其他类型文件需要通过 Loader 来支持。
本身是一个函数。以 raw-loader 为例:
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.txt$/i,
use: "raw-loader",
},
],
},
};
file.js
import txt from "./file.txt";
Plugins
用户 bundle 文件的优化,资源管理和环境变量注入。
作用于整个构建过程。以 HtmlWebpackPlugin 为例:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist/"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/template.html",
}),
],
};
Mode
构建环境。production(默认值), development, none。
webpack.config.js
module.exports = {
mode: "development",
};
转译 ES6+ 和 JSX
ES6+
将 ES6+ 代码转译成 ES5 代码,需要使用 babel-loader。
npm install -D babel-loader @babel/core @babel/preset-env webpack
npm install --save-dev @babel/plugin-proposal-class-properties
webpack.config.js
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-proposal-class-properties"],
},
},
},
];
}
或者是 webpack.config.js + babel.config.json
// webpack.config.js
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: "babel-loader",
},
];
}
// babel.config.json
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
React
为了能正确处理 React 代码,需要用到 @babel/preset-react
// babel.config.json
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
// index.js
import React from "react";
import ReactDOM from "react-dom";
const App = () => {
return <div>Hello World</div>;
};
ReactDOM.render(<App />, document.body);
CSS & Sass
CSS
在 webpack 中解析 CSS,至少需要两个 loader: css-loader, style-loader
css-loader 将 css 文件转成 commonjs 对象,style-loader 通过 <style>
标签,将样式插入到 <head>
中去
npm install --save-dev css-loader
npm install --save-dev style-loader
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};
// file.js
import css from "file.css";
Sass
为了支持使用 Sass,需要安装 sass-loader.
npm install sass-loader sass webpack --save-dev
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
},
],
},
};
加载图片和字体文件
使用 file-loader
加载图片
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: "file-loader",
},
],
},
],
},
};
// 使用方式一:通过 url() 引入图片
h1 {
background-image: url(./images/webpack-site-logo.svg);
background-repeat: no-repeat;
background-size: contain;
font-size: 0;
height: 40px;
}
// 使用方式二:作为 img src 资源引入
import img from "./file.png";
const App = () => {
return <img src={logo} />;
};
加载字体文件
module.exports = {
module: {
rules: [
{
test: /\.(woff2?|eot|ttf|otf)$/i,
use: [
{
loader: "file-loader",
},
],
},
],
},
};
@font-face {
font-family: "FiraCode";
src: url("./fonts/FiraCode-Regular.woff2") format("woff2");
}
body {
font-family: FiraCode, "Courier New", Courier, monospace;
}
还可以使用 url-loader,它是 file-loader 的封装,不过支持较小资源直接 base64 嵌入
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: "url-loader",
options: {
limit: 8192,
},
},
],
},
],
},
};
文件监听
两种方式:
- webpack 命令行参数指定:webpack --watch
- 在配置文件中通过设置 watch: true 实现
使用 webpack-dev-server
webpack-dev-server
webpack-dev-server: 实现热更新:不刷新浏览器
npm install --save-dev webpack-dev-server
devServer: {
contentBase: './dist',
// 启用热更新能力
// See: https://webpack.js.org/guides/hot-module-replacement/
hot: true,
},
"scripts": {
"dev": "webpack serve --open"
},
入口文件需要加点代码,支持热更新,否则是全更新的
// ...
if (module.hot) {
module.hot.accept();
}
热更新原理
hot-module-replacement 为 webpack 打包出来的 bundle 文件中注入 HMR runtime。一旦文件有修改,HMR server 将修改的 js module 信息发送给(通过 websocket) HMR runtime, 由 HMR runtime 负责布局页面的更新操作。
hot-module-replacement 包给 webpack-dev-server 提供了热更新能力。
整个热更新分成两个过程:
- 启动阶段:将源代码编译成 bundle 文件 - 通过 bundle serve 提供 bundle 文件
- 更新阶段:将被修改的 js module 编译,通过 HMR server 将修改通知 HMR runtime,由后者来负责页面局部刷新
文件指纹
打包文件的后缀名。好处:新文件立即生效;旧文件使用缓存,依旧可用。
根据作用范围,文件指纹还分:
- Hash: 项目级别指纹 - 只要项目中有文件修改,此值就发生改变
- ChunkHash: entry 级别的指纹 - 不同的 entry 会生成不同的值
- ContentHash: 文件级别的指纹 - 文件内容不变,此值就不发生改变
outout 使用 [chunkhash]
output: {
filename: "index_bundle.[chunkhash:6].js",
path: path.resolve(__dirname, "dist/"),
},
单独 css 文件使用 [contenthash],需要安装 mini-css-extract-plugin
npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
output: {
// See: https://stackoverflow.com/questions/64294706/webpack5-automatic-publicpath-is-not-supported-in-this-browser
publicPath: "",
// ...
},
plugins: [
new MiniCssExtractPlugin({
filename: `[name].[contenthash:6].css`,
}),
],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
图片字体文件 [hash]
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: "file-loader",
options: {
name: "images/[name].[hash:6].[ext]",
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
use: [
{
loader: "file-loader",
options: {
// 包含存储目录
name: "fonts/[name].[hash:6].[ext]",
},
},
],
},
],
},
代码压缩
压缩 CSS
https://github.com/NMFR/optimize-css-assets-webpack-plugin
npm install --save-dev optimize-css-assets-webpack-plugin
npm i -D cssnano
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
plugins: [
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require("cssnano"),
}),
],
清理构建产物
https://github.com/johnagan/clean-webpack-plugin
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const webpackConfig = {
plugins: [
/**
* All files inside webpack's output.path directory will be removed once, but the
* directory itself will not be. If using webpack 4+'s default configuration,
* everything under <PROJECT_DIR>/dist/ will be removed.
* Use cleanOnceBeforeBuildPatterns to override this behavior.
*
* During rebuilds, all webpack assets that are not used anymore
* will be removed automatically.
*
* See `Options and Defaults` for information
*/
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [
"**/*",
"!static-files*",
"!directoryToExclude/**",
],
}),
],
};
module.exports = webpackConfig;
CSS3 自动补充前缀
要用到 postcss-loader
npm install --save-dev postcss-loader postcss
npm install postcss-preset-env --save-dev
postcss-preset-env 中包含 autoprefixer
{
test: /\.s[ac]ss/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
browsers: 'last 2 versions'
},
],
],
},
},
},
],
},
静态资源引入
inde.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<%= require('raw-loader!./meta.html').default %>
<title>demo page</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
提取公共资源
使用 HtmlWebpackExternalsPlugin
new HtmlWebpackExternalsPlugin({
externals: [
{
module: "react",
entry: "https://unpkg.com/react@17/umd/react.development.js",
global: "React",
},
{
module: "react-dom",
entry: "https://unpkg.com/react-dom@17/umd/react-dom.development.js",
global: "ReactDOM",
},
],
}),
使用
// See: https://webpack.js.org/plugins/split-chunks-plugin/#split-chunks-example-3
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /(react|react-dom)/,
name: "vendors",
chunks: "all",
},
},
},
},