Vue 项目打包

vue3 + vue-router + element-plus

开发模式配置

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
// webpack.dev.js
const path = require("path");
const {DefinePlugin} = require('webpack')
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { VueLoaderPlugin } = require('vue-loader')

// 处理样式loader函数
const getStyleLoaders = (preProcessor) => {
return [
"vue-style-loader",
"css-loader",
{
// 处理css兼容性
// 配合package.json中browserslist来指定兼容性
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
preProcessor,
].filter(Boolean);
};

module.exports = {
entry: "./src/main.js",
output: {
path: undefined,
filename: "static/js/[name].js",
chunkFilename: "static/js/[name].chunk.js",
assetModuleFilename: "static/js/[hash:10][ext][query]",
},
module: {
rules: [
// 处理vue vue-loader不支持oneOf
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
oneOf: [
// 处理css
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders("sass-loader"),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
// 处理图片
{
test: /\.(png|jpe?g|gif|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
},
// 处理其他资源
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
},
// 处理js
{
test: /\.js$/,
include: path.resolve(__dirname, "../src"),
loader: "babel-loader",
options: {
cacheDirectory: true,
cacheCompression: false
},
},

],
},
],
},
plugins: [
// 校验代码规范
new ESLintWebpackPlugin({
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
}),
// 处理html
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
// 处理vue
new VueLoaderPlugin(),
// cross-env 定义的环境变量给打包工具使用
// DefinePlugin 定义的环境变量给源代码使用,从而解决vue3页面警告的问题
new DefinePlugin({
// 要不要用
__VUE_OPTIONS_API__:true,
// 生产模式下开发工具要不要出现
__VUE_PROD_DEVTOOLS__:false
})
],
// 优化
optimization: {
// 代码分割
splitChunks: {
chunks: "all",
},
// 缓存hash提取
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`,
},
},
// webpack解析模块加载选项
resolve: {
extensions: [".vue", ".js", ".json"], // 自动补全文件扩展名
},
// 自动化配置开发服务器
devServer: {
open: true,
host: "localhost",
port: 3000,
hot: true,
compress: true,
historyApiFallback: true, // 解决前端路由刷新404问题
},
// 模式
mode: "development",
// 调试优化
devtool: "cheap-module-source-map",
};

生产模式配置

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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// webpack.prod.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const { VueLoaderPlugin } = require('vue-loader');
const { DefinePlugin } = require('webpack');

// 处理样式loader函数
const getStyleLoaders = (preProcessor) => {
return [
MiniCssExtractPlugin.loader,
"css-loader",
{
// 处理css兼容性
// 配合package.json中browserslist来指定兼容性
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
preProcessor,
].filter(Boolean);
};

module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "../dist"),
filename: "static/js/[name].[contenthash:10].js",
chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
assetModuleFilename: "static/js/[hash:10][ext][query]",
clean: true,
},
module: {
rules: [
// 处理vue vue-loader不支持oneOf
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
oneOf: [
// 处理css
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders("sass-loader"),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
// 处理图片
{
test: /\.(png|jpe?g|gif|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
},
// 处理其他资源
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
},
// 处理js
{
test: /\.js$/,
include: path.resolve(__dirname, "../src"),
loader: "babel-loader",
options: {
cacheDirectory: true,
cacheCompression: false
},
},
],
},
],
},
plugins: [
// 校验代码规范
new ESLintWebpackPlugin({
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
}),
// 处理html
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
// 处理vue
new VueLoaderPlugin(),
// cross-env 定义的环境变量给打包工具使用
// DefinePlugin 定义的环境变量给源代码使用,从而解决vue3页面警告的问题
new DefinePlugin({
// 要不要用
__VUE_OPTIONS_API__: true,
// 生产模式下开发工具要不要出现
__VUE_PROD_DEVTOOLS__: false
}),
// css提取
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
// 将public下面的资源复制到dist目录去(除了index.html)
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
toType: "dir",
noErrorOnMissing: true, // 不生成错误
globOptions: {
// 忽略文件
ignore: ["**/index.html"],
},
info: {
// 跳过terser压缩js
minimized: true,
},
},
],
}),
],
// 优化
optimization: {
// 压缩的操作
minimizer: [
// 压缩css
new CssMinimizerPlugin(),
// 压缩js
new TerserWebpackPlugin(),
// 压缩图片
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",
},
},
],
},
],
],
},
},
}),
],
// 代码分割
splitChunks: {
chunks: "all",
},
// 缓存hash提取
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`,
},
},
// webpack解析模块加载选项
resolve: {
extensions: [".vue", ".js", ".json"], // 自动补全文件扩展名
},
// 模式
mode: "production",
// 调试优化
devtool: "source-map",
};

其他配置

package.json

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
{
"name": "webpack_vue",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "npm run dev",
"dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config ./config/webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/eslint-parser": "^7.19.1",
"@vue/cli-plugin-babel": "^5.0.8",
"babel-loader": "^9.1.2",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^4.2.2",
"eslint-plugin-vue": "^9.9.0",
"eslint-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^5.5.0",
"image-minimizer-webpack-plugin": "^3.8.1",
"imagemin": "^8.0.1",
"imagemin-gifsicle": "^7.0.0",
"imagemin-jpegtran": "^7.0.0",
"imagemin-optipng": "^8.0.0",
"imagemin-svgo": "^10.0.1",
"less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.7.2",
"postcss-loader": "^7.0.2",
"postcss-preset-env": "^8.0.1",
"sass": "^1.58.0",
"sass-loader": "^13.2.0",
"stylus-loader": "^7.1.0",
"unplugin-auto-import": "^0.13.0",
"unplugin-vue-components": "^0.23.0",
"vue-loader": "^17.0.1",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.7.14",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"element-plus": "^2.2.28",
"vue": "^3.2.47",
"vue-router": "^4.1.6"
}
}

.eslintrc.js

1
2
3
4
5
6
7
8
9
10
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"],
};

.browserslistrc

1
2
3
4
5
6
# browserslistrc 是在不同的前端工具之间共用目标浏览器和 node 版本的配置文件。
# 用来配置需要兼容浏览器到什么程度

last 2 version #市面上浏览器最后的两个版本
> 1% #代表全球超过1%使用的浏览器
not dead # 没有死

合并开发和生产配置

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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// webpack.config.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const { DefinePlugin } = require("webpack");
const CopyPlugin = require("copy-webpack-plugin");

// 需要通过 cross-env 定义环境变量
const isProduction = process.env.NODE_ENV === "production";

const getStyleLoaders = (preProcessor) => {
return [
isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: ["postcss-preset-env"],
},
},
},
preProcessor,
].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/js/[hash:10][ext][query]",
clean: true,
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders("sass-loader"),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
},
{
test: /\.(jsx|js)$/,
include: path.resolve(__dirname, "../src"),
loader: "babel-loader",
options: {
cacheDirectory: true,
cacheCompression: false,
plugins: [
// "@babel/plugin-transform-runtime" // presets中包含了
],
},
},
// vue-loader不支持oneOf
{
test: /\.vue$/,
loader: "vue-loader", // 内部会给vue文件注入HMR功能代码
options: {
// 开启缓存
cacheDirectory: path.resolve(
__dirname,
"node_modules/.cache/vue-loader"
),
},
},
],
},
plugins: [
new ESLintWebpackPlugin({
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
toType: "dir",
noErrorOnMissing: true,
globOptions: {
ignore: ["**/index.html"],
},
info: {
minimized: true,
},
},
],
}),
isProduction &&
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
new VueLoaderPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__: "true",
__VUE_PROD_DEVTOOLS__: "false",
}),
].filter(Boolean),
optimization: {
minimize: isProduction,
// 压缩的操作
minimizer: [
new CssMinimizerPlugin(),
new TerserWebpackPlugin(),
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",
},
},
],
},
],
],
},
},
}),
],
splitChunks: {
chunks: "all",
},
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`,
},
},
resolve: {
extensions: [".vue", ".js", ".json"],
},
devServer: {
open: true,
host: "localhost",
port: 3000,
hot: true,
compress: true,
historyApiFallback: true, // 解决vue-router刷新404问题
},
mode: isProduction ? "production" : "development",
devtool: isProduction ? "source-map" : "cheap-module-source-map",
};

优化配置

  • element-plus按需导入、自定义主题
  • 代码分组
  • 路径别名
  • 关闭性能提示
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// webpack.config.js
const path = require("path"); // nodejs核心模块,专门用来处理路径问题
const ESLintWebpackPlugin = require("eslint-webpack-plugin"); // eslint 插件
const HtmlWebpackPlugin = require("html-webpack-plugin"); // 生成 HTML 文件的插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 将 CSS 提取到单独的文件中
const TerserWebpackPlugin = require("terser-webpack-plugin"); // 压缩js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 压缩 CSS
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // 压缩图片
const CopyPlugin = require("copy-webpack-plugin"); // 复制资源
const { VueLoaderPlugin } = require("vue-loader"); // 处理vue
const { DefinePlugin } = require("webpack") // 定义环境变量
const AutoImport = require('unplugin-auto-import/webpack') // 按需加载
const Components = require('unplugin-vue-components/webpack') // 自定义element-plus主题样式
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers') // 自定义element-plus主题样式
// 需要通过 cross-env 定义环境变量
const isProduction = process.env.NODE_ENV === "production"

// 处理样式loader函数
const getStyleLoaders = (preProcessor) => {
return [
isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader",
"css-loader",
{
// 处理css兼容性
// 配合package.json中browserslist来指定兼容性
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
preProcessor,
].filter(Boolean);
};

module.exports = {
// 入口
entry: "./src/main.js",
// 输出
output: {
// 所有文件输出目录,必须是绝对路径
path: isProduction ? path.resolve(__dirname, "../dist") : undefined,
// 输出文件名
// contenthash 根据文件内容生成 hash 值,只有文件内容变化了,hash 值才会变化。所有文件 hash 值是独享且不同的。
// [contenthash:8]使用contenthash,取8位长度
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",
// 图片字体等通过type:asset处理资源命名方式(注意用hash)
assetModuleFilename: "static/js/[hash:10][ext][query]",
// 自动清空上次打包结果
// 过程:在打包前将path整个目录清空,再进行打包
clean: isProduction ? true : false,
},
module: {
rules: [
{
// 每个文件只能匹配其中一个loader配置处理
oneOf: [
// 处理css
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders({
loader: "sass-loader",
options: {
// 自定义主题:自动引入我们定义的scss文件
additionalData: `@use "@/styles/element/index.scss" as *;`,
}
}),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
// 处理图片
{
test: /\.(png|jpe?g|gif|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 小于10kb的图片会被base64处理
// 优点:减少请求数量 缺点:体积会更大
maxSize: 10 * 1024,
},
},
// generator: {
// // 将图片文件输出到 static/imgs 目录中
// // 将图片文件命名 [hash:8][ext][query]
// // [hash:8]: hash值取8位
// // [ext]: 使用之前的文件扩展名
// // [query]: 添加之前的query参数
// filename: 'static/images/[hash:8][ext][query]'
// }
},
// 处理其他资源
{
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
// generator: {
// // 输出名称
// filename: 'static/media/[hash:8][ext][query]'
// }
},
// 处理js
{
test: /\.js$/,
include: path.resolve(__dirname, "../src"), // 只处理src下的js文件,其他文件不处理
loader: "babel-loader",
options: {
cacheDirectory: true, // 开启babel编译缓存
cacheCompression: false, // 缓存文件不要压缩(更占电脑内存些,但是打包速度更快)
plugins: [
// "@babel/plugin-transform-runtime" // 减少代码体积 vue预设中包含了
],
},
},
],
},
// 处理vue
// vue-loader不支持oneOf
{
test: /\.vue$/,
loader: 'vue-loader', // 内部会给vue文件注入HMR功能代码
options: {
// 开启缓存
cacheDirectory: path.resolve(__dirname, '../node_modules/.cache/vue-loader')
}
},
],
},
// 插件
plugins: [
// 校验代码规范
new ESLintWebpackPlugin({
context: path.resolve(__dirname, "../src"), // 检测哪些文件
exclude: "node_modules", // 默认值 排除node_modules代码不检测
cache: true, // 开启缓存
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
}),
// 处理html
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
// 处理vue
new VueLoaderPlugin(),
// cross-env 定义的环境变量给打包工具使用
// DefinePlugin 定义的环境变量给源代码使用,从而解决vue3页面警告的问题
new DefinePlugin({
// 要不要用
__VUE_OPTIONS_API__: true,
// 生产模式下开发工具要不要出现
__VUE_PROD_DEVTOOLS__: false
}),
// css提取
isProduction && new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
// 将public下面的资源复制到dist目录去(除了index.html)
isProduction && new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
toType: "dir",
noErrorOnMissing: true, // 不生成错误
globOptions: {
// 忽略文件
ignore: ["**/index.html"],
},
info: {
// 跳过terser压缩js
minimized: true,
},
},
],
}),
// 按需加载element-plus组件样式
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [
ElementPlusResolver({
// 自定义主题,引入sass
importStyle: "sass"
})
],
}),
].filter(Boolean),
// 优化
optimization: {
// 是否开启压缩
minimize: isProduction,
// 压缩的操作
minimizer: [
// 压缩css
new CssMinimizerPlugin(),
// 压缩js
new TerserWebpackPlugin(),
// 压缩图片
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",
},
},
],
},
],
],
},
},
}),
],
// 代码分割
splitChunks: {
chunks: "all",// 对所有模块都进行分割
cacheGroups: {// 分组
// layouts通常是admin项目的主体布局组件,所有路由组件都要使用的
// 可以单独打包,从而复用
// 如果项目中没有,请删除
layouts: {
name: "layouts",
test: path.resolve(__dirname, "../src/layouts"),
priority: 40,
},
// 如果项目中使用element-plus,此时将所有node_modules打包在一起,那么打包输出文件会比较大。
// 所以我们将node_modules中比较大的模块单独打包,从而并行加载速度更好
// 如果项目中没有,请删除
elementUI: {
name: 'chunk-elementPlus',
test: /[\\/]node_modules[\\/]_?element-plus(.*)/,
priority: 30
},
// 将vue相关的库单独打包,减少node_modules的chunk体积。
vue: {
name: 'vue',
test: /[\\/]node_modules[\\/]vue(.*)?[\\/]/,
priority: 20,
chunks: "initial",
},
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,// 权重最低,优先考虑前面内容
chunks: "initial",
}
}
},
// 缓存依赖的hash值提取
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}.js`, // runtime文件命名规则
},
},
// webpack解析模块加载选项
resolve: {
// 自动补全文件扩展名
extensions: [".vue", ".js", ".json"],
// 路径别名
alias: {
'@': path.resolve(__dirname, '../src')
}
},
// 自动化配置开发服务器
devServer: {
open: true,
host: "localhost",
port: 3000,
hot: true,
compress: true,
historyApiFallback: true, // 解决vue-router刷新404问题
},
// 模式
mode: isProduction ? "production" : "development",
// 调试优化
// devtool: isProduction ? "source-map" : "cheap-module-source-map",
// 生产环境建议不配置,减少打包体积,还可以防止源代码暴露
devtool: !isProduction && "cheap-module-source-map",
// 关闭性能分析,提升打包速度
performance: false
};