在 Vue2 项目开发中,合理的基础配置能显著提升开发效率、规范代码风格并增强项目可维护性。本文整理了 4 个高频实用配置:多语言(i18n)、SVG 图标组件、代码格式化(ESLint + Prettier)、权限控制指令、主题切换,附上可复用代码和配置说明,适用于大多数 Vue2 业务项目
一、多语言配置(vue-i18n)
多语言是中后台系统、国际化产品的核心需求,基于 vue-i18n 实现语言切换与文案管理,支持本地存储记忆语言偏好。
1.1 安装依赖
bash
# Vue2 需使用 vue-i18n@8.x 版本(9.x 仅支持 Vue3)
npm install vue-i18n@8 --save
1.2 完整配置代码
1.2.1 核心配置文件(src/i18n/index.js)
javascript
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import zhCN from './langs/zh-CN' // 中文文案
import enUS from './langs/en-US' // 英文文案
import { I18N_CONFIG, STORAGE_KEYS } from '@/constants' // 常量配置
// 注册 i18n 插件
Vue.use(VueI18n)
// 语言包集合(可扩展更多语言:如 ja-JP 日语、ko-KR 韩语)
const messages = {
'zh-CN': zhCN,
'en-US': enUS,
}
/**
* 从本地存储获取语言设置,无则使用默认语言
*/
const getLanguage = () => {
return localStorage.getItem(STORAGE_KEYS.LANGUAGE) || I18N_CONFIG.defaultLocale
}
// 创建 i18n 实例
const i18n = new VueI18n({
locale: getLanguage(), // 当前语言(优先本地存储)
messages, // 语言包
silentTranslationWarn: true, // 关闭「未找到翻译」警告(避免控制台冗余)
silentFallbackWarn: true, // 关闭「回退语言」警告
fallbackLocale: 'zh-CN', // 翻译缺失时的回退语言
})
export default i18n
1.2.2 英文语言包(src/i18n/langs/en-US.js)
js
export default {
route: {
dashboard: 'Dashboard',
system: 'System',
user: 'User',
role: 'Role',
menu: 'Menu',
monitor: 'Monitor',
job: 'Job',
login: 'Login',
error: 'Error',
404: '404',
401: '401',
profile: 'Profile',
}
}
1.2.3 中文语言包(src/i18n/langs/zh-CN.js)
js
export default {
route: {
dashboard: '首页',
system: '系统管理',
user: '用户管理',
role: '角色管理',
menu: '菜单管理',
monitor: '系统监控',
job: '定时任务',
login: '登录',
error: '错误页面',
404: '404',
401: '401',
profile: '个人中心',
}
}
1.3 配套常量与使用方式
- 常量配置(src/constants/index.js):定义默认语言、存储键名
- 语言包示例:按模块划分文案(如 common、user、menu)
- 全局注册:在 main.js 注入 i18n 实例
- 使用方式:模板中
{{ $t('common.confirm') }},脚本中this.$t('common.success')
二、SVG 图标组件(svg-sprite-loader)
使用 SVG 图标比图片图标更清晰、可缩放,配合 svg-sprite-loader 实现按需加载,封装通用 SvgIcon 组件。
2.1 安装依赖
bash
npm install svg-sprite-loader --save-dev
2.2 Webpack 配置(vue.config.js)
javascript
const path = require('path')
// 路径解析辅助函数
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
chainWebpack: config => {
// 1. 清除默认的 svg 处理规则(避免与 svg-sprite-loader 冲突)
config.module.rules.delete('svg')
// 2. 配置 svg-sprite-loader 处理 src/icons 目录下的 SVG
config.module
.rule('icons')
.test(/.svg$/) // 匹配 svg 文件
.include.add(resolve('src/icons')) // 只处理 src/icons 目录
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]' // 生成的 symbol id 格式:icon-文件名
})
.end()
}
}
2.3 通用 SVG 组件(src/components/SvgIcon/index.vue)
vue
<template>
<!-- 外部 SVG 图标(http/https 链接) -->
<div
v-if="isExternal"
:style="styleExternalIcon"
class="svg-external-icon svg-icon"
v-on="$listeners"
/>
<!-- 内部 SVG 图标(src/icons 目录下) -->
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" /> <!-- 关联 svg-sprite 的 symbol id -->
</svg>
</template>
<script>
export default {
name: 'SvgIcon', // 组件名(必须,便于注册和调试)
props: {
iconClass: {
type: String,
required: true, // 图标名称(必填)
},
className: {
type: String,
default: '', // 额外类名(用于自定义样式)
},
},
computed: {
// 判断是否为外部 SVG 图标(http/https/mailto/tel 开头)
isExternal() {
return /^(https?:|mailto:|tel:)/.test(this.iconClass)
},
// 内部图标:拼接 symbol id(与 webpack 配置的 symbolId 一致)
iconName() {
return `#icon-${this.iconClass}`
},
// 拼接最终的类名
svgClass() {
return this.className ? `svg-icon ${this.className}` : 'svg-icon'
},
// 外部图标样式(通过 mask 实现 SVG 效果)
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`,
maskSize: 'cover',
'-webkit-mask-size': 'cover'
}
},
},
}
</script>
<style scoped>
/* 基础 SVG 样式:继承父元素颜色(fill: currentColor) */
.svg-icon {
width: 1em;
height: 1em;
fill: currentColor;
overflow: hidden;
vertical-align: middle; /* 对齐文字 */
}
/* 外部 SVG 图标样式 */
.svg-external-icon {
background-color: currentColor;
display: inline-block;
}
</style>
2.4 自动导入与使用
js
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'
// 注册全局组件
Vue.component('svg-icon', SvgIcon)
// 自动导入所有 SVG 图标
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => {
const files = requireContext.keys()
files.forEach(key => {
const name = key.replace(/^\.\/(.*)\.\w+$/, '$1')
const component = requireContext(key)
})
return files.map(requireContext)
}
requireAll(req)
- 图标自动导入(src/icons/index.js):批量导入目录下所有 SVG
- 全局注册:在 main.js 注册
SvgIcon组件 - 使用方式:
<svg-icon icon-class="user" className="text-red-500" />
三、代码格式化配置(ESLint + Prettier)
统一代码风格,减少团队协作冲突,自动修复格式问题。
3.1 安装依赖
bash
npm install eslint prettier eslint-plugin-vue eslint-config-prettier eslint-plugin-prettier @babel/eslint-parser --save-dev
3.2 核心配置文件
3.2.1 Prettier 配置(.prettierrc)
json
{
"semi": false, // 不加分号
"singleQuote": true, // 使用单引号
"printWidth": 100, // 每行最大长度(超过自动换行)
"tabWidth": 2, // 缩进 2 个空格
"useTabs": false, // 不使用制表符(Tab)
"trailingComma": "es5", // 对象/数组末尾加逗号(ES5 兼容)
"bracketSpacing": true, // 对象字面量前后加空格({ foo: bar })
"arrowParens": "avoid", // 箭头函数参数只有一个时不加括号(x => x)
"vueIndentScriptAndStyle": true, // Vue 组件中 script 和 style 缩进与 template 一致
"htmlWhitespaceSensitivity": "ignore" // HTML 空格不敏感
}
3.2.2 ESLint 配置(.eslintrc.js)
javascript
module.exports = {
root: true, // 标识为根配置
env: {
node: true,
jest: true,
browser: true
},
extends: [
'plugin:vue/essential',
'eslint:recommended',
'plugin:prettier/recommended'
],
parserOptions: {
parser: '@babel/eslint-parser',
requireConfigFile: false,
ecmaVersion: 2020,
sourceType: 'module'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-unused-vars': [
'warn',
{
vars: 'all',
args: 'after-used',
ignoreRestSiblings: true,
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}
]
}
}
3.3 脚本与使用
- 配置脚本(package.json):添加
lint和lint:fix命令 - 忽略文件:.prettierignore + .eslintignore 排除无需格式化的文件
- 使用方式:
npm run lint检查格式,npm run lint:fix自动修复
四、权限控制指令(v-permission)
基于用户权限动态显示 / 隐藏 DOM 元素,适用于按钮、菜单等权限控制场景。
4.1 指令实现(src/directives/permission.js)
javascript
import store from '@/store' // Vuex 存储用户权限信息
export default {
// 指令绑定到元素时执行
inserted(el, binding) {
checkPermission(el, binding)
},
// 指令所在组件更新时执行(如权限变化后)
update(el, binding) {
checkPermission(el, binding)
},
}
/**
* 权限校验核心逻辑
* @param el 指令绑定的 DOM 元素
* @param binding 指令绑定信息(value 为权限标识数组)
*/
function checkPermission(el, binding) {
const { value } = binding
// 1. 校验权限标识格式(必须是数组)
if (!value || !(value instanceof Array) || value.length === 0) {
console.warn("权限指令使用错误!需指定权限标识数组,例如:v-permission="['admin','editor']"")
el.parentNode?.removeChild(el) // 格式错误时隐藏元素
return
}
// 2. 获取用户权限和角色(从 Vuex 中获取)
const userPermissions = store.getters['user/permissions'] // 权限列表(如 ['user:list'])
const userRoles = store.getters['user/roles'] // 角色列表(如 ['admin'])
// 3. 权限判断:拥有任一所需权限 或 是管理员 → 显示元素
const hasPermission = userPermissions.some(perm => value.includes(perm)) || userRoles.includes('admin')
// 4. 无权限时移除元素
if (!hasPermission) {
el.parentNode?.removeChild(el)
}
}
4.2 全局注册与使用
- 注册指令(src/directives/index.js):全局注册
v-permission - 导入指令:在 main.js 引入
- 使用方式:
<el-button v-permission="['admin','user:edit']">编辑</el-button>
五、主题色配置(支持多主题切换)
基于 CSS 变量 + SCSS 实现主题切换,适配 Element UI 组件样式,支持默认、暗黑、蓝色三种主题,可扩展自定义主题。
5.1 主题配置常量(src/constants/theme.js)
javascript
// 主题配置:定义各主题的颜色变量
export const themes = {
default: {
// 主色调
primaryColor: '#409EFF',
// 功能色
successColor: '#67C23A',
warningColor: '#E6A23C',
dangerColor: '#F56C6C',
infoColor: '#909399',
// 基础色
textColor: '#303133',
borderColor: '#DCDFE6',
backgroundColor: '#FFFFFF',
// 布局色
sidebarBgColor: '#ffffff',
sidebarTextColor: '#303133',
sidebarActiveTextColor: '#409EFF',
},
dark: {
primaryColor: '#409EFF',
successColor: '#67C23A',
warningColor: '#E6A23C',
dangerColor: '#F56C6C',
infoColor: '#909399',
textColor: '#E5EAF3',
borderColor: '#4C4D4F',
backgroundColor: '#141414',
sidebarBgColor: '#1f1f1f',
sidebarTextColor: '#bfcbd9',
sidebarActiveTextColor: '#409EFF',
},
blue: {
primaryColor: '#1890ff',
successColor: '#52c41a',
warningColor: '#faad14',
dangerColor: '#f5222d',
infoColor: '#909399',
textColor: '#303133',
borderColor: '#DCDFE6',
backgroundColor: '#FFFFFF',
sidebarBgColor: '#001529',
sidebarTextColor: '#bfcbd9',
sidebarActiveTextColor: '#1890ff',
},
}
// 主题列表(用于下拉选择框)
export const themeList = [
{ label: '默认主题', value: 'default' },
{ label: '暗黑主题', value: 'dark' },
{ label: '蓝色主题', value: 'blue' }
]
// 从本地存储获取当前主题(无则默认)
export function getTheme() {
return localStorage.getItem('theme') || 'default'
}
// 设置并应用主题
export function setTheme(theme) {
localStorage.setItem('theme', theme)
applyTheme(theme)
}
// 应用主题核心逻辑:设置 CSS 变量 + 切换 body 类名
export function applyTheme(theme) {
const themeConfig = themes[theme]
if (!themeConfig) return
// 1. 移除所有主题类,添加当前主题类
document.body.classList.remove('theme-default', 'theme-dark', 'theme-blue')
document.body.classList.add(`theme-${theme}`)
// 2. 设置全局 CSS 变量(供组件使用)
Object.keys(themeConfig).forEach(key => {
document.documentElement.style.setProperty(`--${key}`, themeConfig[key])
})
}
5.2 SCSS 主题样式(src/styles/theme.scss)
s
@use './variables.scss' as *;
@use './mixin.scss' as *;
// 全局 CSS 变量(默认值)
:root {
--primaryColor: #409eff;
--successColor: #67c23a;
--warningColor: #e6a23c;
--dangerColor: #f56c6c;
--infoColor: #909399;
--textColor: #303133;
--borderColor: #dcdfe6;
--backgroundColor: #f5f7fa;
--sidebarBgColor: #304156;
--sidebarTextColor: #bfcbd9;
--sidebarActiveTextColor: #409eff;
}
// -------------------------- 主题专属样式 --------------------------
// 默认主题
.theme-default {
// 按钮样式
.el-button--primary {
background-color: #409eff;
border-color: #409eff;
&:hover {
background-color: #66b1ff;
border-color: #66b1ff;
}
&:active {
background-color: #3a8ee6;
border-color: #3a8ee6;
}
}
// 表格样式
.el-table {
background-color: #fff;
th {
background-color: #f5f7fa;
color: #303133;
font-weight: 500;
}
td {
color: #606266;
}
tr:hover > td {
background-color: rgba(64, 158, 255, 0.05) !important;
}
.el-table__row--striped {
background-color: #fafafa;
}
}
}
// 暗黑主题(重点适配 Element UI 组件)
.theme-dark {
// 卡片样式
.el-card {
background-color: #1f1f1f;
border-color: #4c4d4f;
color: #a8abb2;
.el-card__header {
border-bottom-color: #4c4d4f;
color: #e5eaf3;
}
}
// 表格样式
.el-table {
background-color: #1f1f1f;
color: #e5eaf3;
th {
background-color: rgba(#141414, 0.6) !important;
color: #e5eaf3;
}
td {
border-bottom: 1px solid rgba(#4c4d4f, 0.1);
color: #a8abb2;
}
tr:hover > td {
background-color: rgba(255, 255, 255, 0.05) !important;
}
}
// 输入框样式
.el-input__inner {
background-color: #1f1f1f;
border-color: #4c4d4f;
color: #a8abb2;
&:hover {
border-color: #606266;
}
&:focus {
border-color: #409eff;
}
}
// 对话框、下拉框、提示框等组件样式(完整代码见上文)
}
// 蓝色主题
.theme-blue {
.el-button--primary {
background-color: #1890ff;
border-color: #1890ff;
&:hover {
background-color: #40a9ff;
border-color: #40a9ff;
}
}
.el-menu-item.is-active {
background-color: rgba(24, 144, 255, 0.1) !important;
}
}
// -------------------------- 全局通用样式 --------------------------
body {
color: var(--textColor);
background-color: var(--backgroundColor);
transition: background-color 0.3s ease; // 平滑过渡
}
// 侧边栏样式
.sidebar-container {
background-color: var(--sidebarBgColor);
.el-menu {
background-color: var(--sidebarBgColor);
.el-menu-item,
.el-submenu__title {
color: var(--sidebarTextColor);
&.is-active {
color: var(--sidebarActiveTextColor);
}
}
}
}
// 按钮、表单、卡片等全局样式(使用 CSS 变量,自动适配主题)
.el-button--primary {
background-color: var(--primaryColor);
border-color: var(--primaryColor);
}
.el-input__inner {
color: var(--textColor);
background-color: var(--backgroundColor);
border-color: var(--borderColor);
}
.el-card {
background-color: var(--backgroundColor);
border-color: var(--borderColor);
}
5.3 主题初始化与切换
5.3.1 初始化主题(main.js)
javascript
import Vue from 'vue'
import App from './App.vue'
import { getTheme, applyTheme } from '@/constants/theme'
import '@/styles/theme.scss' // 导入主题样式
// 初始化主题(页面加载时执行)
applyTheme(getTheme())
new Vue({
el: '#app',
render: h => h(App)
})
5.3.2 主题切换组件(示例)
vue
<template>
<el-select
v-model="currentTheme"
placeholder="选择主题"
size="mini"
@change="handleThemeChange"
>
<el-option
v-for="item in themeList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</template>
<script>
import { getTheme, setTheme, themeList } from '@/constants/theme'
export default {
data() {
return {
themeList,
currentTheme: getTheme()
}
},
methods: {
handleThemeChange(theme) {
setTheme(theme) // 切换并保存主题
this.currentTheme = theme
this.$message.success(`已切换至${themeList.find(item => item.value === theme).label}`)
}
}
}
</script>
5.4 主题适配说明
- CSS 变量优先级 :主题切换时通过
document.documentElement.style.setProperty动态修改 CSS 变量,覆盖默认值; - Element UI 适配 :通过主题类(如
.theme-dark)针对性修改 Element 组件样式,解决第三方组件主题兼容问题; - 平滑过渡 :在 body 或核心容器添加
transition: background-color 0.3s ease,实现主题切换时的视觉过渡; - 扩展性 :新增主题时,只需在
themes常量中添加配置,在theme.scss中补充专属样式即可。