vue.config配置
代码规范配置
使用 vue created 命令搭建一个基本的架子出来 , 把 vuex router 等都勾选上.
接下来打开项目开始进行配置:
我们先来配置一下这个代码规范的问题,在这里我们需要使用到的工具有:vetur,eslint,prettier,首先我们要在项目中安装一个包:@vue/prettier
yarn add -D @vue/eslint-config-prettier
安装好之后,在 .eslintrc.js 给它加上:
"extends": [
"plugin:vue/essential",
"eslint:recommended",
"@vue/prettier"
],
现在我们执行 npm run lint 的时候,可以看到 eslint 已经帮我们启动了代码规范, 但是很多东西都不能按照我们的想法来执行的,这个时候我们还需要做一些配置:
在项目的根目录下创建一个.prettierrc.js 文件,然后在其中加入:
module.exports = {
semi: false, //行位是否使用分号,默认为true
singleQuote: true, // 是否使用单引号
// bracketSpacing: true, //对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
// "printWidth": 80, //一行的字符数,如果超过会进行换行,默认为80
// "tabWidth": 2, //一个tab代表几个空格数,默认为80
// "useTabs": false, //是否使用tab进行缩进,默认为false,表示用空格进行缩减
// "trailingComma": "none", //是否使用尾逗号,有三个可选值"<none|es5|all>"
// parser: "babylon" //代码的解析引擎,默认为babylon,与babel相同。
};
到了这里,即时有上千个 VUE 文件,我们也可以通过 npm run lint 处理我们的错误规范代码
配置到这里我们已经可以实现代码规范了, 但是为了能够在 vscode 编辑器看到我们的错误规范,我们还需要装一个插件,这个插件就叫 Eslint。
安装好 eslint 后,因为 eslint 并不认识我们 vue 文件里面包含了 js 语法,所以我们还需要打开我们的 vscode 配置文件,这里的配置只是配置我们个人的文件,并没有达到团队的配置效果,所以我们要在项目中创建一个.vscode 文件夹,但是这里需要注意一个问题:.vscode 这个文件夹在.gitignore 文件里面,所以千万要记得把.vscode 删除
做好上面的一步之后,在.vscode 里面创建文件 settings.json 文件,添加如下代码:
{
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "vue",
"autoFix": true
}
]
}
上面的配置就是在保存的时候校验并修改我们的代码,它会自动帮我们整理代码规范
配置到这里其实已经结束了,但是因为我们安装了很多插件,例如 Prettier , 因为我们不只开发 vue 项目,可能还有其它类型的 js 项目特别是传统 js 项目,需要用到 prettier 进行美化,而 prettier 的一些功能是会和 eslint 相冲突的,比如说我们在全局设置了 prettier 的 formatOnSave,这个功能就会和 eslint 的 autoFixOnSave 打架,为了避免这个矛盾,我们通常还会在本项目的 settings.json 文件里再多加几个选项,类似于这样:
"editor.tabSize": 2,
"editor.formatOnSave": false,
"prettier.semi": false,
"prettier.singleQuote": true
有了这些设置,基本上 prettier 就不会和 eslint 打架了。
配置 git hook 以及提交规范
项目级安装:
如果想要在全局安装, 可以看这篇文章
npm install -D commitizen cz-conventional-changelog
然后在 package.json 中配置:
"script": {
"commit": "git-cz",
},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
}
}
如果全局安装过 commitizen, 那么在对应的项目中执行 git cz 或者 npm run commit 都可以。
注意: 如果没有全局安装 commitizen , git cz 是无法执行的。
走完上述操作后其实已经可以实现我们提交的规范了, 但是 Angular 这一套规范我们可能不太习惯, 那么可以通过指定 Adapter cz-customizable 指定一套符合自己团队的规范.
项目级安装 cz-customizable
npm i -D cz-customizable
然后修改 package.json 中的 config 为:
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
}
然后在项目目录下创建 .cz-config.js 文件 , 注明: 配置出自 GitHub: Leo Hui
'use strict';
module.exports = {
types: [
{
value: 'WIP',
name: '💪 WIP: Work in progress',
},
{
value: 'feat',
name: '✨ feat: A new feature',
},
{
value: 'fix',
name: '🐞 fix: A bug fix',
},
{
value: 'refactor',
name: '🛠 refactor: A code change that neither fixes a bug nor adds a feature',
},
{
value: 'docs',
name: '📚 docs: Documentation only changes',
},
{
value: 'test',
name: '🏁 test: Add missing tests or correcting existing tests',
},
{
value: 'chore',
name: "🗯 chore: Changes that don't modify src or test files. Such as updating build tasks, package manager",
},
{
value: 'style',
name: '💅 style: Code Style, Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)',
},
{
value: 'revert',
name: '⏪ revert: Revert to a commit',
},
],
scopes: [],
allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix'],
};
接下来我们再配置 校验 message 是否合法 , 如果不合法则拒绝提交.
项目级安装:
npm i -D @commitlint/config-conventional @commitlint/cli
同时需要在项目目录下创建配置文件 .commitlintrc.js, 写入示例配置:
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {},
};
如果您是自定义的 Adapter 那么您可能需要针对自定义的 Adapter 进行 Lint:
上述配置是使用了 符合 Angular 团队规范 的 Adapter , 而要根据我们自定义的 commitizen adapter , 则需要安装:
npm i -D commitlint-config-cz @commitlint/cli
.commitlintrc.js 中写入:
module.exports = {
extends: ['cz'],
rules: {},
};
结合 Husky:
校验 commit message 的最佳方式是结合 git hook, 所以需要配合 Husky.
这个插件是可以让我们在 git commit 之前 都执行一次 hook 脚本
npm install husky --save-dev
package.json 中添加:
"husky": {
"hooks": {
"pre-commit": "npm run lint",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
或者 创建 .huskyrc.js 添加:
module.exports = {
hooks: {
'pre-commit': 'npm run lint',
'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
},
};
配置后在后续的每一次 git commit 之前,都会执行一次对应的 hook 脚本 npm run lint 。其他 hook 同理
standard-version: 自动生成 CHANGELOG
通过以上工具的帮助, 我们的工程 commit message 应该是符合 Angular 团队那套, 这样也便于我们借助 standard-version 这样的工具, 自动生成 CHANGELOG, 甚至是 语义化的版本号(Semantic Version).
安装使用:
npm i -S standard-version
package.json 配置:
"scirpt": {
"release": "standard-version"
}
vue 国际化配置
我们使用 vue-i18n 来实现国际化。
首先当然是安装啦:
npm install vue-i18n -S
安装完毕后,写配置文件,我们先来完成语言包吧:
//新建中文语言包:zh.js
export default {
input: {
placeholder: '请输入用户名',
password: '请输入密码',
},
};
然后再新建英文语言包:
// en.js
export default {
input: {
placeholder: 'Please enter a user name',
password: 'Please enter your password',
},
};
这是 demo ,所以就弄这两种语言试试水。
接下来新建一个 i18n.js 配置文件。
// i18n.js
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import elementEnLocale from 'element-ui/lib/locale/lang/en'; // element-ui lang
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'; // element-ui lang
import enLocale from './lang/en';
import zhLocale from './lang/zh';
Vue.use(VueI18n);
const messages = {
en: {
...enLocale,
...elementEnLocale,
},
zh: {
...zhLocale,
...elementZhLocale,
},
};
const i18n = new VueI18n({
locale: localStorage.getItem('locale') || 'zh', // set locale
messages, // set locale messages
});
export default i18n;
然后我们再来配置 mian.js.
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './registerServiceWorker';
// 引入element ui框架
import ElementUI from 'element-ui';
// 引入element ui 样式文件
import 'element-ui/lib/theme-chalk/index.css';
// 引入阿里icon
import '../public/img/icons/iconfont.css';
// 如果是使用svg就要引入iconfont.js这个文件
import '../public/img/icons/iconfont';
// 国际化
import i18n from './common/i18n.js';
//全局注册组件
import './components/index';
//全局使用插件
Vue.use(ElementUI, {
size: 'medium', // set element-ui default size
i18n: (key, value) => i18n.t(key, value),
});
Vue.config.productionTip = false;
new Vue({
router,
store,
i18n, // 国际化
render: h => h(App),
}).$mount('#app');
入口文件这里其他的我就不解释了,主要讲国际化,把我们的国际化配置文件引进来,引进来之后,注入到 vue 的实例里面去,这里有一个很重要的点,为了让 element 的内部组件语言也改变,所以下面这段代码不能少:
//全局使用插件
Vue.use(ElementUI, {
size: 'medium', // set element-ui default size
i18n: (key, value) => i18n.t(key, value),
});
具体原因别问我,我不懂,因为我也踩了好久的坑,才百度到这个。
接下来就是使用了。
<template>
<div class="login">
<el-input :placeholder="$t('input.placeholder')" prefix-icon="icon-tubiao-15" v-model="input21" />
<el-input :placeholder="$t('input.password')" prefix-icon="icon-tubiao-15" v-model="input" />
<div class="lang">
<el-dropdown @command="handleCommand" size="small">
<span class="el-dropdown-link"> {{ locale }}<i class="el-icon-arrow-down el-icon--right"></i> </span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="zh-CN" :disabled="locale === '中文'">中文</el-dropdown-item>
<el-dropdown-item command="en-US" :disabled="locale === 'English'">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-date-picker v-model="value1" type="datetime" :placeholder="$t('input.placeholder')"> </el-date-picker>
</div>
</template>
<script>
export default {
name: 'login',
data() {
return {
input21: '',
input: '',
value1: '',
};
},
computed: {
locale() {
if (this.$i18n.locale === 'zh') {
return '中文';
} else {
return 'English';
}
},
},
methods: {
handleCommand(command) {
if (command == 'zh-CN') {
localStorage.setItem('locale', 'zh');
this.$i18n.locale = localStorage.getItem('locale');
console.log(this.$i18n.locale);
this.$message({
message: '切换为中文!',
type: 'success',
});
} else if (command == 'en-US') {
localStorage.setItem('locale', 'en');
this.$i18n.locale = localStorage.getItem('locale');
console.log(this.$i18n.locale);
this.$message({
message: 'Switch to English!',
type: 'success',
});
}
},
},
};
</script>
<style></style>
使用的话就是:
<el-input :placeholder="$t('input.placeholder')" prefix-icon="icon-tubiao-15" v-model="input21" /> // 属性里面这样用 <span>{{$t('input.placeholder')}}</span> // 标签内这样用。
这里面的值是我们写的语言包对象,key 是 input,value 是 placeholder,
至于我们如何来根据事件更换语言包呢?
直接改变它的值:this.$i18n.locale 就可以实现了,具体就是当你触发点击事件的时候,把这个值改变成你相应的语言包名字就可以了,注意,每次更换一点要把本地存储的值给覆盖哦。
handleCommand (command) {
if (command == 'zh-CN') {
localStorage.setItem('locale', 'zh')
this.$i18n.locale = localStorage.getItem('locale')
// console.log(this.$i18n.locale);
this.$message({
message: '切换为中文!',
type: 'success'
})
} else if (command == 'en-US') {
localStorage.setItem('locale', 'en')
this.$i18n.locale = localStorage.getItem('locale')
// console.log(this.$i18n.locale);
this.$message({
message: 'Switch to English!',
type: 'success'
})
}
}
这样国际化就实现啦~
这里再提一个 bug,但是我想不清楚应该怎么解决,只能临时找到个比较 bug 的方式
假如,我们有一个数组,也是需要配置国际化的,例如下拉框,看代码如下:
javascript<el-select v-model="value" :placeholder="$t('input.placeholder')">
<el-option v-for="item in options"
:key="item.id"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
data () {
return {
options: this.$t('input.options')
}
},
我们都知道,vue 更改数组是要使用 this.$set()方法才能更新视图的,但是,这里是直接从语言包里面拿到一个数据,当你切换语言包的时候,视图并不会更新,需要手动刷新一次才能够显示,我这里暂时的解决方案是直接在切换语言的时候在那个事件上调用:
this.$router.go(0);
但是用户体验真心不好,哪位大哥知道更好的方法,求加微信:294999978 指教,本人小白一枚,真心感谢
handleCommand (command) {
if (command == 'zh-CN') {
localStorage.setItem('locale', 'zh')
this.$i18n.locale = localStorage.getItem('locale')
console.log(this.$i18n.locale);
this.$router.go(0)// 刷新页面,为的就是触发视图
this.$message({
message: '切换为中文!',
type: 'success'
})
} else if (command == 'en-US') {
localStorage.setItem('locale', 'en')
this.$i18n.locale = localStorage.getItem('locale')
console.log(this.$i18n.locale);
this.$router.go(0) // 刷新页面,为的就是触发视图
this.$message({
message: 'Switch to English!',
type: 'success'
})
}
}
最后奉上项目的整体配置 , 以及源码的地址
const path = require('path');
let LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// 配置别名
function resolve(dir) {
return path.join(__dirname, dir);
}
// gzip压缩
const CompressionWebpackPlugin = require('compression-webpack-plugin');
// 代码压缩
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// 是否为生产环境
const isProduction = process.env.NODE_ENV !== 'development';
// 本地环境是否需要使用cdn
const devNeedCdn = false;
// cdn链接
const cdn = {
// cdn:模块名称和模块作用域命名(对应window里面挂载的变量名称)
externals: {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
'element-ui': 'ELEMENT',
axios: 'axios',
},
// cdn的css链接
css: ['https://unpkg.com/element-ui/lib/theme-chalk/index.css'],
// cdn的js链接
js: ['https://cdn.bootcss.com/vue/2.6.10/vue.min.js', 'https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js', 'https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js', 'https://unpkg.com/element-ui/lib/index.js', 'https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js'],
};
// 获取baseurl
function getProxy(path, type) {
if (path === '/api') {
switch (type) {
case 'localhost':
return 'http://106.52.38.245:9000/sys';
case 'service':
return 'http://106.52.38.245:9000/sys';
}
}
}
module.exports = {
productionSourceMap: false,
chainWebpack: config => {
config.resolve.alias.set('@', resolve('src')).set('@assets', resolve('src/assets')).set('@components', resolve('src/components')).set('@router', resolve('src/router')).set('@views', resolve('src/views')).set('@store', resolve('src/store')).set('@utils', resolve('src/utils')).set('@interface', resolve('src/http/interface'));
// ============压缩图片 start============
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options(
{
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: false },
pngquant: { quality: [0.65, 0.9], speed: 4 },
gifsicle: { interlaced: false },
// webp: { quality: 75 }
},
{ bypassOnDebug: true }
)
.end();
// ============压缩图片 end============
// ============注入cdn start============
config.plugin('html').tap(args => {
// 生产环境或本地需要cdn时,才注入cdn
if (isProduction || devNeedCdn) args[0].cdn = cdn;
// 修复 Lazy loading routes Error
args[0].chunksSortMode = 'none';
return args;
});
// ============注入cdn end============
// 修复HMR
config.resolve.symlinks(true);
// 移除 prefetch 插件不会加载其他路由文件,只加载当前路由文件
config.plugins.delete('prefetch');
// preload插件作用,暂时未知
config.plugins.delete('preload');
// 配置TypeScript
config.resolve.extensions
.add('.ts')
.add('.tsx')
.end()
.end()
.module.rule('typescript')
.test(/\.tsx?$/)
.use('babel-loader')
.loader('babel-loader')
.end()
.use('ts-loader')
.loader('ts-loader')
.options({
transpileOnly: true,
appendTsSuffixTo: ['\\.vue$'],
happyPackMode: false,
})
.end();
},
// 如果你不需要使用eslint,把lintOnSave设为false即可
lintOnSave: true,
css: {
sourceMap: false, // 开启 CSS source maps
extract: true, // 是否使用css分离插件 ExtractTextPlugin
requireModuleExtension: true, // false 会导致样式失效
loaderOptions: {
sass: {
prependData: `@import "@/assets/styles/varibles.scss";`,
},
},
},
devServer: {
// historyApiFallback: true // history 异步路由没有缓存在页面中,第一次进入页面会找不到 , 这个解决
// open: true,
// host: 'localhost',
// port: 8081,
// https: false,
// overlay: {
// warnings: true,
// errors: true
// },
// proxy: {
// 这里配置了 /sys 相当于就是 服务器的地址 . 例如: sys/login = http:localhost:8081/api/login
// '/sys': {
// target: getProxy('/api', process.env.VUE_APP_TYPE),
// ws: true,
// changOrigin: true, //允许跨域
// pathRewrite: {
// '^/sys': ''
// }
// }
// }
},
configureWebpack: config => {
// 生产环境相关配置
if (isProduction) {
// 代码压缩
config.plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
warnings: false, // 若打包错误,则注释这行
//生产环境自动删除console
compress: {
drop_debugger: true,
drop_console: true,
pure_funcs: ['console.log'],
},
},
sourceMap: false,
parallel: true,
})
);
// gzip压缩
const productionGzipExtensions = ['html', 'js', 'css'];
config.plugins.push(
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240, // 只有大小大于该值的资源会被处理 10240
minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
deleteOriginalAssets: false, // 删除原文件
})
);
// 公共代码抽离 构建优化上我们使用了 happypack 来利用多核CPU 加快打包的速度。
config.optimization = {
// https://webpack.js.org/plugins/split-chunks-plugin/
splitChunks: {
// minSize: 1000000, // 单个文件的最小size
// maxSize: 2000000, // 单个文件最大的size
// minChunks: 2, // 最小被引用
// maxAsyncRequests: 5, // 首页加载资源
// maxInitialRequests: 3,
// automaticNameDelimiter: '~', // 打包文件自定义的链接符
// name: true,
// chunks: 'async', // initial(初始块)、async(按需加载块)、all(默认,全部块)
// 这里需要注意的是如果使用initial 会将首页需要的依赖和项目本身的依赖打包2次增大文件体积
cacheGroups: {
default: false,
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
chunks: 'initial',
priority: 2,
reuseExistingChunk: true,
enforce: true,
},
common: {
name: 'chunk-common',
chunks: 'initial',
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
reuseExistingChunk: true,
enforce: true,
},
elementUI: {
name: 'chunk-elementui',
test: /[\\/]node_modules[\\/]element-ui[\\/]/,
chunks: 'all',
priority: 3,
reuseExistingChunk: true,
enforce: true,
},
echarts: {
name: 'chunk-echarts',
test: /[\\/]node_modules[\\/](vue-)?echarts[\\/]/,
chunks: 'all',
priority: 4,
reuseExistingChunk: true,
enforce: true,
},
vue: {
test(module) {
let path = module.resource;
if (!path) return false;
path = path.replace(/\\/g, '/');
// return path && path.indexOf('node_modules') > -1 && path.indexOf('vuetify') > -1
return path && /node_modules\/vue/.test(path);
},
name: 'chunk-vuetify',
priority: 9,
enforce: true,
},
styles: {
name: 'styles',
test: /\.(sa|sc|c)ss$/,
chunks: 'all',
enforce: true,
},
runtimeChunk: {
name: 'manifest',
},
},
},
};
// 可视化包大小
config.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8889,
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: false,
generateStatsFile: false,
statsFilename: 'stats.json',
statsOptions: null,
logLevel: 'info',
})
);
}
config.plugins.push(new LodashModuleReplacementPlugin());
// 用cdn方式引入,则构建时要忽略相关资源
if (isProduction || devNeedCdn) config.externals = cdn.externals;
// 取消webpack警告的性能提示
config.performance = {
hints: 'warning',
//入口起点的最大体积
maxEntrypointSize: 50000000,
//生成文件的最大体积
maxAssetSize: 30000000,
//只给出 js 文件的性能提示
assetFilter: function (assetFilename) {
return assetFilename.endsWith('.js');
},
};
// 配置TS
// config.resolve = { extensions: ['.ts', '.tsx', '.js', '.json'] }
// config.module = {
// rules: [
// {
// test: /.tsx?$/,
// loader: 'ts-loader',
// exclude: /node_modules/,
// options: {
// appendTsSuffixTo: [/.vue$/]
// }
// }
// ]
// }
},
// 第三方插件配置
pluginOptions: {},
};
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!