一、vue开发环境配置
效果:
1、目录结构
2、开始搭建
npm init -y生成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.17.0",
"@vue/cli-plugin-babel": "^5.0.4",
"babel-loader": "^8.2.5",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"eslint": "^8.42.0",
"eslint-plugin-vue": "^8.7.1",
"eslint-webpack-plugin": "^3.1.1",
"html-webpack-plugin": "^5.5.1",
"less-loader": "^10.2.0",
"postcss-loader": "^6.2.1",
"postcss-preset-env": "^7.5.0",
"sass": "^1.62.1",
"sass-loader": "^13.3.1",
"vue-loader": "^17.2.2",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.7.14",
"webpack": "^5.72.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.0"
},
"dependencies": {
"vue": "^3.3.4",
"vue-router": "^4.2.2"
}
}
babel.config.js
module.exports = {
presets: ['@vue/cli-plugin-babel/preset']
};
.eslintrc.js
module.exports = {
root: true,
env: { node: true },
extends: ['plugin:vue/vue3-essential', 'eslint:recommended'],
parserOptions: { parser: '@babel/eslint-parser' }
};
pages/Home.vue
<template>
<h1 class="title">Home</h1>
</template>
<style lang='less' scoped>
.title {
color: blue;
}
</style>
pages/About.vue
<template>
<h1 class="title">About</h1>
</template>
App.vue
<template>
<div>
<h1>App</h1>
<ul>
<li><router-link to='/home'>Home</router-link></li>
<li><router-link to='/about'>About</router-link></li>
</ul>
<router-view></router-view>
</div>
</template>
router.js
import { createRouter, createWebHistory } from 'vue-router';
const Home = () => import(/* webpackChunkName: 'home' */ './pages/Home');
const About = () => import('./pages/About');
const routers = createRouter({
history: createWebHistory(),
routes: [
{ path: '/home', component: Home },
{ path: '/about', component: About }
]
});
export default routers;
main.js
import { createApp } from 'vue';
import App from './App';
import router from './router';
createApp(App).use(router).mount(document.querySelector('#app'));
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vue cli</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
config/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');
const getStyleLoaders = loaderName => {
const loaders = [
'vue-style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: { plugins: ['postcss-preset-env'] }
}
},
loaderName
];
return loaders.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: [
{ test: /\.css$/, use: getStyleLoaders() },
{ test: /\.less$/, use: getStyleLoaders('less-loader') },
{ test: /\.s[ac]ss$/, use: getStyleLoaders('sass-loader') },
{
test: /\.(jpe?g|png|gif|webp|svg)$/,
type: 'asset',
parser: { dataUrlCondition: { maxSize: 10 * 1024 } }
},
{ test: /\.(woff2?|ttf)$/, type: 'asset/resource' },
{
test: /\.js$/,
include: path.resolve(__dirname, '../src'),
loader: 'babel-loader',
options: { cacheDirectory: true, cacheCompression: false }
},
{ test: /\.vue$/, loader: '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 VueLoaderPlugin(),
// DefinePlugin定义环境变量给源代码使用,从而解决vue3页面警告的问题
new DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
})
],
mode: 'development',
devtool: 'cheap-module-source-map',
optimization: {
splitChunks: { chunks: 'all' },
runtimeChunk: { name: entrypoint => `runtime~${entrypoint}.js` }
},
resolve: { extensions: ['.vue', '.js', '.json'] },
devServer: {
host: 'localhost',
port: 3005,
// open: true,
// hot: true,
historyApiFallback: true // 解决前端路由刷新404问题
}
};
二、vue生产环境配置
安装包(node版本:16.14.0)
cnpm i mini-css-extract-plugin css-minimizer-webpack-plugin -D // 提取css、css压缩
cnpm i image-minimizer-webpack-plugin imagemin imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D // 图片无损压缩
cnpm i copy-webpack-plugin -D // 将plugin目录下的资源拷贝到dist目录中
添加指令
"prod": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"
public
- 添加favicon.ico
- 引入ico:
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
webpack.prod.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');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const { loader: miniCssLoader } = MiniCssExtractPlugin;
const getStyleLoaders = loaderName => {
const loaders = [
miniCssLoader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: { plugins: ['postcss-preset-env'] }
}
},
loaderName
];
return loaders.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: [
{ test: /\.css$/, use: getStyleLoaders() },
{ test: /\.less$/, use: getStyleLoaders('less-loader') },
{ test: /\.s[ac]ss$/, use: getStyleLoaders('sass-loader') },
{
test: /\.(jpe?g|png|gif|webp|svg)$/,
type: 'asset',
parser: { dataUrlCondition: { maxSize: 10 * 1024 } }
},
{ test: /\.(woff2?|ttf)$/, type: 'asset/resource' },
{
test: /\.js$/,
include: path.resolve(__dirname, '../src'),
loader: 'babel-loader',
options: { cacheDirectory: true, cacheCompression: false }
},
{ test: /\.vue$/, loader: '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 MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:10].css',
chunkFilename: 'static/css/[name].[contenthash:10].chunk.css'
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist'),
globOptions: { ignore: ['**/index.html'] } // 忽略index.html文件
}
]
}),
new VueLoaderPlugin(),
// DefinePlugin定义环境变量给源代码使用,从而解决vue3页面警告的问题
new DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
})
],
mode: 'production',
devtool: 'source-map',
optimization: {
splitChunks: { chunks: 'all' },
runtimeChunk: { name: entrypoint => `runtime~${entrypoint}.js` },
minimizer: [
new CssMinimizerWebpackPlugin(),
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' }
}
]
}
]
]
}
}
})
]
},
resolve: { extensions: ['.vue', '.js', '.json'] }
};
三、合并配置
webpack.config.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');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const { loader: miniCssLoader } = MiniCssExtractPlugin;
const isProduction = process.env.NODE_ENV === 'production';
const getStyleLoaders = loaderName => {
const loaders = [
isProduction ? miniCssLoader : 'vue-style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: { plugins: ['postcss-preset-env'] }
}
},
loaderName
];
return loaders.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: /\.css$/, use: getStyleLoaders() },
{ test: /\.less$/, use: getStyleLoaders('less-loader') },
{ test: /\.s[ac]ss$/, use: getStyleLoaders('sass-loader') },
{
test: /\.(jpe?g|png|gif|webp|svg)$/,
type: 'asset',
parser: { dataUrlCondition: { maxSize: 10 * 1024 } }
},
{ test: /\.(woff2?|ttf)$/, type: 'asset/resource' },
{
test: /\.js$/,
include: path.resolve(__dirname, '../src'),
loader: 'babel-loader',
options: { cacheDirectory: true, cacheCompression: false }
},
{ test: /\.vue$/, loader: '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')
}),
isProduction &&
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:10].css',
chunkFilename: 'static/css/[name].[contenthash:10].chunk.css'
}),
isProduction &&
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist'),
globOptions: { ignore: ['**/index.html'] } // 忽略index.html文件
}
]
}),
new VueLoaderPlugin(),
// DefinePlugin定义环境变量给源代码使用,从而解决vue3页面警告的问题
new DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
})
].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 CssMinimizerWebpackPlugin(),
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' }
}
]
}
]
]
}
}
})
]
},
resolve: { extensions: ['.vue', '.js', '.json'] },
devServer: {
host: 'localhost',
port: 3005,
// open: true,
// hot: true,
historyApiFallback: true
},
performance: false
};
package.json
"dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.config.js",
"prod": "cross-env NODE_ENV=production webpack --config ./config/webpack.config.js"
四、优化配置
1、按需引入element-plus
下载包
cnpm i element-plus
cnpm install -D unplugin-vue-components unplugin-auto-import
webpack.config.js
// 按需引入element-plus
const AutoImport = require('unplugin-auto-import/webpack');
const Components = require('unplugin-vue-components/webpack');
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers');
AutoImport({ resolvers: [ElementPlusResolver()] }),
Components({ resolvers: [ElementPlusResolver()] })
App.vue
<el-button type='primary'>按钮</el-button>
2、element-plus自定义主题色
styles/element/index.scss
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': green
)
)
);
webpack.config.js
loaderName && {
loader: loaderName,
options:
loaderName === 'sass-loader'
? {
additionalData: `@use "@/styles/element/index.scss" as *;`
}
: {}
}
Components({
resolvers: [ElementPlusResolver({ importStyle: 'sass' /* 自定义主题 */ })]
})
resolve: {
alias: { '@': path.resolve(__dirname, '../src') }
}
3、开启打包缓存
{
test: /\.vue$/,
loader: 'vue-loader',
// 开启缓存
options: {
cacheDirectory: path.resolve(
__dirname,
'../node_modules/.cache/vue-loader'
)
}
}
4、将vue、element-plus模块打包成单独js文件
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(.*)[\\/]/,
chunks: 'initial',
priority: 20
},
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10, // 权重最低,优先考虑前面内容
chunks: 'initial'
}
}
}