主题
最终配置
base
- webpack.base.js
js
// webpack基础配置
// 1. 入口文件
// 2. 出口文件
// 3. loader
// 4. plugins
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ESLintPlugin = require("eslint-webpack-plugin");
const AutoImport = require("unplugin-auto-import/webpack");
const Components = require("unplugin-vue-components/webpack");
const { ElementPlusResolver } = require("unplugin-vue-components/resolvers");
const fs = require("fs");
const NODE_ENV = process.env.NODE_ENV;
if (!NODE_ENV) {
throw new Error(
"The NODE_ENV environment variable is required but was not specified."
);
}
const dotenvFiles = [
`.env`,
`.env.${NODE_ENV}`,
`.env.local`,
`.env.${NODE_ENV}.local`,
].filter(Boolean);
dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) {
require("dotenv").config({
path: dotenvFile,
});
}
});
const isProduction = process.env.NODE_ENV === "production";
const getStyleLoaders = (preProcessor) => {
return [
isProduction ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
"postcss-loader",
preProcessor,
].filter(Boolean);
};
/**
* @type {import('webpack').Configuration}
*/
module.exports = {
entry: path.resolve(__dirname, "../src/main.ts"),
output: {
path: path.resolve(__dirname, "../dist"), // 打包后的目录
filename: "js/[name].[contenthash:6].js", // 打包后的文件名
chunkFilename: "js/[name].[contenthash:8].js",
clean: true, // 清除上一次打包的文件
publicPath: "/", // 打包后的资源的访问路径前缀
},
resolve: {
alias: {
"@": path.resolve(__dirname, "../src"), // @ 代表 src 路径
vue$: "vue/dist/vue.runtime.esm-bundler.js",
},
// 引入文件的时候不需要添加后缀,这个配置也会稍微的提升构建速度
extensions: [".js", ".ts", ".vue", ".json"],
},
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
title: process.env.VUE_APP_TITLE,
BASE_URL: process.env.BASE_URL,
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
toType: "dir",
globOptions: {
ignore: ["**/.DS_Store", "**/index.html"],
},
info: {
minimized: true,
},
},
],
}),
new VueLoaderPlugin(), // vue-loader插件
new webpack.DefinePlugin({
"process.env.VUE_APP_API_URL": JSON.stringify(
process.env.VUE_APP_API_URL
),
"process.env.BASE_URL": JSON.stringify(process.env.BASE_URL),
"process.env.CURRENT_ENV": JSON.stringify(process.env.CURRENT_ENV),
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
}),
new MiniCssExtractPlugin({
filename: "css/[name].[contenthash:6].css",
}),
new ESLintPlugin({
extensions: ["js", "ts", "vue", "jsx", "tsx"],
exclude: "node_modules",
context: path.resolve(__dirname, "../src"),
cache: true, // 开启缓存
// 缓存目录
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
}),
],
module: {
rules: [
{
test: /\.m?jsx?$/,
exclude: /node_modules/,
use: "babel-loader",
},
{
test: /\.vue$/,
use: "vue-loader",
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: "ts-loader",
options: {
transpileOnly: true, // 关闭类型检测,即只进行转译
appendTsSuffixTo: ["\\.vue$"], // 给vue文件添加ts后缀
},
},
],
},
{
test: /\.(png|jpe?g|gif|webp|avif)(\?.*)?$/,
type: "asset", // webpack5通用资源处理模块,默认8kb以下的资源会被转换为base64
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 10kb以下的资源会被转换为base64
},
},
generator: {
filename: "img/[name].[contenthash:6][ext]", //文件打包输出目录
},
},
{
test: /\.(svg)(\?.*)?$/,
type: "asset/resource", // webpack5通用资源处理模块,默认会导出出单独的文件
generator: {
filename: "img/[name].[contenthash:6][ext]", //文件打包输出目录
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 2 * 1024, // 2kb以下的资源会被转换为base64
},
},
generator: {
filename: "fonts/[name].[contenthash:6][ext]", //文件打包输出目录
},
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 20 * 1024, // 20kb以下的资源会被转换为base64
},
},
generator: {
filename: "media/[name].[contenthash:6][ext]", //文件打包输出目录
},
},
{
oneOf: [
{
test: /\.css$/i,
exclude: [/\.module\.css$/],
use: getStyleLoaders(),
},
{
test: /\.module\.css$/i,
use: [
isProduction ? MiniCssExtractPlugin.loader : "style-loader",
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[name]__[local]__[hash:base64:5]",
},
},
},
{
loader: "postcss-loader",
},
],
},
{
test: /\.s[ac]ss$/i,
use: [
...getStyleLoaders("sass-loader"),
{
loader: "style-resources-loader",
options: {
patterns: [
path.resolve(
__dirname,
"../src/assets/styles/variables.scss"
),
],
},
},
],
},
{
test: /\.less$/i,
use: getStyleLoaders("less-loader"),
},
{
test: /\.styl$/i,
use: getStyleLoaders("stylus-loader"),
},
],
},
// {
// test: /\.css$/i,
// use: [
// MiniCssExtractPlugin.loader, // 代替style-loader,提取css到单独的文件中
// 'css-loader'
// ]
// },
// {
// test: /\.s[ac]ss$/i,
// use: [
// MiniCssExtractPlugin.loader,
// 'css-loader', 'sass-loader'
// ]
// },
// {
// test: /\.less$/i,
// use: [
// MiniCssExtractPlugin.loader,
// 'css-loader', 'less-loader'
// ]
// }
],
},
};
dev
- webpack.dev.js
js
const baseConfig = require("./webpack.base.js");
const { merge } = require("webpack-merge");
/**
* @type {import('webpack').Configuration}
*/
const devConfig = {
mode: "development",
devtool: "eval-cheap-module-source-map",
devServer: {
port: process.env.VUE_APP_PORT || 8080,
open: JSON.parse(process.env.VUE_APP_OPEN),
historyApiFallback: true, // 该选项的作用所有的404都连接到index.html
// hot:true // 模块热替换在webpack5中默认开启
setupMiddlewares: require("../mock"),
},
plugins: [
// new webpack.HotModuleReplacementPlugin() // 模块热替换插件
],
};
module.exports = merge(baseConfig, devConfig);
prod
- webpack.prod.js
js
const baseConfig = require("./webpack.base.js");
const { merge } = require("webpack-merge");
const BundleAnalyzerPlugin =
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const TerserPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
/**
* @type {import('webpack').Configuration}
*/
const prodConfig = {
mode: "production",
plugins: [new BundleAnalyzerPlugin()],
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin({
parallel: true, // 开启多进程并行压缩
terserOptions: {
format: {
comments: false, // 去除注释
},
compress: {
drop_console: true, // 去除console.log
drop_debugger: true, // 去除debugger
pure_funcs: ["console.log", "console.info", "console.error"], // 配置发布时,不被打包的函数
},
},
extractComments: false, // 不将注释提取到单独的文件中
}),
],
chunkIds: "named",
runtimeChunk: "single",
splitChunks: {
chunks: "all", //initial、async和all
// minSize: 30000, // 形成一个新代码块最小的体积
// minChunks: 1, // 在分割之前,这个代码块最小应该被引用的次数
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
priority: 10,
reuseExistingChunk: true,
},
echarts: {
test: /[\\/]node_modules[\\/]echarts|zrender(.*)/,
name: "chunk-echarts",
priority: 25,
reuseExistingChunk: true,
},
element: {
test: /[\\/]node_modules[\\/]element(.*)/,
name: "chunk-element",
priority: 25,
reuseExistingChunk: true,
},
commons: {
name: "chunk-commons",
minChunks: 2,
priority: 5,
minSize: 0,
reuseExistingChunk: true,
},
lib: {
test(module) {
return (
module.size() > 160000 &&
module.nameForCondition() &&
module.nameForCondition().includes("node_modules")
);
},
name: "chunk-lib",
priority: 20,
minChunks: 1,
reuseExistingChunk: true,
},
},
},
},
};
module.exports = merge(baseConfig, prodConfig);
babel.config
- babel.config.js
js
module.exports = {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage", // 按需引入 polyfill
corejs: 3,
},
],
[
"@babel/preset-typescript",
{
allExtensions: true,
},
],
],
// plugins: [
// [
// "@babel/plugin-transform-runtime",
// {
// corejs: 3
// }
// ]
// ]
};
mock
- mock/index.js
js
const Mock = require("mockjs"); //mockjs 导入依赖模块
const util = require("./util"); //自定义工具模块
//返回一个函数
module.exports = function (middlewares, devServer) {
//监听http请求
devServer.app.get("/api/users/list", function (_rep, res) {
//每次响应请求时读取mock data的json文件
//util.getJsonFile方法定义了如何读取json文件并解析成数据对象
var json = util.getJsonFile("./userInfo.json");
//将json传入 Mock.mock 方法中,生成的数据返回给浏览器
res.json(Mock.mock(json));
});
return middlewares;
};
- mock/userinfo.json
json
{
"code": 200,
"data|20-30": [
{
"_id": "@guid",
"loginId": "@string(2,6)",
"loginPwd": "@word(3,5)",
"name": "@cname",
"age|18-45": 1,
"loves": "@pick(['电影','阅读','旅游','运动','游戏'],1,3)",
"address": {
"province": "@province",
"city": "@city"
}
}
],
"msg": "成功"
}
- mock/util.js
js
const fs = require("fs"); //引入文件系统模块
const path = require("path"); //引入path模块
module.exports = {
//读取json文件
getJsonFile: function (filePath) {
//读取指定json文件
var json = fs.readFileSync(path.resolve(__dirname, filePath), "utf-8");
//解析并返回
return JSON.parse(json);
},
};