前言
最近在做一个关于webpack5手动搭建项目的测试练习,网上找了很多文章,发现很多文章按照他们的步骤搭建,都会有各种各样的报错,于是自己参考网上各类文章,加上自己的实际搭建,完成了这篇文章。如果有错误,欢迎在评论区指出。
时间 2021/09/28
初始化环境
生成package.json
yarn init -y
安装webpack 和webpack-cli
yarn add -D webpack webpack-cli
webpack-cli
是webpack
的命令行工具,用于在命令行中使用webpack
。
配置react支持
下载react和react-dom
yarn add react react-dom
接下来让我们的项目先支持react
和jsx
。
支持jsx
需要额外配置babel
去处理jsx
文件,将jsx
转译成为浏览器可以识别的js
。
这里我们需要用到如下几个库:
babel-loader
@babel/core
@babel/preset-env
@babel/plugin-transform-runtime
@babel/preset-react
我们来稍微梳理一下这几个babel
的作用
babel-loader
首先对于我们项目中的jsx
文件我们需要通过一个"转译器"将项目中的jsx
文件转化成js
文件,babel-loader
在这里充当的就是这个转译器。
@babel/core
babel-loader
仅仅识别出了jsx
文件,内部核心转译功能需要@babel/core
这个核心库,@babel/core
模块就是负责内部核心转译实现的。
@babel/preset-env
@babel/prest-env
是babel
转译过程中的一些预设,它负责将一些基础的es 6+
语法,比如const/let...
转译成为浏览器可以识别的低级别兼容性语法。
这里需要注意的是
@babel/prest-env
并不会对于一些es6+
高版本语法的实现,比如Promise
等polyfill
,你可以将它理解为语法层面的转化不包含高级别模块(polyfill
)的实现。
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime
,上边我们提到了对于一些高版本内置模块,比如Promise/Generate
等等@babel/preset-env
并不会转化,所以@babel/plugin-transform-runtime
就是帮助我们来实现这样的效果的,他会在我们项目中如果使用到了Promise
之类的模块之后去实现一个低版本浏览器的polyfill
。
其实与
@babel/plugin-transform-runtime
达到相同的效果还可以直接安装引入@babel/polyfill
,不过相比之下这种方式不被推荐,他存在污染全局作用域,全量引入造成提及过大以及模块之间重复注入等缺点。
此时这几个插件我们已经可以实现将es6+
代码进行编译成为浏览器可以识别的低版本兼容性良好的js
代码了,不过我们还缺少最重要一点。
@babel/preset-react
上面的这些插件处理的都是js
文件,我们也要能够识别并处理jsx
文件。
此时就引入了我们至关重要的@babel/preset-react
。
@babel/preset-react
是一组预设,所谓预设就是内置了一系列babel plugin
去转化jsx
代码成为我们想要的js
代码。
插件安装
接下来让我们来安装这5个插件
yarn add -D @babel/core @babel/preset-env babel-loader @babel/plugin-transform-runtime @babel/preset-react
webpack
中进行配置:
根目录新建 webpack.config.js,目前的目录结构如下:
webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
filename: "index_bundle.js",
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
],
},
};
根目录新建.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
}
根目录新建src文件夹,里面新建index.js
import React from "react";
import ReactDOM from "react-dom";
function App() {
return <div>hello webpack5!</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
根目录新建 public文件夹,里面新建index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webpack5</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
配置html
页面
当前我们所有涉及的都是针对单页面应用的配置,此时我们迫切需要一个html
展示页面。
此时就引入我们的主角 html-webpack-plugin,
html-webpack-plugin
的作用是:当使用webpack
打包时,创建一个html
文件,并把webpack
打包后的静态文件自动插入到这个html
文件当中。
yarn add --dev html-webpack-plugin
我们使用这个文件作为插件的模板文件,同时与index.js入口文件中的ReactDom.reander(...,document.getElementById('root'))
进行呼应,页面中已经创建一个id=root
的div
作为渲染节点。
在webpack.config.js里面使用插件
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
filename: "index_bundle.js",
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
],
};
webpack-dev-server
上边的长篇大论已经能满足一个SPA
单页面应用的构建了,但是我们总不能每次修改代码都需要执行一次打包命令在预览吧。
这样的话也太过于麻烦了,别担心webpack
为我们提供了devServer
配置,支持我们每次更新代码热重载。
yarn add -D webpack-dev-server
然后配置webpack.config.js
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
filename: "index_bundle.js",
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
],
devServer: {
// 当使用 [HTML5 History API] 时,任意的 `404` 响应被替代为 `index.html`
historyApiFallback: true,
open: true, // 自动打开浏览器
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 9000,
},
};
在package.json的scripts
里面里面新增
"scripts": {
"start": "webpack serve" // 启动项目
"build":"webpack" // 执行webpack打包
},
然后控制台 执行 yarn start
,可以看到,一个超简单的react项目已经搭建完成了
clean-webpack-plugin
clean-webpack-plugin是一个清除文件的插件。在每次打包后,磁盘空间会存有打包后的资源,在再次打包的时候,我们需要先把本地已有的打包后的资源清空,来减少它们对磁盘空间的占用。插件clean-webpack-plugin就可以帮我们做这个事情。
安装
yarn add -D clean-webpack-plugin
修改webpack.config.js
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
filename: "index_bundle.js",
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
new CleanWebpackPlugin(),
],
devServer: {
// 当使用 [HTML5 History API] 时,任意的 `404` 响应被替代为 `index.html`
historyApiFallback: true,
open: true, // 自动打开页面
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 9000,
},
};
文件名hash
给打包过后的文件名字加上hash
在webpack.config.js中,filename加上占位符
output: {
path: __dirname + "/dist",
// [contenthash:8] - 本应用打包输出文件级别的更新,导致输出文件名变化
filename: "[name]-[contenthash:8].js",
},
配置路径别名
在webpack.config.js中加入
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
mainFiles: ["index", "main"],
},
此时,webpack.config.js的全部配置为:
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
// [contenthash:8] - 本应用打包输出文件级别的更新,导致输出文件名变化
filename: "[name]-[contenthash:8].js",
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
mainFiles: ["index", "main"],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
new CleanWebpackPlugin(),
],
devServer: {
// 当使用 [HTML5 History API] 时,任意的 `404` 响应被替代为 `index.html`
historyApiFallback: true,
open: true, // 自动打开页面
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 9000,
},
};
配置图片和字体
在webpack.cofig.js module的rule新增
assets
模块是webpack5自带,不用下载
{
test: /\.(png|jpe?g|svg|gif)$/,
type: "asset/inline",
},
{
test: /\.(eot|ttf|woff|woff2)$/,
type: "asset/resource",
generator: {
filename: "fonts/[hash][ext][query]",
},
},
js压缩
webpack5 自带最新的 terser-webpack-plugin
,无需手动安装。
terser-webpack-plugin
默认开启了 parallel: true
配置,并发运行的默认数量: os.cpus().length - 1
,本文配置的 parallel 数量为 4,使用多进程并发运行压缩以提高构建速度。
webpack.prod.js
配置方式如下:
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
]
}
}
目前 webpack.config.js的完整配置为:
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
// [contenthash:8] - 本应用打包输出文件级别的更新,导致输出文件名变化
filename: "[name]-[contenthash:8].js",
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
mainFiles: ["index", "main"],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
{
test: /\.(png|jpe?g|svg|gif)$/,
type: "asset/inline",
},
{
test: /\.(eot|ttf|woff|woff2)$/,
type: "asset/resource",
generator: {
filename: "fonts/[hash][ext][query]",
},
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
new CleanWebpackPlugin(),
],
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
],
},
devServer: {
// 当使用 [HTML5 History API] 时,任意的 `404` 响应被替代为 `index.html`
historyApiFallback: true,
open: true, // 自动打开页面
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 9000,
},
};
css的配置
接下来我们给项目添加一些样式来美化它。
这里其实React
项目有太多有关css
的争吵了,但是无论如何我们是都要在webpack
中针对css
进行处理的。
这里用到的loader
如下:
postcss-loader
css-loader
MiniCssExtractPlugin.loader
我们来一个一个来分析这些loader
的作用的对应的配置:
postcss-loader
PostCSS是什么?或许,你会认为它是预处理器、或者后处理器等等。其实,它什么都不是。它可以理解为一种插件系统。
针对于postcss
其实我这里并不打算深入去讲解,它是babel
一样都是两个庞然大物。拥有自己独立的体系,在这里你需要清楚的是我们使用postcss-loader
处理生成的css
。
第一步首先安装post-css
对应的内容:
yarn add -D postcss-loader postcss
postcss-loader
同时支持直接在loader
中配置规则选项,也支持单独建立文件配置,这里我们选择单独使用文件进行配置:
我们在项目根目录下新建一个postcss.config.js
的文件:
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default',
}),
],
}
这里我们使用到了两个postcss
的插件:
autoprefixer
插件的作用是为我们的css
内容添加浏览器厂商前缀兼容。cssnano
的作用是尽可能小的压缩我们的css
代码。
接下来我们去安装这两个插件:
yarn add -D cssnano autoprefixer@latest
css-loader
css-loader
是解析我们css
文件中的@import/require
语句分析的.
yarn add -D css-loader
MiniCssExtractPlugin.loader
这个插件将 CSS 提取到单独的文件中。它为每个包含CSS
的 JS
文件创建一个 CSS
文件。它支持按需加载 CSS 和 SourceMaps
。
这里需要提一下他和style-loader
的区别,这里我们使用了MiniCssExtractPlugin
代替了style-loader
。
style-loader
会将生成的css
添加到html
的header
标签内形成内敛样式,这显然不是我们想要的。所以这里我们使用MiniCssExtractPlugin.loader
的作用就是拆分生成的css
成为独立的css
文件。
yarn add -D mini-css-extract-plugin
生成css
最终配置文件
接下来我们来生成css
文件的最终配置文件:
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
// [contenthash:8] - 本应用打包输出文件级别的更新,导致输出文件名变化
filename: "[name]-[contenthash:8].js",
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
mainFiles: ["index", "main"],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"postcss-loader",
],
},
{
test: /\.(png|jpe?g|svg|gif)$/,
type: "asset/inline",
},
{
test: /\.(eot|ttf|woff|woff2)$/,
type: "asset/resource",
generator: {
filename: "fonts/[hash][ext][query]",
},
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "assets/[name].css",
}),
],
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
],
},
devServer: {
// 当使用 [HTML5 History API] 时,任意的 `404` 响应被替代为 `index.html`
historyApiFallback: true,
open: true, // 自动打开页面
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 9000,
},
};
配置less
yarn add less less-loader --dev
配置如下 注意1:此处的style-loader、css-loader、less-loader的顺序不能变的,因为webpack在转换时是从后往前转的,即先将.less转成.css,然后在将.css写进style里的。
注意2:当我们引入antd.less时,需要启动less-loader的javascriptEnabled=true
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"postcss-loader",
// 当解析antd.less,必须写成下面格式,否则会报Inline JavaScript is not enabled错误
{ loader: "less-loader", options: { lessOptions: { javascriptEnabled: true } } },
],
},
目前webpack.config.js的全部配置如下
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
// [contenthash:8] - 本应用打包输出文件级别的更新,导致输出文件名变化
filename: "[name]-[contenthash:8].js",
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
mainFiles: ["index", "main"],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
{
test: /\.(sa|sc|le|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"postcss-loader",
// 当解析antd.less,必须写成下面格式,否则会报Inline JavaScript is not enabled错误
{ loader: "less-loader", options: { lessOptions: { javascriptEnabled: true } } },
],
},
{
test: /\.(png|jpe?g|svg|gif)$/,
type: "asset/inline",
},
{
test: /\.(eot|ttf|woff|woff2)$/,
type: "asset/resource",
generator: {
filename: "fonts/[hash][ext][query]",
},
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "assets/[name].css",
}),
],
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
],
},
devServer: {
// 当使用 [HTML5 History API] 时,任意的 `404` 响应被替代为 `index.html`
historyApiFallback: true,
open: true, // 自动打开页面
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 9000,
},
};
css压缩
使用 CssMinimizerWebpackPlugin 压缩 CSS 文件。
和 optimize-css-assets-webpack-plugin 相比,css-minimizer-webpack-plugin 在 source maps 和 assets 中使用查询字符串会更加准确,而且支持缓存和并发模式下运行。
CssMinimizerWebpackPlugin
将在 Webpack 构建期间搜索 CSS 文件,优化、压缩 CSS。
安装:
yarn add -D css-minimizer-webpack-plugin
webpack.prod.js
配置方式如下:
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin({
parallel: 4,
}),
],
}
}
完整webpack.config.js配置如下
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin"); // js压缩
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // css抽离
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); //css压缩
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
// [contenthash:8] - 本应用打包输出文件级别的更新,导致输出文件名变化
filename: "[name]-[contenthash:8].js",
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
mainFiles: ["index", "main"],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: "babel-loader",
},
{
test: /\.(sa|sc|le|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"postcss-loader",
// 当解析antd.less,必须写成下面格式,否则会报Inline JavaScript is not enabled错误
{ loader: "less-loader", options: { lessOptions: { javascriptEnabled: true } } },
{
loader: "resolve-url-loader",
options: {
keepQuery: true,
},
},
{
loader: "sass-loader",
options: {
sourceMap: true,
},
},
],
},
{
test: /\.(png|jpe?g|svg|gif)$/,
type: "asset/inline",
},
{
test: /\.(eot|ttf|woff|woff2)$/,
type: "asset/resource",
generator: {
filename: "fonts/[hash][ext][query]",
},
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "assets/[name].css",
}),
],
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
new CssMinimizerPlugin({
parallel: 4,
}),
],
},
devServer: {
// 当使用 [HTML5 History API] 时,任意的 `404` 响应被替代为 `index.html`
historyApiFallback: true,
open: true, // 自动打开页面
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 9000,
},
};
编译进度条
一般来说,中型项目的首次编译时间为 5-20s,没个进度条等得多着急,通过 progress-bar-webpack-plugin 插件查看编译进度,方便我们掌握编译情况。
安装:
yarn add progress-bar-webpack-plugin --dev
webpack.common.js
配置方式如下:
const chalk = require('chalk')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
module.exports = {
plugins: [
// 进度条
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)`
})
],
}
复制代码
贴心的为进度百分比添加了加粗和绿色高亮态样式。
包含内容、进度条、进度百分比、消耗时间,进度条效果如下:
Eslint
& prettier
prettier
yarn add --dev --exact prettier
安装完成之后我们在项目根目录新建.prettierrc.js
配置文件
我们添加一些基础配置
module.exports = {
printWidth: 120, // 代码宽度建议不超过100字符
tabWidth: 2, // tab缩进2个空格
semi: false, // 末尾分号
singleQuote: true, // 单引号
jsxSingleQuote: true, // jsx中使用单引号
trailingComma: 'es5', // 尾随逗号
arrowParens: 'avoid', // 箭头函数仅在必要时使用()
htmlWhitespaceSensitivity: 'css', // html空格敏感度
}
我们再来添加一份.prettierignore
让prettier
忽略检查一些文件:
//.prettierignore
**/*.min.js
**/*.min.css
.idea/
node_modules/
dist/
build/
ESlint
yarn add eslint --dev
初始化eslint
npx eslint --init
复制代码
eslint
回和我们进行一些列的交互提示,按照提示进行选择我们需要的配置就可以了
当prettier
和eslint
共同工作时,他们可能会冲突。我们需要安装eslint-config-prettier
插件并且覆盖eslint
部分规则。
yarn add -D eslint-config-prettier
安装完成之后,我们稍微修改一下eslint
的配置文件,让冲突时,优先使用prettier
覆盖eslint
规则:
// .eslint.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
// 添加`prettier`拓展 用于和`prettier`冲突时覆盖`eslint`规则
'prettier',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
plugins: ['react'],
rules: {},
}
同时我们来添加.eslintignore
忽略掉一些我们不要检查eslint的ts
目录文件,比如构建的一些脚本文件。
.eslintrc.js
node_modules
public
lint-staged
如果在整个项目上运行 lint
效率会非常底下,更好的做法是只让进入暂存区的文件做代码校验,这会节约很多时间,我们需要使用的工具是 lint-staged
yarn add lint-staged --dev
添加 lint-staged.config.js
配置文件:
module.exports = {
"src/**/*.{js,ts,vue}": [
"eslint --fix --ext .js,.ts,.vue",
"prettier --write",
],
};
这样在命令行执行 npx lint-staged
就能手动在暂存区运行eslint+prettier
做代码风格校验了
husky
为了确保进入 git
仓库的代码都是符合代码规则且拥有统一风格,在代码提交之前,我们还需要强制进行一次代码校验,使用 husky
(哈士奇),能够在我们做代码提交动作(commit)的时候自动运行代码校验命令
yarn add husky --dev
我们安装的是 6.0
版本以上的 husky
,根据文档先在 package.json
中添加一条脚本:
{
"prepare": "husky install"
}
运行 husky
安装脚本:
yarn prepare
运行完此命令会在项目根目录下生成一个 .husky
文件夹
添加一个 git hook
:
npx husky add .husky/pre-commit "npx lint-staged"
复制代码
执行完此命令后,会在 .husky
目录下自动生成一个 pre-commit
文件,但是经过我在公司 windows 电脑上的测试并没有成功生成,所以我们手动添加这个文件
在 .husky
目录下新建 pre-commit
文件,写入一下内容:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
现在每当你执行 commit
操作时,都会自动执行 lint-staged
做规则校验,如果 lint
没有通过,则会 abort
当前的 commit
操作,直到你修复完了所有的 error
才能成功提交
下载各个包的版本信息如下
上面全部经过实测完成,如果有错误,可能是版本迭代导致,下面贴出依赖的包的所有版本
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/plugin-transform-runtime": "^7.15.0",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"autoprefixer": "^10.3.6",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.3.0",
"css-minimizer-webpack-plugin": "^3.0.2",
"cssnano": "^5.0.8",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-react": "^7.26.0",
"gh-pages": "^3.2.3",
"html-webpack-plugin": "^5.3.2",
"husky": "^7.0.0",
"less": "^4.1.1",
"less-loader": "^10.0.1",
"lint-staged": "^11.1.2",
"mini-css-extract-plugin": "^2.3.0",
"node-sass": "^6.0.1",
"postcss": "^8.3.8",
"postcss-loader": "^6.1.1",
"prettier": "2.4.1",
"progress-bar-webpack-plugin": "^2.1.0",
"resolve-url-loader": "^4.0.0",
"sass": "^1.42.1",
"sass-loader": "^12.1.0",
"webpack": "^5.54.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.3.0"
},
"dependencies": {
"husky": "^7.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
webpack环境拆分
development(开发环境) 和 production(生产环境) 这两个环境下的构建目标存在着巨大差异。为代码清晰简明,为每个环境编写彼此独立的 webpack 配置
在根目录新建config文件夹,然后新建三个webpack配置文件夹
webpack-merge
使用 webpack-marge 合并通用配置和特定环境配置。
安装 webpack-merge:
yarn add webpack-merge --dev
然后根据我们现在配好的webpack.config.js 的内容拆分到config文件夹的三个配置文件中.
webpack.common.js
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // css抽离
const chalk = require('chalk')
const ProgressBarPlugin = require('progress-bar-webpack-plugin') // 编译进度条
module.exports = {
resolve: {
// 配置路径别名
alias: {
'@': path.resolve(__dirname, './src'),
},
mainFiles: ['index', 'main'],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: 'babel-loader',
},
{
test: /\.(le|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
'postcss-loader',
// 当解析antd.less,必须写成下面格式,否则会报Inline JavaScript is not enabled错误
{ loader: 'less-loader', options: { lessOptions: { javascriptEnabled: true } } },
],
},
{
test: /\.(png|jpe?g|svg|gif)$/,
type: 'asset/inline',
},
{
test: /\.(eot|ttf|woff|woff2)$/,
type: 'asset/resource',
generator: {
filename: 'fonts/[hash][ext][query]',
},
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../public/index.html'),
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'assets/[name].css',
}),
// 进度条
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)`,
}),
],
}
webpaack.dev.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
// 开发工具,开启 source map,编译调试
devtool: 'eval-cheap-module-source-map',
cache: {
type: 'filesystem', // 使用文件缓存
},
entry: './src/index.js',
devServer: {
historyApiFallback: true,
open: true, // 自动打开页面
// 默认为true
hot: true,
// 是否开启代码压缩
compress: true,
// 启动的端口
port: 8888,
},
})
webpack.prd.js
const TerserPlugin = require('terser-webpack-plugin') // js压缩
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') // css压缩
const { merge } = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'production',
entry: './src/index.js',
output: {
path: __dirname + '/../dist',
// [contenthash:8] - 本应用打包输出文件级别的更新,导致输出文件名变化
filename: '[name]-[contenthash:8].js',
// 编译前清除目录
clean: true,
},
//terser-webpack-plugin 默认开启了 parallel: true 配置,并发运行的默认数量: os.cpus().length - 1 ,
// 配置的 parallel 数量为 4,使用多进程并发运行压缩以提高构建速度。
optimization: {
// 通过配置 optimization.runtimeChunk = true,为运行时代码创建一个额外的 chunk,减少 entry chunk 体积,提高性能。
// runtimeChunk: true,
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
new CssMinimizerPlugin({
parallel: 4,
}),
],
},
})
最后修改package.json 里面的webpack的运行和打包命令
"scripts": {
"start": "webpack serve --config config/webpack.dev.js",
"build": "webpack --config config/webpack.prd.js",
},
源码
最后
自己花了两天时间,参考各类文章总结了这篇文章,如果觉得对你有帮助,就点个赞呗