一份完整的vue-cli3项目基础配置项
安装依赖
cnpm install
开发模式
npm run dev
打包测试环境
npm run test
测试和生产一起打包
npm run publish
打包生产环境
npm run build
完整代码
项目配置功能
- 配置全局cdn,包含js、css
- 开启Gzip压缩,包含文件js、css
- 去掉注释、去掉console.log
- 压缩图片
- 本地代理
- 设置别名,vscode也能识别
- 配置环境变量开发模式、测试模式、生产模式
- 请求路由动态添加
- axios配置
- 添加mock数据
- 配置全局less
- 只打包改变的文件
- 开启分析打包日志
- 拷贝文件
附加功能
- vue如何刷新当前页面
目录结构
├── public 静态模板资源文件
├── src 项目文件
├──|── assets 静态文件 img 、css 、js
├──|── components 全局组件
├──|── http 请求配置
├──|── layout 布局文件
├──|── mock 测试数据
├──|── modules 放置动态是添加路由的页面
├──|── router 路由
├──|── store vuex数据管理
├──|── utils 工具文件
├──|── view 页面文件
├──|── App.vue
├──|── main.js
├── .env.development 开发模式配置
├── .env.production 正式发布模式配置
├── .env.test 测试模式配置
├── entrance.js 入口文件
├── vue.config.js config配置文件
vue.config.js
完整的架构配置
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 去掉注释
const CompressionWebpackPlugin = require('compression-webpack-plugin'); // 开启压缩
const { HashedModuleIdsPlugin } = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
function resolve(dir) {
return path.join(__dirname, dir)
}
const isProduction = process.env.NODE_ENV === 'production';
// cdn预加载使用
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
"element-ui": "ELEMENT"
}
const cdn = {
// 开发环境
dev: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],
js: []
},
// 生产环境
build: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],
js: [
'https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/vue-router@3.0.1/dist/vue-router.min.js',
'https://cdn.jsdelivr.net/npm/vuex@3.0.1/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js',
'https://unpkg.com/element-ui/lib/index.js'
]
}
}
module.exports = {
lintOnSave: false, // 关闭eslint
productionSourceMap: false,
publicPath: './',
outputDir: process.env.outputDir, // 生成文件的目录名称
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
// 压缩图片
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({ bypassOnDebug: true })
// webpack 会默认给commonChunk打进chunk-vendors,所以需要对webpack的配置进行delete
config.optimization.delete('splitChunks')
config.plugin('html').tap(args => {
if (process.env.NODE_ENV === 'production') {
args[0].cdn = cdn.build
}
if (process.env.NODE_ENV === 'development') {
args[0].cdn = cdn.dev
}
return args
})
config
.plugin('webpack-bundle-analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
},
configureWebpack: config => {
const plugins = [];
if (isProduction) {
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注释
},
warnings: false,
compress: {
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log'] //移除console
}
}
})
)
// 服务器也要相应开启gzip
plugins.push(
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css)$/, // 匹配文件名
threshold: 10000, // 对超过10k的数据压缩
deleteOriginalAssets: false, // 不删除源文件
minRatio: 0.8 // 压缩比
})
)
// 用于根据模块的相对路径生成 hash 作为模块 id, 一般用于生产环境
plugins.push(
new HashedModuleIdsPlugin()
)
// 拷贝文件到指定目录,比如一些网站在根目录需要验证某些文本, from为文件的路径,还有一个to属性是输出的文件夹路径,不写则默认复制到打包后文件的根目录
plugins.push(
new CopyWebpackPlugin([{ from: './NLwdLAxhwv.txt'}])
)
// 开启分离js
config.optimization = {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 1000 * 60,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// 排除node_modules 然后吧 @ 替换为空 ,考虑到服务器的兼容
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]
return `npm.${packageName.replace('@', '')}`
}
}
}
}
};
// 取消webpack警告的性能提示
config.performance = {
hints: 'warning',
//入口起点的最大体积
maxEntrypointSize: 1000 * 500,
//生成文件的最大体积
maxAssetSize: 1000 * 1000,
//只给出 js 文件的性能提示
assetFilter: function(assetFilename) {
return assetFilename.endsWith('.js');
}
}
// 打包时npm包转CDN
config.externals = externals;
}
return { plugins }
},
pluginOptions: {
// 配置全局less
'style-resources-loader': {
preProcessor: 'less',
patterns: [resolve('./src/style/theme.less')]
}
},
devServer: {
open: false, // 自动启动浏览器
host: '0.0.0.0', // localhost
port: 6060, // 端口号
https: false,
hotOnly: false, // 热更新
proxy: {
'^/sso': {
target: process.env.VUE_APP_SSO, // 重写路径
ws: true, //开启WebSocket
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true
}
}
}
}
html模板配置cdn
<!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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<% for (var i in
htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<% for (var i in
htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
html模板配置cdn
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
// cdn预加载使用
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter'
}
const cdn = {
// 开发环境
dev: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],
js: []
},
// 生产环境
build: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],
js: [
'https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/vue-router@3.0.1/dist/vue-router.min.js'
]
}
}
chainWebpack: config => {
config.plugin('html').tap(args => {
if (process.env.NODE_ENV === 'production') {
args[0].cdn = cdn.build
}
if (process.env.NODE_ENV === 'development') {
args[0].cdn = cdn.dev
}
return args
})
}
开启Gzip压缩,包含文件js、css
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css)$/, // 匹配文件名
threshold: 10000, // 对超过10k的数据压缩
deleteOriginalAssets: false, // 不删除源文件
minRatio: 0.8 // 压缩比
})
去掉注释、去掉console.log
安装cnpm i uglifyjs-webpack-plugin -D
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注释
},
warnings: false,
compress: {
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log'] //移除console
}
}
})
压缩图片
chainWebpack: config => {
// 压缩图片
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({ bypassOnDebug: true })
}
本地代理
devServer: {
open: false, // 自动启动浏览器
host: '0.0.0.0', // localhost
port: 6060, // 端口号
https: false,
hotOnly: false, // 热更新
proxy: {
'^/sso': {
target: process.env.VUE_APP_SSO, // 重写路径
ws: true, //开启WebSocket
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true
}
}
}
设置vscode 识别别名
在vscode中插件安装栏搜索 Path Intellisense 插件,打开settings.json文件添加 以下代码 "@": "${workspaceRoot}/src",安以下添加
{
"workbench.iconTheme": "material-icon-theme",
"editor.fontSize": 16,
"editor.detectIndentation": false,
"guides.enabled": false,
"workbench.colorTheme": "Monokai",
"path-intellisense.mappings": {
"@": "${workspaceRoot}/src"
}
}
在项目package.json所在同级目录下创建文件jsconfig.json
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": [
"node_modules"
]
}
如果还没看懂的客官请移步在vscode中使用别名@按住ctrl也能跳转对应路径
配置环境变量开发模式、测试模式、生产模式
在根目录新建
.env.development
# 开发环境
NODE_ENV='development'
VUE_APP_SSO='http://http://localhost:9080'
.env.test
NODE_ENV = 'production' # 如果我们在.env.test文件中把NODE_ENV设置为test的话,那么打包出来的目录结构是有差异的
VUE_APP_MODE = 'test'
VUE_APP_SSO='http://http://localhost:9080'
outputDir = test
.env.production
NODE_ENV = 'production'
VUE_APP_SSO='http://http://localhost:9080'
package.json
"scripts": {
"build": "vue-cli-service build", //生产打包
"lint": "vue-cli-service lint",
"dev": "vue-cli-service serve", // 开发模式
"test": "vue-cli-service build --mode test", // 测试打包
"publish": "vue-cli-service build && vue-cli-service build --mode test" // 测试和生产一起打包
}
请求路由动态添加
在router/index.js,核心
router.beforeEach((to, from, next) => {
const { hasRoute } = store.state; // hasRoute设置一个状态,防止重复请求添加路由
if (hasRoute) {
next()
} else {
dynamicRouter(to, from, next, selfaddRoutes)
}
})
如果动态添加路由抛警告,路由重复添加,需要添加 router.matcher = new VueRouter().matcher
axios配置
在http/request.js
import http from './src/http/request'
Vue.prototype.$http = http;
// 使用
this.$http.windPost('url','参数')
添加mock数据
const Mock = require('mockjs')
const produceNewsData = []
Mock.mock('/mock/menu', produceNewsData)
Mock支持随机数据,具体参看官网列子 mockjs.com/examples.ht…
配置全局less
pluginOptions: {
// 配置全局less
'style-resources-loader': {
preProcessor: 'less',
patterns: [resolve('./src/style/theme.less')]
}
}
只打包改变的文件
安装cnpm i webpack -D
const { HashedModuleIdsPlugin } = require('webpack');
configureWebpack: config => {
const plugins = [];
plugins.push(
new HashedModuleIdsPlugin()
)
}
开启分析打包日志
安装cnpm i webpack-bundle-analyzer -D
chainWebpack: config => {
config
.plugin('webpack-bundle-analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
}
拷贝文件
安装npm i copy-webpack-plugin -D
const CopyWebpackPlugin = require('copy-webpack-plugin');
configureWebpack: config => {
const plugins = [];
plugins.push(
new CopyWebpackPlugin([{ from: './NLwdLAxhwv.txt'}])
)
}
from为文件的路径,还有一个to属性是输出的文件夹路径,不写则默认复制到打包后文件的根目录
vue如何刷新当前页面
刷新当前页面适合在只改变了路由的id的页面,比如查看详情页面,当路由id发生时候,并不会去触发当前页面的钩子函数
查看App.vue
<template>
<div class="app">
<router-view v-if="isRouterAlive"></router-view>
</div>
</template>
<script>
export default {
name: "App",
provide() {
return {
reload: this.reload
};
},
data() {
return {
isRouterAlive: true
};
},
methods: {
// 重载页面 适合添加数据或者路由id改变
reload() {
this.isRouterAlive = false;
this.$nextTick(()=>{
this.isRouterAlive = true;
});
}
}
};
</script>
然后其它任何想刷新自己的路由页面,都可以这样: this.reload()
如有疑问
我们这里沟通github