# webpack 5 从零开始构建项目
webpack于两个月前(今年10月)发布,距离上一次大版本升级已经过去两年了,目测本文有效期也可以挺个两年。
本文基于webpack5从零开始构建一个项目,梳理一下整个webpack构建流程,中间可能提及与webpack4的差异,顺便搞清楚自己一直以来对webpack理解模糊的点。
# 初始化项目
以React项目为例,新建项目文件夹并初始化。
// 创建文件夹
mkdir react-app
cd react-app
npm init
一路回车到底,会生成一个package.json文件
新建入口文件app.js和HTML模板index.html
// app.js
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => {
return <div>webpack5</div>
}
ReactDOM.render(<App />, document.querySelector("#app"));
// index.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>webpack 5 从零开始构建项目</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
安装react依赖,以及加载less文件所需的loader
npm i react react-dom style-loader css-loader less less-loader -S
安装开发工具依赖
npm i webpack webpack-cli -D
开发环境需要用到devServer
npm i @webpack-cli/serve webpack-dev-server -D
安装babel系列,转义ES6和React
npm i @babel/core @babel/preset-env @babel/preset-react babel-loader babel-preset-react -D
安装html-webpack-plugin,从模板文件生成HTML
npm i html-webpack-plugin -D
创建.babelrc文件并配置
{
"presets": [
"@babel/preset-react"
]
}
# 开始配置webpack.config.js
// webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./app.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "webpack.bundle.js",
},
module: {
rules: [
{
test: /\.less$/,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "less-loader",
options: {
lessOptions: {
strictMath: true,
},
},
},
],
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [["@babel/preset-env", { targets: "defaults" }]],
cacheDirectory: true,
},
},
},
],
},
plugins: [
// 根据指定模板文件生成HTML
new HtmlWebpackPlugin({
template: "./index.html",
}),
// 开启热替换
new webpack.HotModuleReplacementPlugin(),
],
};
然后,在package.json里,配置scripts命令
"dev": "webpack serve --config ./webpack.config.js"
webpack serve会自动启动DevServer服务,不需要在webpack.config.js里做任何配置。
运行项目,本地服务默认端口为8080
npm run dev
如果提示端口占用,配置另一个端口即可
devServer: {
port: 9000
}
# DllPlugin 单独编译第三方库
npm run dev编译结果提示文件大小超出244kb,可能影响性能,需要将大文件拆分,怎么拆?
项目中,react、react-dom等npm包很少变更,不需要每次都重复编译,因此将这类包单独编译成一个文件。DllPlugin就是用来干这个活的。
DllPlugin与DllReferencePlugin配合使用,DllPlugin创建一个manifest.json文件供DllReferencePlugin提供映射依赖关系使用。
DllPlugin在webpack.dll.config.js中配置,DllReferencePlugin在webpack.config.js中配置。
另外,开启代码压缩,webpack4代码压缩插件是new webpack.optimize.UglifyJsPlugin(),不支持ES6,在压缩某些包的时候会报以下错误:
Unexpected token: punc (()
在webpack5中,内置代码压缩插件 terser-webpack-plugin
,支持ES6,所以直接用它就好。
// webpack.dll.config.js
const vendors = ["react", "react-dom"];
const webpack = require("webpack");
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
// 注意:output.library和DllPlugin配置的name必须完全一样
module.exports = {
output: {
path: path.resolve('./static/js'),
filename: "[name].dll.js?v=[hash]",
library: "[name]",
},
entry: {
dll_base: vendors,
},
optimization: {
// 代码压缩
minimize: true,
minimizer: [
new TerserPlugin({
// 开启多进程并行构建
parallel: true,
}),
],
},
plugins: [
// 生成映射文件给DllReferencePlugin使用
new webpack.DllPlugin({
path: path.resolve(__dirname, "./manifest.base.json"),
name: "[name]",
context: __dirname,
}),
],
};
webpack.config.js增加一个插件配置
plugins: [
// 把只有 dll 的 bundle(们)引用到需要的预编译的依赖
new webpack.DllReferencePlugin({
context: __dirname,
manifest,
name: "dll_base",
}),
]
在package.json中增加scripts命令
"dll": "webpack --config ./webpack.dll.config.js",
执行npm run dll会生成dll_base.dll.js,手动在HTML模板里引入
<scsript src="./static/js/dll_base.dll.js"></script>
开发过程中,定位报错的代码,发现是编译之后的,不利于调试,开启source-map:
devtool: "eval-source-map"
devtool有多种可选值 (opens new window),兼顾编译速度与可调试性,选择"eval-source-map"
到目前为止,已经构建了一个最基本的项目的开发环境。