webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

1.入门

npm i webpack

npm i webpack-cli

npx webpack “./src/main.js” –mode development

基本的webpack.config.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const path = require('path');

module.exports = {
//入口
entry: "./src/index.js",
//打包的输出
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
//插件
plugins:[

],
//开发模式 和 生产模式
mode: "development",
//模块
module: {
rules: [
]
},
};
1
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
27
28
{
"name": "shopping",
"version": "1.0.0",
"description": "shopping",
"main": "./src/index.js",
"scripts": {
"package": "webpack",
"dev": "webpack server",
"built": "webpack"
},
"author": "richu",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"babel-loader": "^9.2.1",
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.2",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.6.3",
"style-loader": "^4.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.96.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.1.0"
}
}

2.资源的处理

1.处理css资源

npm i css-loader style-loader -D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const path = require('path');

module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
plugins:[
{
test: /\.css$/, //处理css资源
use: [
'style-loader', //将js中css通过创建style标签添加html文件中生效
'css-loader' //将css资源编译成commonJS的模块到JS中
],
},
],
mode: "development",
module: {
rules: [
]
},
};

2.处理less资源

npm i less less-loader –save-dev

1
2
3
4
5
6
7
8
9
module: {
rules: [
{
{
test: /\.less$/,
use: ['style-loader', 'css-loader','less-loader'],
}
]
},

3.处理sass资源

npm i sass sass-loader -D

1
2
3
4
{
test: /\.s[ac]ss$/,
use: ['style-loader', 'css-loader','sass-loader'],
}

4.处理图片资源

将图片资源转为base64

1
2
3
4
5
6
7
8
9
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type:"asset",
parser:{
dataUrlCondition:{
maxSize:10*1024
}
}
}

5.修改打包输出的文件目录

1.修改js的输出目录

1
2
3
4
output: {
path: path.resolve(__dirname, "dist"),
filename: "js/bundle.js",
},

2.修改图片的输出目录

1
2
3
4
5
6
7
8
9
10
11
12
13
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type:"asset",
parser:{
dataUrlCondition:{
maxSize:10*1024
}
},
generator:{
//输出图片名字
filename:"images/[hash][ext]"
}
}

6.自动清空上次打包的内容

1
2
3
4
5
6
output: {
path: path.resolve(__dirname, "dist"),
filename: "js/bundle.js",
//将path对应的目录整个清空
clean:true
},

7.处理Html资源

npm i –save-dev html-webpack-plugin

1
2
3
4
new HtmlWebpackPlugin({
filename:'index.html',
template:'./src/index.html'
}),

8.搭建开发服务器

npm i webpack-dev-server -D

1
2
3
4
5
6
7
devServer: {
static: {
directory: path.join(__dirname, 'src'), //静态文件目录
},
compress: true, //压缩
port: 9000, //端口
},

3.eslint

1.基本配置

1.parserOptions 解析选项

2.rules 具体规则

  • off 或0 关闭规则
  • warn 或 1 使用警告级别的错误
  • error 或 2 使用错误级别的错误

3.extends继承

2.入门

npm i eslint-webpack-plugin –save-dev

npm i eslint –save-dev

  • 创建.eslintrc.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
extends: [
"eslint:recommended", //继承eslint
],
env:{
node:true, //启用全局node
browser:true, //启用浏览器全局变量
},
parserOptions: {
ecmaVersion:6, //es6
sourceType:"module", //es module
},
plugins: [
],
rules: {
"no-var":2, //使用var的错误级别为2
},
}
  • 创建.eslintignore文件
1
dist  //忽略dist文件夹

4.babel

将es6转为向后兼容的js代码

1
npm i -D babel-loader @babel/core @babel/preset-env
1
2
3
4
5
6
7
8
9
10
{
test: /\.js$/,
exclude: /node_modules/, //不需要加载node_modules文件内的文件
use: [{
loader: 'babel-loader',
options:{
presets:["@babel/preset-env"]
}
}],
},

或者将OPtions写到babel.config.js

  • 创建babel.config.js文件
1
2
3
module.exports = () => ({
presets: [require("@babel/preset-env")], //只能预设
});

生产模式的配置

优化代码的性能

优化代码的打包速度

mode: “production”,

5.优化

1.将css打包成单独文件

npm i –save-dev mini-css-extract-plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader','less-loader'],
},
{
test: /\.s[ac]ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader','sass-loader'],
},

new MiniCssExtractPlugin()

指定输出文件目录

1
2
3
new MiniCssExtractPlugin({
filename:"css/main.css"
})

2.css兼容性处理

npm i postcss-loader postcss postcss-preset-env -D

1
2
3
4
5
6
7
8
9
10
11
12
13
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader',{
loader:"postcss-loader",
options:{
postcssOptions:{
plugins:[
"postcss-preset-env"
]
}
}
}],
},

在package.json中加入

1
2
3
"browserslist":[
"ie >= 8"
]

3.封装样式Loader函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function GetStyleLoader(pre) {
return [
MiniCssExtractPlugin.loader, 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}
1
2
3
4
5
6
7
8
9
10
11
12
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},

4.css压缩

npm i css-minimizer-webpack-plugin –save-dev

1
new CssMinimizerPlugin()

5.sourceMap源代码映射

当我们在开发过程中,可能会出现错误,但是当我们在浏览器调试时会发现,报错的位置是打包后的位置,因此我们需要sourceMap定位源代码错误的位置,更方便开发

1
2
devtool: "eval-cheap-module-source-map"   ///开发环境使用
devtool: "source-map" ///生成环境使用

6.提升打包速度

1
2
3
4
5
6
7
8
devServer: {
static: {
directory: path.join(__dirname, 'src'),
},
compress: true,
port: 9000,
hot:true
},

7.OneOf

每个文件只能被其中一个Loader(配置)处理

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
module: {
rules: [
{
oneOf:[
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ["@babel/preset-env"]
}
}
],
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
},
generator: {
//输出图片名字
filename: "images/[hash][ext]"
}
}
]
}
]
},

8.Include/Exclude

Exclude 排除,除了指定的都处理

Include 制定 处理制定的

1
2
3
4
5
6
7
8
9
10
11
12
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ["@babel/preset-env"]
}
}
],
},

9.Bable和Eslint的缓存

Bable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true, //开启缓存
cacheCompression: false //关闭缓存文件压缩
/* presets: ["@babel/preset-env"] */
}
}
],
},

Eslint

1
2
3
4
5
6
7
8
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "src"),
exclude:/node_modules/,
cache:true,
cacheLocation:path.join(__dirname,'../node_modules/.cache/eslintcache')
}
),

10.多进程打包

npm i thread-loader -D

1
2
const os = require('os')
const threads = os.cpus().length
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader:'thread-loader',
options: {
works: threads,
}
},
{
loader: 'babel-loader',
options: {
cacheDirectory: true, //开启缓存
cacheCompression: false //关闭缓存文件压缩
/* presets: ["@babel/preset-env"] */
}
}
],
},
1
2
3
4
5
6
7
8
9
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "src"),
exclude:"node_modules",
cache:true,
cacheLocation:path.join(__dirname,'../node_modules/.cache/eslintcache'),
threads,
}
),

压缩js

1
2
3
4
const TerserWebpackPlugin = require('terser-webpack-plugin')
new TerserWebpackPlugin({
parallel:threads
})

更改压缩插件的位置

1
2
3
4
5
6
7
8
9
10
optimization:{
//压缩的操作
minimizer:[
new CssMinimizerPlugin(), //压缩css
new TerserWebpackPlugin({ //压缩js
parallel:threads
})
]
},
mode: "development",

11.减少图片体积

npm i image-minimizer-webpack-plugin imagemin -D

无损压缩

npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo –save-dev

1
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
27
28
29
30
31
32
33
34
35
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");

module.exports = {
optimization: {
minimizer: [
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
],
},
};

12.进一步解决兼容性问题

coreJs npm i core.js

babel.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = function(api) {
api.cache(true);
return {
presets: [
[
"@babel/preset-env", // 预设名称
{
useBuiltIns: 'usage', // 选项
corejs: 3 // 选项
}
]
]
};
};

13.PWD

npm install workbox-webpack-plugin –save-dev

1
2
3
4
5
6
7
const WorkboxPlugin = require('workbox-webpack-plugin');
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),

注册 Service Worker

npx SW registered

启动服务

1
2
npm i server -g
npx server dist

6.CodeSplit

1.CodeSplit

添加多入口

1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const path = require('path');

module.exports = {
entry: './src/index.js',
mode: 'development',
entry: {
index: './src/index.js', //主入口
another: './src/another-module.js', //其他js文件
},
output: {
filename: 'main.js', //主入口打包的输出目录
filename: '[name].bundle.js', //其他js文件打包的输出目录
path: path.resolve(__dirname, 'dist'),
},
};

动态导入(Dynamic Imports)

通过模块的内联函数调用来分离代码。例如,使用import()函数:

1
2
3
import(/* webpackChunkName: 'subPageA' */'./subPageA').then(function(res){
console.log('import()', res)
});

防止依赖重复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const path = require('path');

module.exports = {
mode: 'development',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: 'lodash',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
runtimeChunk: 'single',
},
};

2.SplitChunksPlugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const path = require('path');

module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};

3.preload、prefetch

npm i –save-dev @vue/preload-webpack-plugin

1
2
3
4
5
6
7
8
9
10
const PreLoadWebpackPlugin = require('@vue/preload-webpack-plugin');

new PreLoadWebpackPlugin({
rel:"preload",
as:"script",
}),
/*new PreLoadWebpackPlugin({
rel:"prefetch",
as:"script",
}),*/

7.typeScript

npm install –save-dev typescript ts-loader

tsconfig.js

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "es5",
"jsx": "react",
"allowJs": true,
"moduleResolution": "node",
}
}

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const path = require('path');

module.exports = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};

8.react-cli

1.webpack.dev.js

基本配置

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')

function GetStyleLoader(pre) {
return [
MiniCssExtractPlugin.loader, 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}

module.exports = {
entry: "./src/main.js",
output: {
path: undefined,
filename: "js/[name].bundle.js",
chunkFilename: "static/js/[name].chunk.js",
assetModuleFilename: "static/media/[hash:10][ext][query]"
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false
}
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
}
},
{
test: /\.(woff2|ttf)/,
type: "asset/resource",

}
]
},
plugins: [
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
}
),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
],
mode: "development",
devtool: "cheap-module-source-map",
optimization: {
splitChunks: {
chunks: "all",
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
}
},
devServer:{
host:"localhost",
port:3000,
open:true,
hot:true,
}
}

.eslintrc.js

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
extends: [
"react-app", //react
],
parserOptions: {
babelOptions:{
presets:[
["react-app",false],
"babel-preset-react-app/prod"
]
}
},
}

babel.config.js

1
2
3
module.exports = {
presets: ['react-app'],
};

npm i eslint-webpack-plugin html-webpack-plugin css-loader postcss-loader style-loader less-loader postcss-preset-env sass stylus-loader sass-loader -D

npm i -D babel-loader @babel/core babel-preset-react-app eslint-config-react-app -D

npm i webpack-dev-server webpack webpack-cli -D

npm i react react-dom

2.解决启动后报错的问题

1
Error: [BABEL] D:\MyTest\cli\src\index.js: Using `babel-preset-react-app` requires that you specify `NODE_ENV` or `BABEL_ENV` environment variables. Valid values are "development", "test", and "production". Instead, received: undefined. (While processing: "D:\\MyTest\\cli\\node_modules\\babel-preset-react-app\\index.js")

npm i –save-dev cross-env

设置启动脚本为 “serve”: “cross-env NODE_ENV=production webpack serve –config ./config/webpack.dev.js”,

1
2
3
4
//在webpack.config.js中配置
resolve: {
extensions: ['.js','.jsx','.json'],
},

3.更新局部代码

npm i -D @pmmmwh/react-refresh-webpack-plugin react-refresh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
{
test: /\.jsx?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
plugins:[
'react-refresh/babel'
]
}
},


new ReactRefreshPlugin()

4.解决路由报错问题

在我们使用react-router-dom的时候会出现这样的问题

Cannot GET /about

1
2
3
4
5
6
7
devServer:{
host:"localhost",
port:3000,
open:true,
hot:true,
historyApiFallback:true //解决前端路由刷新404的问题
},

5.生产模式

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');

function GetStyleLoader(pre) {
return [
MiniCssExtractPlugin.loader, 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}

module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname,"../dist"),
filename: "js/[name].[contenthash:10].js",
chunkFilename: "static/js/[name].chunk.js",
assetModuleFilename : "static/media/[hash:10][ext][query]",
clean:true,
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
}
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
}
},
{
test: /\.(woff2|ttf)/,
type: "asset/resource",

}
]
},
plugins: [
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
}
),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
],
mode: "production",
devtool: "source-map",
optimization: {
splitChunks: {
chunks: "all",
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
},
minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
test: /\.js(\?.*)?$/i,
})]
},
resolve: {
extensions: ['.js','.jsx','.json'],
},
}

npm i css-minimizer-webpack-plugin mini-css-extract-plugin terser-webpack-plugin -D

6.图标的解析

npm i copy-webpack-plugin -D

1
2
3
4
5
6
7
8
9
10
11
12
13
const CopyWebpackPlugin = require('copy-webpack-plugin');
new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../public'),
to: path.resolve(__dirname,'../dist'),
globOptions:{
//忽略这个文件
ignore:["**/index.html"]
}
}
]
}),

7.合并配置

webpack.config.js

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const isproduction = process.env.NODE_ENV === "production"

function GetStyleLoader(pre) {
return [
isproduction?MiniCssExtractPlugin.loader:'style-loader', 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}

module.exports = {
entry: "./src/index.js",
output: {
path: isproduction?path.resolve(__dirname,"../dist"):undefined,
filename: isproduction?"js/[name].[contenthash:10].js":"js/[name].bundle.js",
chunkFilename: isproduction?"static/js/[name].chunk.js":"static/js/[name].chunk.js",
assetModuleFilename : "static/media/[hash:10][ext][query]",
clean:true
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
plugins:[
!isproduction && 'react-refresh/babel'
].filter(Boolean)
}
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
}
},
{
test: /\.(woff2|ttf)/,
type: "asset/resource",

}
]
},
plugins: [
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
}
),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
!isproduction && new ReactRefreshPlugin(),
isproduction && new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
isproduction && new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../public'),
to: path.resolve(__dirname,'../dist'),
globOptions:{
//忽略这个文件
ignore:["**/index.html"]
}
}
]
}),
].filter(Boolean),
mode: isproduction?"production": "development",
devtool: isproduction?"source-map":"cheap-module-source-map",
optimization: {
splitChunks: {
chunks: "all",
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
},
minimize:isproduction,
minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
test: /\.js(\?.*)?$/i,
})]
},
devServer:{
host:"localhost",
port:3000,
open:true,
hot:true,
historyApiFallback:true //解决前端路由刷新404的问题
},
resolve: {
extensions: ['.js','.jsx','.json'],
},
}

8.优化

npm i antd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//webpack.config.js
function GetStyleLoader(pre) {
return [
isproduction?MiniCssExtractPlugin.loader:'style-loader', 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre && {
loader:pre,
options:pre === "less-loader"?{
//antdesign自定义主题色
lessOptions:{
modifyVars:{"@primary-color":"#1DA57A"},
javascriptEnabled:true
}
}:{}
}
].filter(Boolean)
}

分开打包cacheGroups

1
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
27
28
29
optimization: {
splitChunks: {
chunks: "all",
cacheGroups:{
react:{
test:/[\\/]node_modules[\\/]react(.*)?[\\/]/,
name:"chunk-react",
priority:40,
},
antd:{
test:/[\\/]node_modules[\\/]antd[\\/]/,
name:"chunk-antd",
priority:30,
},
libs:{
test:/[\\/]node_modules[\\/]/,
name:"chunk-libs",
priority:20,
}
}
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
},
minimize:isproduction,
minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
test: /\.js(\?.*)?$/i,
})]
},
1
2
3
4
module.exports = {
...
performance:false //关闭性能分析,提升打包效率
}

9.VueCli

1.vue-loader

npm install -D vue-loader vue-template-compiler

npm i eslint-webpack-plugin html-webpack-plugin vue-style-loader css-loader postcss-loader sass less-loader sass-loader stylus-loader babel-loader postcss-preset-env webpack webpack-cli webpack-dev-server cross-env -D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
module: {
rules: [
// ... 其它规则
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}

.eslintrc.js

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
root:true,
env:{
node:true
},
extends: [
"plugin:vue/vue3-essential","eslint:recommended"
],
parserOptions:{
parser:"@babel/eslint-parser",
}
}

babel.config.js

1
2
3
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
};

npm i @babel/eslint-parser eslint-plugin-vue -D

2.给vue赋值全局变量

1
2
3
4
5
6
7
8
9
10
const {DefinePlugin} = require("webpack")

plugins: [
...
new DefinePlugin({
__VUE_OPTIONS_API__:true,
__VUE_PROD_DEVTOOLS__:false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
})
],

3.整体开发模式下vue的配置

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const {DefinePlugin} = require("webpack")

function GetStyleLoader(pre) {
return [
'vue-style-loader', 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}

module.exports = {
entry: "./src/index.js",
output: {
path: undefined,
filename: "static/js/[name].bundle.js",
chunkFilename: "static/js/[name].chunk.js",
assetModuleFilename : "static/media/[hash:10][ext][query]"
},
module: {
rules: [
{
test: /\.js?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
}
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
}
},
{
test: /\.(woff2|ttf)/,
type: "asset/resource",

}
]
},
plugins: [
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
}
),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
new VueLoaderPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__:true,
__VUE_PROD_DEVTOOLS__:false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
})
],
mode: "development",
devtool: "cheap-module-source-map",
optimization: {
splitChunks: {
chunks: "all",
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
}
},
devServer:{
host:"localhost",
port:3000,
open:true,
hot:true,
historyApiFallback:true //解决前端路由刷新404的问题
},
resolve: {
extensions: ['.js','.vue','.json'],
},
}

4.生产模式配置

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const path = require("path")
const {DefinePlugin} = require("webpack")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader')
function GetStyleLoader(pre) {
return [
MiniCssExtractPlugin.loader, 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}

module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname,"../dist"),
filename: "static/js/[name].[contenthash:10].js",
chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
assetModuleFilename : "static/media/[hash:10][ext][query]",
clean:true,
},
module: {
rules: [
{
test: /\.js?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
}
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
}
},
{
test: /\.(woff2|ttf)/,
type: "asset/resource",

}
]
},
plugins: [
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
}
),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../public'),
to: path.resolve(__dirname,'../dist'),
globOptions:{
//忽略这个文件
ignore:["**/index.html"]
}
}
]
}),
new VueLoaderPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__:true,
__VUE_PROD_DEVTOOLS__:false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
})
],
mode: "production",
devtool: "source-map",
optimization: {
splitChunks: {
chunks: "all",
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
},
minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
test: /\.js(\?.*)?$/i,
})]
},
resolve: {
extensions: ['.js','.vue','.json'],
},
}

npm i mini-css-extract-plugin css-minimizer-webpack-plugin terser-webpack-plugin copy-webpack-plugin html-webpack-plugin -D

5.合并配置

webpack.config.js

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
const path = require("path")
const {DefinePlugin} = require("webpack")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader')

const isproduction = process.env.NODE_ENV === "production"

function GetStyleLoader(pre) {
return [
isproduction ? MiniCssExtractPlugin.loader:"vue-style-loader", 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}

module.exports = {
entry: "./src/index.js",
output: {
path: isproduction?path.resolve(__dirname,"../dist"):undefined,
filename: isproduction?"static/js/[name].[contenthash:10].js":"static/js/[name].js",
chunkFilename: isproduction?"static/js/[name].[contenthash:10].chunk.js":"static/js/[name].chunk.js",
assetModuleFilename : "static/media/[hash:10][ext][query]",
clean:true,
},
module: {
rules: [
{
test: /\.js?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
}
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
}
},
{
test: /\.(woff2|ttf)/,
type: "asset/resource",

}
]
},
plugins: [
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
}
),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
isproduction && new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
isproduction && new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../public'),
to: path.resolve(__dirname,'../dist'),
globOptions:{
//忽略这个文件
ignore:["**/index.html"]
}
}
].filter(Boolean)
}),
new VueLoaderPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__:true,
__VUE_PROD_DEVTOOLS__:false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
})
],
mode: isproduction ? "production":"development",
devtool: isproduction ?"source-map":"cheap-module-source-map",
optimization: {
splitChunks: {
chunks: "all",
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
},
minimize:isproduction,
minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
test: /\.js(\?.*)?$/i,
})]
},
devServer:{
host:"localhost",
port:3000,
open:true,
hot:true,
historyApiFallback:true //解决前端路由刷新404的问题
},
resolve: {
extensions: ['.js','.vue','.json'],
},
}

6.element-plus的使用

npm i element-plus

1
2
3
4
5
6
7
8
9
10
11
12
//全部引入
import { createApp } from 'vue'
import App from './App'
import router from './router';
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

const Vue = createApp(App)
Vue.use(router)
Vue.use(ElementPlus)

Vue.mount(document.getElementById("app"));

按需导入

npm install -D unplugin-vue-components unplugin-auto-import

npm i unplugin-vue-components@0.25.2 -D npm install unplugin-auto-import@0.16.1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
// ...
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}

按需导入的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div class="about">AboutTitle
<el-button type="primary">click me</el-button>
</div>
</template>

<script>
import {ElButton} from 'element-plus'
export default{
name:"About",
componenets:{
ElButton
}
}
</script>

<style>
.about{
color: rgb(188, 14, 72);
}
</style>

7.使用路径别名

1
2
3
4
5
6
resolve: {
extensions: ['.js','.vue','.json'],
alias:{
"@":path.resolve(__dirname,"../src")
}
},

8.优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
splitChunks: {
chunks: "all",
cacheGroups:{
vue:{
test:/[\\/]node_modules[\\/]vue(.*)?[\\/]/,
name:"chunk-vue",
priority:40,
},
elementPlus:{
test:/[\\/]node_modules[\\/]element-plus[\\/]/,
name:"chunk-elementPlus",
priority:30,
},
libs:{
test:/[\\/]node_modules[\\/]/,
name:"chunk-libs",
priority:20,
}
}
},
1
2
3
4
module.exports = {
...
performance:false //关闭性能分析,提升打包效率
}

最后的完整配置

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
const path = require("path")
const {DefinePlugin} = require("webpack")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

const isproduction = process.env.NODE_ENV === "production"

function GetStyleLoader(pre) {
return [
isproduction ? MiniCssExtractPlugin.loader:"vue-style-loader", 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
pre
].filter(Boolean)
}

module.exports = {
entry: "./src/main.js",
output: {
path: isproduction?path.resolve(__dirname,"../dist"):undefined,
filename: isproduction?"static/js/[name].[contenthash:10].js":"static/js/[name].js",
chunkFilename: isproduction?"static/js/[name].[contenthash:10].chunk.js":"static/js/[name].chunk.js",
assetModuleFilename : "static/media/[hash:10][ext][query]",
clean:true,
},
module: {
rules: [
{
test: /\.js?$/,
include: path.resolve(__dirname, "../src"),
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
}
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: GetStyleLoader(),
},
{
test: /\.less$/,
use: GetStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: GetStyleLoader('sass-loader')
},
{
test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
}
},
{
test: /\.(woff2|ttf)/,
type: "asset/resource",

}
]
},
plugins: [
new ESLintWebpackPlugin(
{
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
}
),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
isproduction && new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
isproduction && new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../public'),
to: path.resolve(__dirname,'../dist'),
globOptions:{
//忽略这个文件
ignore:["**/index.html"]
}
}
].filter(Boolean)
}),
new VueLoaderPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__:true,
__VUE_PROD_DEVTOOLS__:false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
}),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
mode: isproduction ? "production":"development",
devtool: isproduction ?"source-map":"cheap-module-source-map",
optimization: {
splitChunks: {
chunks: "all",
cacheGroups:{
vue:{
test:/[\\/]node_modules[\\/]vue(.*)?[\\/]/,
name:"chunk-vue",
priority:40,
},
elementPlus:{
test:/[\\/]node_modules[\\/]element-plus[\\/]/,
name:"chunk-elementPlus",
priority:30,
},
libs:{
test:/[\\/]node_modules[\\/]/,
name:"chunk-libs",
priority:20,
}
}
},
runtimeChunk:{
name: entrypoint => `runtime~${entrypoint.name}.js`,
},
minimize:isproduction,
minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
test: /\.js(\?.*)?$/i,
})]
},
devServer:{
host:"localhost",
port:3000,
open:true,
hot:true,
historyApiFallback:true //解决前端路由刷新404的问题
},
resolve: {
extensions: ['.js','.vue','.json'],
alias:{
"@":path.resolve(__dirname,"../src")
}
},
performance:false
}

10.原理分析

1.构建Loader

npm i html-webpack-plugin webpack webpack-cli -D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
entry:"./src/main.js",
output:{
path:path.resolve(__dirname,"./src"),
filename:"js/[name].js",
clean:true
},
module:{
rules:[

]
},
plugins:[
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "./public/index.html")
}),
],
mode:"development"
}

定义loader文件

1
2
3
4
5
//loaders/test-loaders.js
module.exports = function (content){
console.log(content)
return content
}

在rules中配置

1
2
3
4
5
6
7
8
9
//webpack.config.js
module:{
rules:[
{
test: /\.js$/,
loader:'./loaders/test-loader.js'
}
]
},

输出结果

let arr = [1,2,3,4] ===>该文件为mian.js中的文件

1
2
3
4
5
6
7
8
9
/*
// content 文件管理的内容
// map sourceMap
// meta 别的loader传递的数据
*/
module.exports = function (content,map,meta){
console.log(content,map,meta)
return content
}

2.同步loader

1
2
3
4
5
6
7
8
9
module.exports = function(content,map,meta){
/*
1 代表是否有错误
2 content 处理的内容
3 map 继续传递sourcemap
4 meta 给下一个loader传递参数
*/
this.callback(null,content,map,meta)
}

3.异步loader

1
2
3
4
5
6
7
8
module.exports = function(content,map,meta){
const callback = this.async();

setTimeout(()=>{
console.log("test2")
callback(null,content,map,meta)
},1000)
}

4.raw loader

1
2
3
4
5
6
7
8
9
//<Buffer 6c 65 74 20 61 72 72 20 3d 20 5b 31 2c 32 2c 33 2c 34 5d 0d 0a>'
//接收到的是Buffer数据内容

module.exports = function(content){
console.log(content)
return content
}

module.exports.raw = true;

5.pitch loader

优先执行pitch内的方法

1
2
3
4
5
6
7
module.exports = function(content){
return content
}

module.exports.pitch = function(){
console.log("pitch")
}

6.自定义loader

清除所有的console.log()语句

1
2
3
module.exports = function(content){
return content.replace(/console\.log\(.*\);?/g,"")
}

schema.json

1
2
3
4
5
6
7
8
9
{
"type":"object",
"properties":{
"author":{
"type":"string"
}
},
"additionalProperties":false //不能新增字段
}

banner-loader.js

1
2
3
4
5
6
7
8
9
10
const schema = require("./schema.json")
module.exports = function(conten){
const options = this.getOptions(schema)
const prefix = `
/*
Author:${options.author}
*/
`
return prefix + conten
}

webpack.config.js

1
2
3
4
5
6
7
{
test: /\.js$/,
loader:"./loaders/banner-loader.js",
options:{
"author":"RC"
}
},

7.file-loader

file-loader.js

1
2
3
4
5
6
7
8
9
10
11
12
13
const loaderUtils = require("loader-utils")

module.exports = function(content){
let interpolateName = loaderUtils.interpolateName(this,"[hash].[ext][query]",{
content:content
})
interpolateName =`images/${interpolateName}`
this.emitFile(interpolateName,content)

return `module.exports = "${interpolateName}"`
}

module.exports.raw = true

webpack.config.js

1
2
3
4
5
{
test: /\.(png|jpe?g|gif)$/,
loader: "./loaders/file-loader.js",
type:"javascript/auto"
},

8.style.lodaer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports.pitch = function(remainingRequest){
const path = remainingRequest.split('!').map(absolutePath => {
return this.utils.contextify(this.context,absolutePath)
}).join('!')

const script =`
import style from "!!${path}"
const style1 = document.createElement('style')
style1.innerHTML = style
document.head.appendChild(style1)
`
//终止后面loader的执行
return script
}

11.plugin

1.自定义plugin

1
2
3
4
5
6
7
8
9
class TestPlugin{
constructor(){
console.log("this is constructor")
}
apply(compile){
console.log("compile")
}
}
module.exports = TestPlugin

2.hooks的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apply(compile){
console.log("compile")

compile.hooks.environment.tap("TestPlugin",()=>{
console.log(1)
})
compile.hooks.emit.tap("TestPlugin",()=>{
console.log(2)
})
compile.hooks.emit.tapAsync("TestPlugin",()=>{
setTimeout(()=>{
console.log(3)
},1000)
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class TestPlugin {
constructor() {
console.log("this is constructor")
}
apply(compiler) {
console.log("compile")

compiler.hooks.environment.tap("TestPlugin", () => {
console.log(1)
})
compiler.hooks.emit.tap("TestPlugin", (compilation) => {
console.log(2)

})
compiler.hooks.emit.tapAsync("TestPlugin", (compilation,callback) => {
compilation.hooks.seal.tap("TestPlugin", () => {
console.log("Test")
})
setTimeout(() => {
console.log(3)
callback()
}, 1000)
})
}
}

3.在文件前添加注释的plugin

1
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
27
28
29
30
31
32
33
34
35
36
37
class BannerPlugin{
apply(compiler){
compiler.hooks.emit.tap("BannerPlugin",(compilation)=>{
//过滤图片资源,只保留js和css资源
const extension = ['js','css']
//过滤资源
const assets = Object.keys(compilation.assets).filter((path)=>{
let splitted = path.split('.')
const ext = splitted[splitted.length-1]
return extension.includes(ext)
})
//需要添加的注释前缀
let prefix = `/*
Author:"RC"
*/`
assets.forEach(asset=>{
//获取原来的内容
let source = compilation.assets[asset].source()
//更改文件内容
let content = prefix+=source
compilation.assets[asset] = {
//source就是文件的具体内容
source(){
return content
},
//资源大小
size(){
return content.length
}
}

})
})
}
}

module.exports = BannerPlugin

传递参数

1
2
3
4
5
6
new BannerPlugin({
"name":"wsRC"
})
constructor(options = {}){
this.options = options
}

4.clean-plugins

清空输出目录的资源

1
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
27
28
class CleanPlugin{

apply(compiler){
const outputPath = compiler.options.output.path
const fs = compiler.outputFileSystem
compiler.hooks.emit.tap("CleanPlugin",(compilation)=>{
this.removeFiles(fs,outputPath)
})
}
//删除文件
removeFiles(fs,output){
const files = fs.readdirSync(output)
files.forEach(file => {
let filepath = `${output}/${file}`
const fileStat = fs.statSync(filepath)
if (fileStat.isDirectory()){
//是文件夹
this.removeFiles(fs,filepath)
}else{
//是文件
fs.unlinkSync(filepath)
}
});

}
}

module.exports = CleanPlugin

5.AnalyzeWebpackPlugin

分析包的大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class AnalyzePlugin{
apply(compiler){
compiler.hooks.emit.tap("AnalyzePlugin",(compilation)=>{
const assets = Object.entries(compilation.assets)
let content = `| 名称 | 大小 |
| --- | --- |`
assets.forEach(([filename,file])=>{
content+=`\n| ${filename} | ${Math.ceil(file.size() / 1024)}kb |`
})

compilation.assets["analyze.md"] = {
source(){
return content
},
size(){
return content.length
}
}
})
}
}

module.exports = AnalyzePlugin

6.inlinePlugin

将runtime文件内联到html文件

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const HtmlWebpackPlugin = require("safe-require")("html-webpack-plugin")

class InlinePlugin{
apply(compiler){
compiler.hooks.compilation.tap("InlinePlugin",(compilation)=>{
const hooks = HtmlWebpackPlugin.getHooks(compilation)

/*
{
tagName: 'script',
voidTag: false,
meta: { plugin: 'html-webpack-plugin' },
attributes: { defer: true, src: 'js/main.js' }
}
*/
hooks.alterAssetTagGroups.tap(
"InlinePlugin",(assets)=>{
assets.headTags = this.getInlineChunk(assets.headTags,compilation.assets)
assets.bodyTags = this.getInlineChunk(assets.bodyTags,compilation.assets)
}
)
})

}
getInlineChunk(tags,assets){
return tags.map((tag)=>{
if (tag.tagName != "script") return tag
const filepath = tag.attributes.src
if (!filepath) return tag
if (!/runtime(.*)\.js$/g.test(filepath)) return tag

return {
tagName: 'script',
innerHTML: assets[filepath].source(),
closeTag:true
}
})
}
}

module.exports = InlinePlugin