开发环境配置
package.json
{
"name": "vue-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "npm run dev",
"dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/eslint-parser": "^7.19.1",
"@vue/cli-plugin-babel": "^5.0.8",
"babel-loader": "^9.1.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.2",
"eslint-plugin-vue": "^9.8.0",
"eslint-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^5.5.0",
"less-loader": "^11.1.0",
"postcss-loader": "^7.0.1",
"postcss-preset-env": "^7.8.3",
"sass-loader": "^13.2.0",
"stylus-loader": "^7.1.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.0",
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"vue": "^3.2.45",
"vue-router": "^4.1.6"
}
}
config/webpack.dev.js
const path = require('path');
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader'); // vue-loader
const { DefinePlugin } = require('webpack'); // DefinePlugin用来定义环境变量给代码使用
// 返回处理样式loader的函数
const getStyleLoaders = (pre) => {
return [
"vue-style-loader", // 处理样式需要用vue-style-loader
"css-loader",
{
// 处理浏览器兼容性问题
// 需要配合package.json中的browserslist来指定兼容性处理到什么程度
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins:['postcss-preset-env']
}
}
},
pre
].filter(Boolean);
}
module.exports = {
entry: './src/main.js',
output: {
path: undefined,
filename: 'static/js/[name].js',
chunkFilename: 'static/js/[name].chunk.js',
assetModuleFilename: 'static/media/[hash:10][ext][query]',
},
module: {
rules: [
// 处理css
{
test: /\.css$/i,
use: getStyleLoaders()
},
{
test: /\.less$/i,
use: getStyleLoaders('less-loader')
},
{
test: /\.s[ac]ss$/i,
use: getStyleLoaders('sass-loader')
},
{
test: /\.styl$/i,
use: getStyleLoaders('stylus-loader')
},
// 处理图片
{
test: /.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 小于10kb的图片会转base64
maxSize: 10 * 1024 // 10kb
}
},
},
// 处理其他资源
{
test: /.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource"
},
// 处理js
// 配置babel
{
test: /\.js?$/,
include: path.resolve(__dirname, '../src'), // 只处理src
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
}
},
// 解析vue文件
{
test: /\.vue$/,
loader: 'vue-loader'
},
]
},
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'],
cache: true, // 开启eslint缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // eslint缓存目录
}),
// 处理html
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
}),
new VueLoaderPlugin(), // 编译vue
// cross-env定义的环境变量时给webpack使用
// DefinePlugin 定义环境变量给源代码使用,从而解决vue3页面相关警告
new DefinePlugin({
__VUE_OPTIONS_API__: true, // 打开options api
__VUE_PROD_DEVTOOLS__: false, // 开发环境不开启devtool
})
],
// 模式
mode: 'development',
// SourceMap
devtool: 'cheap-module-source-map',
optimization: {
// 代码分割配置
splitChunks: {
chunks: "all",
// 其它的都用默认值即可
},
// runtime文件来保存文件的hash值
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}.js`
}
},
// webpack解析模块加载选项
resolve: {
extensions: ['.vue', '.js', '.json']
},
devServer: {
host: 'localhost', // 启动服务器的域名
port: 4000, // 启动服务器端口号
open: true,
hot: true, // 默认开启
historyApiFallback: true, // 解决前端路由刷新404问题
},
}
.eslintrc.js
module.exports = {
root: true, // 跟目录是当前
env: {
node: true // 启用node环境变量
},
extends: ["plugin:vue/vue3-essential", "eslint:recommended"], // 继承vue/vue3的官方规则、以及eslint的官方规则
parserOptions: {
parser: "@babel/eslint-parser"
}
}
babel.config.js
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
}
src/main.js
import { createApp } from 'vue'
import App from './App'
import router from './router'
createApp(App).use(router).mount(document.getElementById('app'));
src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const Home = () => import('../views/Home');
const About = () => import('../views/About');
export default createRouter({
history: createWebHistory(),
routes: [
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
})
生产环境配置
package.json
{
"name": "vue-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "npm run dev",
"dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/eslint-parser": "^7.19.1",
"@vue/cli-plugin-babel": "^5.0.8",
"babel-loader": "^9.1.0",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.2",
"css-minimizer-webpack-plugin": "^4.2.2",
"eslint-plugin-vue": "^9.8.0",
"eslint-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^5.5.0",
"image-minimizer-webpack-plugin": "^3.8.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.0",
"postcss-loader": "^7.0.1",
"postcss-preset-env": "^7.8.3",
"sass-loader": "^13.2.0",
"stylus-loader": "^7.1.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.0",
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"vue": "^3.2.45",
"vue-router": "^4.1.6"
}
}
config/webpack.prod.js
const path = require('path');
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 提取css为单独文件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // css压缩
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // 图片压缩
const CopyPlugin = require("copy-webpack-plugin"); // 复制插件
const { VueLoaderPlugin } = require('vue-loader'); // vue-loader
const { DefinePlugin } = require('webpack'); // DefinePlugin用来定义环境变量给代码使用
// 返回处理样式loader的函数
const getStyleLoaders = (pre) => {
return [
MiniCssExtractPlugin.loader, // 提取css为单独文件
"css-loader",
{
// 处理浏览器兼容性问题
// 需要配合package.json中的browserslist来指定兼容性处理到什么程度
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins:['postcss-preset-env']
}
}
},
pre
].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/media/[hash:10][ext][query]',
clean: true,
},
module: {
rules: [
// 处理css
{
test: /\.css$/i,
use: getStyleLoaders()
},
{
test: /\.less$/i,
use: getStyleLoaders('less-loader')
},
{
test: /\.s[ac]ss$/i,
use: getStyleLoaders('sass-loader')
},
{
test: /\.styl$/i,
use: getStyleLoaders('stylus-loader')
},
// 处理图片
{
test: /.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 小于10kb的图片会转base64
maxSize: 10 * 1024 // 10kb
}
},
},
// 处理其他资源
{
test: /.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource"
},
// 处理js
// 配置babel
{
test: /\.js?$/,
include: path.resolve(__dirname, '../src'), // 只处理src
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
// plugins: ['react-refresh/babel'], // 激活js的HMR 生产模式没HMR功能
}
},
// 解析vue文件
{
test: /\.vue$/,
loader: 'vue-loader'
},
]
},
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'],
cache: true, // 开启eslint缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // eslint缓存目录
}),
// 处理html
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
}),
new MiniCssExtractPlugin({ // 提取css为单独文件
filename: 'static/css/[name].[contenthash:10].css',
chunkFilename: 'static/css/[name].[contenthash:10].chunk.css',
}),
new CopyPlugin({
patterns: [
{ // 把public下的文件复制到dist目录
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist'),
globOptions: {
ignore: ["**/index.html"], // 忽略public中的index.html
},
},
],
}),
new VueLoaderPlugin(), // 编译vue
// cross-env定义的环境变量时给webpack使用
// DefinePlugin 定义环境变量给源代码使用,从而解决vue3页面相关警告
new DefinePlugin({
__VUE_OPTIONS_API__: true, // 打开options api
__VUE_PROD_DEVTOOLS__: false, // 开发环境不开启devtool
})
],
// 模式
mode: 'production',
// SourceMap
devtool: 'source-map',
optimization: {
// 代码分割配置
splitChunks: {
chunks: "all",
// 其它的都用默认值即可
},
// runtime文件来保存文件的hash值
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}.js`
},
minimizer: [
new CssMinimizerPlugin(), // css压缩
new TerserWebpackPlugin(), // 压缩js
// 压缩图片(也可以放到plugin配置中)
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"
}
}
]
}
]
]
}
}
}),
]
},
// webpack解析模块加载选项(文件拓展名)
resolve: {
extensions: ['.vue', '.js', '.json']
},
}
开发环境和生产环境合并配置
package.json
{
"name": "vue-cli",
"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.0",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.2",
"css-minimizer-webpack-plugin": "^4.2.2",
"eslint-plugin-vue": "^9.8.0",
"eslint-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^5.5.0",
"image-minimizer-webpack-plugin": "^3.8.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.0",
"postcss-loader": "^7.0.1",
"postcss-preset-env": "^7.8.3",
"sass-loader": "^13.2.0",
"stylus-loader": "^7.1.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.0",
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"vue": "^3.2.45",
"vue-router": "^4.1.6"
}
}
config/webpack.config.js
const path = require('path');
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 提取css为单独文件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // css压缩
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // 图片压缩
const CopyPlugin = require("copy-webpack-plugin"); // 复制插件
const { VueLoaderPlugin } = require('vue-loader'); // vue-loader
const { DefinePlugin } = require('webpack'); // DefinePlugin用来定义环境变量给代码使用
// 是否是生产环境
const isProduction = process.env.NODE_ENV === 'production';
// 返回处理样式loader的函数
const getStyleLoaders = (pre) => {
return [
isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader',
"css-loader",
{
// 处理浏览器兼容性问题
// 需要配合package.json中的browserslist来指定兼容性处理到什么程度
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: [
// 处理css
{
test: /\.css$/i,
use: getStyleLoaders()
},
{
test: /\.less$/i,
use: getStyleLoaders('less-loader')
},
{
test: /\.s[ac]ss$/i,
use: getStyleLoaders('sass-loader')
},
{
test: /\.styl$/i,
use: getStyleLoaders('stylus-loader')
},
// 处理图片
{
test: /.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 小于10kb的图片会转base64
maxSize: 10 * 1024 // 10kb
}
},
},
// 处理其他资源
{
test: /.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource"
},
// 处理js
// 配置babel
{
test: /\.js?$/,
include: path.resolve(__dirname, '../src'), // 只处理src
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
// plugins: ['react-refresh/babel'], // 激活js的HMR 生产模式没HMR功能
}
},
// 解析vue文件
{
test: /\.vue$/,
loader: 'vue-loader'
},
]
},
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'],
cache: true, // 开启eslint缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // eslint缓存目录
}),
// 处理html
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
}),
isProduction && new MiniCssExtractPlugin({ // 提取css为单独文件
filename: 'static/css/[name].[contenthash:10].css',
chunkFilename: 'static/css/[name].[contenthash:10].chunk.css',
}),
isProduction && new CopyPlugin({
patterns: [
{ // 把public下的文件复制到dist目录
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist'),
globOptions: {
ignore: ["**/index.html"], // 忽略public中的index.html
},
},
],
}),
new VueLoaderPlugin(), // 编译vue
// cross-env定义的环境变量时给webpack使用
// DefinePlugin 定义环境变量给源代码使用,从而解决vue3页面相关警告
new DefinePlugin({
__VUE_OPTIONS_API__: true, // 打开options api
__VUE_PROD_DEVTOOLS__: false, // 开发环境不开启devtool
})
].filter(Boolean),
// 模式
mode: isProduction ? 'production' : 'development',
// SourceMap
devtool: isProduction ? 'source-map' : 'cheap-module-source-map',
optimization: {
// 代码分割配置
splitChunks: {
chunks: "all",
// 其它的都用默认值即可
},
// runtime文件来保存文件的hash值
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}.js`
},
minimize: isProduction, // 是否需要压缩
minimizer: [
new CssMinimizerPlugin(), // css压缩
new TerserWebpackPlugin(), // 压缩js
// 压缩图片(也可以放到plugin配置中)
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"
}
}
]
}
]
]
}
}
}),
]
},
// webpack解析模块加载选项(文件拓展名)
resolve: {
extensions: ['.vue', '.js', '.json']
},
devServer: {
host: 'localhost', // 启动服务器的域名
port: 4000, // 启动服务器端口号
open: true,
hot: true, // 默认开启
historyApiFallback: true, // 解决前端路由刷新404问题
},
}
vue cli优化配置
1、vue中常用的ui库是 element-plus。有全部引入和按需引入两种方式
安装element-plus
npm i element-plus
全局引入:
import { createApp } from 'vue'
import App from './App'
import router from './router'
// 全局引入element-plus
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'
createApp(App).use(router).use(ElementPlus).mount(document.getElementById('app'));
按需引入:element-plus.org/zh-CN/guide…
按需引入不需要在main.js中引入所有的组件和样式,在组件内用哪个组件,引入注册就行
2、自定义主题
文档:element-plus.org/zh-CN/guide…
原理是定义scss变量去覆盖原有的element-plus中的变量
创建一个样式文件,定义变量
src/styles/element/index.scss
// styles/element/index.scss
/* 只需要重写你需要的即可 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': green,
),
),
);
引入这个文件有两种方式:1:手动引入, 2:自动引入
1、手动引入
在main.js中,在element-plus引入后引入此样式即可
2、自动引入
现在不需要安装node-sass,安装sass即可
// 返回处理样式loader的函数
const getStyleLoaders = (pre) => {
return [
isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader',
"css-loader",
{
// 处理浏览器兼容性问题
// 需要配合package.json中的browserslist来指定兼容性处理到什么程度
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins:['postcss-preset-env']
}
}
},
pre && {
loader: pre,
options: pre === 'sass-loader' ? {
additionalData: `@use "@/styles/element/index.scss" as *;`, // element-plus自定义主题引入scss文件
} : {}
}
].filter(Boolean);
}
plugins中的变动
Components({
resolvers: [ElementPlusResolver({
importStyle: "sass", // 告知将来会引入sass文件(element-plus自定义主题,引入sass)
})],
}),
3、分包
将vue相关的单独打包、element-plus单独打包、剩余的单独打包
optimization: {
// 代码分割配置
splitChunks: {
chunks: "all",
// 分开打包
cacheGroups: {
vue: { // vue相关的分到一个包
test: /[\\/]node_modules[\\/]vue(.*)?/,
name: 'vue-chunk', // 打包名字
priority: 40, // 权重设置为40 (目的是让权重大于node_modules的权重,否则会打到node_modules包中)
},
elementPlus: { // element-plus打包到一个包
test: /[\\/]node_modules[\\/]element-plus[\\/]/,
name: 'elementPlus-chunk',
priority: 30,
},
libs: { // 剩余的打到一个包
test: /[\\/]node_modules[\\/]/,
name: 'libs-chunk',
priority: 20,
},
}
},
},
4、关闭性能分析、提升打包速度
performance: false, // 关闭性能分析,提上打包速度
5、vue-loader开启缓存,提升第二次以后打包速度
// 解析vue文件
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// 开启缓存(提升第二次以后打包速度)
cacheDirectory: path.resolve(__dirname, '../node_modules/.cache/vue-loader')
}
},
优化后的总体配置
package.json
{
"name": "vue-cli",
"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.0",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.2",
"css-minimizer-webpack-plugin": "^4.2.2",
"eslint-plugin-vue": "^9.8.0",
"eslint-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^5.5.0",
"less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.7.0",
"postcss-loader": "^7.0.1",
"postcss-preset-env": "^7.8.3",
"sass-loader": "^13.2.0",
"stylus-loader": "^7.1.0",
"unplugin-auto-import": "^0.12.0",
"unplugin-vue-components": "^0.22.11",
"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.0",
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"element-plus": "^2.2.25",
"sass": "^1.56.1",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
}
}
webpack.config.js
const path = require('path');
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 提取css为单独文件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // css压缩
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
// const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // 图片压缩
const CopyPlugin = require("copy-webpack-plugin"); // 复制插件
const { VueLoaderPlugin } = require('vue-loader'); // vue-loader
const { DefinePlugin } = require('webpack'); // DefinePlugin用来定义环境变量给代码使用
// element-plus按需引入
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';
// 返回处理样式loader的函数
const getStyleLoaders = (pre) => {
return [
isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader',
"css-loader",
{
// 处理浏览器兼容性问题
// 需要配合package.json中的browserslist来指定兼容性处理到什么程度
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins:['postcss-preset-env']
}
}
},
pre && {
loader: pre,
options: pre === 'sass-loader' ? {
additionalData: `@use "@/styles/element/index.scss" as *;`, // element-plus自定义主题引入scss文件
} : {}
}
].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: [
// 处理css
{
test: /\.css$/i,
use: getStyleLoaders()
},
{
test: /\.less$/i,
use: getStyleLoaders('less-loader')
},
{
test: /\.s[ac]ss$/i,
use: getStyleLoaders('sass-loader')
},
{
test: /\.styl$/i,
use: getStyleLoaders('stylus-loader')
},
// 处理图片
{
test: /.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 小于10kb的图片会转base64
maxSize: 10 * 1024 // 10kb
}
},
},
// 处理其他资源
{
test: /.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource"
},
// 处理js
// 配置babel
{
test: /\.js?$/,
include: path.resolve(__dirname, '../src'), // 只处理src
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
// plugins: ['react-refresh/babel'], // 激活js的HMR 生产模式没HMR功能
}
},
// 解析vue文件
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// 开启缓存(提升第二次打包速度)
cacheDirectory: path.resolve(__dirname, '../node_modules/.cache/vue-loader')
}
},
]
},
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'],
cache: true, // 开启eslint缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // eslint缓存目录
}),
// 处理html
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
}),
isProduction && new MiniCssExtractPlugin({ // 提取css为单独文件
filename: 'static/css/[name].[contenthash:10].css',
chunkFilename: 'static/css/[name].[contenthash:10].chunk.css',
}),
isProduction && new CopyPlugin({
patterns: [
{ // 把public下的文件复制到dist目录
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist'),
globOptions: {
ignore: ["**/index.html"], // 忽略public中的index.html
},
},
],
}),
new VueLoaderPlugin(), // 编译vue
// cross-env定义的环境变量时给webpack使用
// DefinePlugin 定义环境变量给源代码使用,从而解决vue3页面相关警告
new DefinePlugin({
__VUE_OPTIONS_API__: true, // 打开options api
__VUE_PROD_DEVTOOLS__: false, // 开发环境不开启devtool
}),
// element-plus按需导入
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver({
importStyle: "sass", // 告知将来会引入sass文件(element-plus自定义主题,引入sass)
})],
}),
].filter(Boolean),
// 模式
mode: isProduction ? 'production' : 'development',
// SourceMap
devtool: isProduction ? 'source-map' : 'cheap-module-source-map',
optimization: {
// 代码分割配置
splitChunks: {
chunks: "all",
// 分开打包
cacheGroups: {
vue: { // vue相关的分到一个包
test: /[\\/]node_modules[\\/]vue(.*)?/,
name: 'vue-chunk', // 打包名字
priority: 40, // 权重设置为40 (目的是让权重大于node_modules的权重,否则会打到node_modules包中)
},
elementPlus: { // element-plus打包到一个包
test: /[\\/]node_modules[\\/]element-plus[\\/]/,
name: 'elementPlus-chunk',
priority: 30,
},
libs: { // 剩余的打到一个包
test: /[\\/]node_modules[\\/]/,
name: 'libs-chunk',
priority: 20,
},
}
},
// runtime文件来保存文件的hash值
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}.js`
},
minimize: isProduction, // 是否需要压缩
minimizer: [
new CssMinimizerPlugin(), // css压缩
new TerserWebpackPlugin(), // 压缩js
// 压缩图片(也可以放到plugin配置中) 依赖难下 image-minimizer-webpack-plugin、imagemin-gifsicle、imagemin-jpegtran、imagemin-optipng、imagemin-svgo
// 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"
// }
// }
// ]
// }
// ]
// ]
// }
// }
// }),
]
},
// webpack解析模块加载选项
resolve: {
extensions: ['.vue', '.js', '.json'], // 文件拓展名
alias: { // 路径别名
'@': path.resolve(__dirname, '../src')
}
},
devServer: {
host: 'localhost', // 启动服务器的域名
port: 4000, // 启动服务器端口号
open: true,
hot: true, // 默认开启
historyApiFallback: true, // 解决前端路由刷新404问题
},
performance: false, // 关闭性能分析,提上打包速度
}