🔥🔥🔥 Vue3.0 + Vite2 + Element-Plus 搭建后台管理模版

5,654 阅读7分钟

建议开发环境

一.创建Vite2项目

官方文档:vite2,兼容性注意: Vite 需要 Node.js 版本 >= 12.0.0


npm init @vitejs/app #or yarn create @vitejs/app

我们可以通过附加的命令行选项直接指定项目名称和你想要使用的模板,可以直接生成项目


# npm 6.x

npm init @vitejs/app my-vue-app --template vue



# npm 7+, 需要额外的双横线:

npm init @vitejs/app my-vue-app -- --template vue



# yarn

yarn create @vitejs/app my-vue-app --template vue

二.安装Element-plus

官方文档地址:element-plus ,具体配置:快速开始


npm install element-plus -S #or yarn add element-plus -S

完整引入

main.js中写入


import { createApp } from 'vue'

import ElementPlus from 'element-plus';

import 'element-plus/lib/theme-chalk/index.css';

import App from './App.vue';


const app = createApp(App)

app.use(ElementPlus)

app.mount('#app')

按需加载

首先,安装 vite-plugin-style-import:


#npm

npm install vite-plugin-style-import -D

# yarn

yarn add vite-plugin-style-import -D

然后,将 vite.config.js 修改为:

引入 .scss 样式


import { defineConfig } from 'vite'

import vue from '@vitejs/plugin-vue'

import styleImport from 'vite-plugin-style-import'


export default defineConfig({

plugins: [

vue(),

styleImport({

libs: [{

libraryName: 'element-plus',

esModule: true,

ensureStyleFile: true,

resolveStyle: (name) => {

name = name.slice(3)

return `element-plus/packages/theme-chalk/src/${name}.scss`;

},

resolveComponent: (name) => {

return `element-plus/lib/${name}`;

},

}]

})

]

})

main.js引入所需的组件即可:


import { createApp } from 'vue'

import { ElButton, ElSelect, ElRow, ElCol } from 'element-plus';

// 如果要使用.scss样式文件,则需要引入base.scss文件

import 'element-plus/packages/theme-chalk/src/base.scss'

import App from './App.vue'

const app = createApp(App)

app.component(ElButton.name, ElButton);

app.component(ElSelect.name, ElSelect);

app.component(ElRow.name, ElRow);

app.component(ElCol.name, ElCol);


app.mount('#app')

Eelement Plus 按需加载优化

src目录下创建plugin文件,并创建Element按需加载文件el-comp.js


import {

ElAlert,

ElAside,

ElButton,

ElSelect,

ElRow,

ElCol,

ElForm,

ElFormItem,

ElInput,

ElTabs,

ElTabPane,

ElCheckbox,

ElIcon,

ElDivider,

} from 'element-plus';


// 所需的组件

export const components = [

ElAlert,

ElAside,

ElButton,

ElSelect,

ElRow,

ElCol,

ElForm,

ElFormItem,

ElInput,

ElTabs,

ElTabPane,

ElCheckbox,

ElIcon,

ElDivider,

];



// 注册

export default (app) => {

components.forEach((component) => {

app.component(component.name, component);

});

};

然后在main.js里使用,删除之前element按需加载的代码


// main.js

import { createApp } from 'vue';

import elementPlus from './plugin/el-comp';

import 'element-plus/packages/theme-chalk/src/base.scss';

import App from './App.vue';

const app = createApp(App);

// 注册 elementPlus组件/插件

elementPlus(app);

三.安装sass预编译

Element-plus样式基于sass开发的,所以建议选择安装sass保持一致


# npm

npm install sass -S

# or yarn

yarn add sass -S

src目录下创建styles样式文件夹,存放样式文件,参考目录结构如下:


|-- src

|-- styles

|-- common.scss // 公共样式文件

|-- variable.scss // 样式变量文件

|-- index.scss // 导入合并文件

|-- reset.scss // 重置浏览器默认样式文件

其中index.scss代码参考


@import './common.scss';

@import './reset.scss';

@import './variable.scss';

scss文件全局引入

平常开发中会用到一些公共样式或者变量,使用时每个页面单独引入,会比较麻烦。使用如下方式可以解决此问题,还有其他方式,请自行查找

vite.config.js文件新增以下代码


export default defineConfig({

// ...

css: {

preprocessorOptions: {

// 引入公用的样式

scss: {

// styles/index.scss 样式文件 在src目录下

additionalData: `@use "@/styles/index.scss" as *;`

}

}

}

}

样式自动加前缀

安装autoprefixerpostcss


# npm

npm install autoprefixer postcss -D

# yarn

yarn add autoprefixer postcss -D

然后创建postcss.config.js文件


// postcss.config.js

module.exports = {

plugins: [

require('autoprefixer')

]

}

修改vite.config.js代码


// vite.config.js

export default defineConfig({

css: {

postcss: {

plugins: [

require('autoprefixer')

]

}

}

})

四.代码规范EsLint

具体配置点击详细配置文档地址

我们借助eslint规范项目代码,方面多人开发,和阅读代码。首先安装相关依赖


# npm

npm install eslint babel-eslint eslint-plugin-prettier eslint-plugin-vue prettier @vue/eslint-config-prettier -D

# yarn

yarn add eslint babel-eslint eslint-plugin-prettier eslint-plugin-vue prettier @vue/eslint-config-prettier -D

然后在项目根目录下创建.eslintrc.js文件,内容如下:


module.exports = {

root: true,

env: {

node: true,

},

extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/prettier'],

parserOptions: {

parser: 'babel-eslint',

},

rules: {

'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',

'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',

'prettier/prettier': [

'warn',

{

printWidth: 80, // 每行代码长度(默认80)

tabWidth: 2, // 每个tab相当于多少个空格(默认2)

useTabs: false, // 是否使用tab进行缩进(默认false)

semi: false, // 使用单引号(默认false)

singleQuote: true, // 声明结尾使用分号(默认true)

quoteProps: 'as-needed',

jsxSingleQuote: false,

trailingComma: 'es5', // 多行使用拖尾逗号(默认none)

bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true)

jsxBracketSameLine: false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false)

arrowParens: 'avoid', // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid)

htmlWhitespaceSensitivity: 'ignore',

vueIndentScriptAndStyle: true,

endOfLine: 'lf',

},

],

},

}

如果某些文件不需要eslint校验,我们需要在根目录创建.eslintignore文件来忽略这些文件


src/assets

src/icons

public

dist

node_modules

五.安装vue-router 4.x


# npm

npm install vue-router@next -S

# or yarn

yarn add vue-router@next -S

src目录下创建src/router/index.js路由配置文件

注意:需要引入完整的文件名(.vue),vite不建议忽略后缀名, views为别名配置,参考vite-config.js文件中别名配置


import { createRouter, createWebHashHistory } from 'vue-router';


const router = createRouter({

history: createWebHashHistory(),

routes: [

{

path: '/',

redirect: '/index'

},

{

path: '/index',

name: 'Index',

// 注意引入完整的文件名(.vue),vite不建议忽略后缀名

component: () => import('views/index/index.vue'),

meta: {

title: '首页'

}

},

]

});



export default router

六.安装vuex4.x


# npm

npm install vuex@next -S

# or yarn

yarn add vuex@next -S

vuex工程化配置

首先在src目录下创建store文件夹,完整目录如下:


|-- src

|-- store

|-- modules // 模块文件夹

|-- user.js // 用户模块

|-- getters.js // getter 文件

|-- index.js // vuex文件入口

使用案例

  • store/modules/user.js 用户模块,定义statemutations, actions

const state = {

userInfo: {},

token: 'VUEX_TOKEN'

}

const mutations = {

SET_USERINFO: (state, userInfo) => {

state.userInfo = userInfo

}

}

const actions = {

setUserInfo: ({commit}, userInfo) => {

commit('SET_USERINFO', userInfo)

}

}


export default {

state,

mutations,

actions

}

  • store/getters.js,需要全局的变量通过getter方式获取

const getters = {

    userInfo: (state) => state.user.userInfo,

    token: (state) => state.user.token,

};

export default getters;

  • store/index.js,vuex入口文件,通过import.meta.globEager获取modules下所有模块文件,无需每个文件单独导入

import { createStore } from 'vuex'

import getters from './getters.js'


// https://vitejs.dev/guide/features.html#glob-import

const modulesFiles = import.meta.globEager('./modules/*.js')


let modules = {}

for (const path in modulesFiles) {

const moduleName = path.replace(/(.*\/)*([^.]+).*/gi, '$2')

modules[moduleName] = modulesFiles[path].default

}


const store = new createStore({

modules,

getters,

})


export default store

main.js中使用


import store from "@/store";

import App from './App.vue'

import store from "@/store";


const app = createApp(App)

app.use(store)

app.mount('#app')

七.安装axios


# npm

npm install axios -S

# yarn

yarn add axios -S

完成安装后,在src目录下创建src/config/net.config.js 文件

vite export default {} 导出会报错,不知道啥原因,提示建议带名导出 如:export const netConfig = {...}


// net.config.js

/**

* @description 配置axios请求基础信息

* @author hu-snail 1217437592@qq.com

*/


export const netConfig = {

// axios 基础url地址

baseURL:

process.env.NODE_ENV === 'development'

? 'https://xxx/api'

: 'https://xxx/api',

// 为开发服务器配置 CORS。默认启用并允许任何源,传递一个 选项对象 来调整行为或设为 false 表示禁用

cors: true,

// 根据后端定义配置

contentType: 'application/json;charset=UTF-8',

//消息框消失时间

messageDuration: 3000,

//最长请求时间

requestTimeout: 10000,

//操作正常code,支持String、Array、int多种类型

successCode: [200, 0],

//登录失效code

invalidCode: -1,

//无权限code

noPermissionCode: -1,

}

axios封装处理

import axios from 'axios';

import { netConfig } from '@/config/net.config';

const {

baseURL,

contentType,

invalidCode,

noPermissionCode,

requestTimeout,

successCode } = netConfig;

import store from '@/store/index.js';

import router from '@/router/index.js';

import { ElMessageBox, ElMessage } from 'element-plus';

// eslint-disable-next-line no-unused-vars

let tokenLose = true;

/**

*

* @description 处理code异常

* @param {*} code

* @param {*} msg

*/

const handleCode = (code, msg) => {

switch (code) {

case invalidCode:

tokenLose = false;

ElMessageBox.confirm('您已掉线,或者访问权限出错,请重新登录!', '重新登录', {

confirmButtonText: '确定',

cancelButtonText: '取消',

type: 'warning',

})

.then(async () => {

// 处理重新登录逻辑

})

.catch(() => {

tokenLose = true;

});

break;

case noPermissionCode:

router.push({ path: '/401' }).catch(() => {});

break;

default:

console.log(msg);

// Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error');

break;

}

};


const instance = axios.create({

baseURL,

timeout: requestTimeout,

headers: {

'Content-Type': contentType,

},

});


instance.interceptors.request.use(

(config) => {

// 处理token header信息

if (store.getters.token) {

// config.headers['Authorization'] = ''

}

return config;

},

(error) => {

return Promise.reject(error);

}

);


instance.interceptors.response.use(

(response) => {

const res = response.data;

const { data } = response;

const { code, msg } = data;

// 操作成功

if (successCode.indexOf(code) !== -1) {

return res;

} else {

handleCode(code, msg);

return Promise.reject();

}

},

(error) => {

const { response, message } = error;

if (error.response && error.response.data) {

const { status, data } = response;

handleCode(status, data.msg || message);

return Promise.reject(error);

} else {

let { message } = error;

if (message === 'Network Error') {

message = '后端接口连接异常';

}

if (message.includes('timeout')) {

message = '后端接口请求超时';

}

if (message.includes('Request failed with status code')) {

const code = message.substr(message.length - 3);

message = '后端接口' + code + '异常';

}

ElMessage(message || `后端接口未知异常`, 'error');

return Promise.reject(error);

}

}

);


export default instance;

八.vite打包问题

  • vite打包之后页面空白页解决方案

// vite.config.js

export default defineConfig({

base: './',

})

  • vite打包之后访问资源跨域解决方案

Vite 本身依赖于 ESModule 来做模块加载,而 ESModule 是不支持 file:// 本地访问的(事实上在生产环境中压根也没这种需求)。

想要本地访问,介绍两种方式

  • 方式一:安装serve

# npm

npm i serve -g

# yarn

yarn add serve -g

然后进入到打包文件(如:dist)根目录执行 serve命令,快速启动web服务就可以访问了


Serving!

Local: http://localhost:54698

  • 方式二:执行npm run preview

// package.json

[

"scripts": {

"build": "vite build --mode production",

"preview": "vite build && vite preview"

},

]

自动给css添加兼容性前缀

需要安装的插件

  • autoprefixe - 解析 CSS 并通过 Can I Use 向规则添加供应商前缀

  • postcss - 使用 JS 插件转换样

安装插件


# npm

npm install autoprefixe postcss -D

# yarn

yarn add autoprefixe postcss -D

项目根目录下创建postcss.config.js


module.exports = {

plugins: {

// 兼容浏览器,添加前缀

autoprefixer: {

overrideBrowserslist: [

'Android 4.1',

'iOS 7.1',

'Chrome > 31',

'ff > 31',

'ie >= 8',

//'last 2 versions', // 所有主流浏览器最近2个版本

],

grid: true,

}

},

};

九.安装字节跳动IconPark图标库, Element SvgIcon 图标库

iconpark文档:github.com/bytedance/I…

图标库:iconpark.oceanengine.com/official

安装IconPark


# npm

npm install @icon-park/vue-next -S

# yarn

yarn add @icon-park/vue-next -S

安装 SvgIcon


# npm

npm install @element-plus/icons -S

# yarn

yarn add @element-plus/icons -S

按需注册图标

src/plugin目录下创建icon-park.js文件


// icon-park.js

/**

* 字节跳动图标库:https://iconpark.oceanengine.com/official

* and vue3 https://github.com/bytedance/IconPark/tree/master/packages/vue-next

* element-plus icon: https://element-plus.gitee.io/#/zh-CN/component/icon

* @description 图标库按需注册

* @author hu-snail

* @example <icon-user theme="outline" size="16" fill="#999" />

* @example <el-icon :size="20"> <edit /> </el-icon>

*/


// iconpark

import { User, Lock, Alipay, Wechat, Github, Twitter, Google } from '@icon-park/vue-next';

// el-icon

import { Edit } from '@element-plus/icons';


// 所需的组件

export const components = [User, Lock, Alipay, Edit, Wechat, Github, Twitter, Google];


// 注册

export default (app) => {

components.forEach((component) => {

app.component(component.name.replace('icon-', ''), component);

});

};

然后在main.js中使用, 添加以下代码


// main.js

import iconPark from './plugin/icon-park';

// 注册字节跳动图标

iconPark(app);

template中使用


<template>

<div class="icon">

<!-- iconpark -->

<user theme="outline" size="16" fill="#999" />

<!-- element-plus icon -->

<el-icon :size="20"> <edit /> </el-icon>

</div>

</template>