vue从零搭建后台管理系统(二)ElementUI 、Vuex、 axios、Mock.js及其他一些配置

本章主要演示VUE项目引入ElementUI 、Vuex、 axios、Mock.js及一些公共变量的配置 本章需要在上一章的基础上安装以下组件,绿色为新增组件 image.png 也可异步github至项目自行对照package.json对比缺少哪些依赖 https://github.com/a307420929/Vue-management-system dependencies下安装命令: 以axios为例 npm i axios --save (最新发布版本) npm i axios@^7.8.7 --save (安装指定版本) devDependencies下安装命令: 以babel-cli为例 npm i babel-cli --save-dev (最新发布版本) npm i babel-cli@^6.26.0 --save-dev (安装指定版本) 后续不再过多赘述安装依赖方法 1.ElementUI 因为是后台项目,视图组件库采用目前支持比较好的并且用的比较多的ElementUI>https://element.eleme.cn/#/zh-CN 在/src/main.js中添加代码 import ElementUI from 'element-ui' //引入ElementUI import 'element-ui/lib/theme-chalk/index.css' //引入ElementUI样式相关 Vue.use(ElementUI) // Vue中使用 安装成功后,便可在项目中使用elment相关视图组件,类似下面当中的el-***开头的标签都是element的组件,未来项目中会大面积使用,先截取/src/views/login/index.vue 部分代码熟悉一下 image.png 2.Vuex的引用 详细了解移步官网地址:https://vuex.vuejs.org/zh/ image.png 我们将项目目录中的store优化成以下结构,方便后期分功能模块存取状态值 image.png /store/modules/index.js import Vue from 'vue' import Vuex from 'vuex' import getters from './getters' import app from './modules/app' import settings from './modules/settings' import user from './modules/user' Vue.use(Vuex) const store = new Vuex.Store({ modules: { app, settings, user }, getters }) export default store /store/modules/getters.js const getters = { sidebar: state => state.app.sidebar, device: state => state.app.device, token: state => state.user.token, avatar: state => state.user.avatar, name: state => state.user.name } export default getters /store/modules/user.js import { login, logout, getInfo } from '@/api/user' import { getToken, setToken, removeToken } from '@/utils/auth' import { resetRouter } from '@/router' const getDefaultState = () => { return { token: getToken(), name: '', avatar: '' } } const state = getDefaultState() const mutations = { RESET_STATE: (state) => { Object.assign(state, getDefaultState()) }, SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: (state, name) => { state.name = name }, SET_AVATAR: (state, avatar) => { state.avatar = avatar } } const actions = { // user login login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { login({ username: username.trim(), password: password }).then(response => { const { data } = response commit('SET_TOKEN', data.token) setToken(data.token) resolve() }).catch(error => { reject(error) }) }) }, // get user info getInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo(state.token).then(response => { const { data } = response if (!data) { reject('Verification failed, please Login again.') } const { name, avatar } = data commit('SET_NAME', name) commit('SET_AVATAR', avatar) resolve(data) }).catch(error => { reject(error) }) }) }, // user logout logout({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { removeToken() // must remove token first resetRouter() commit('RESET_STATE') resolve() }).catch(error => { reject(error) }) }) }, // remove token resetToken({ commit }) { return new Promise(resolve => { removeToken() // must remove token first commit('RESET_STATE') resolve() }) } } export default { namespaced: true, state, mutations, actions } 同样需要在/src/main.js中添加代码,因为我们通过vue-cli的时候选择了vuex,所以main里面默认就已经引用了store import store from './store' new Vue({ router, store, render: h => h(App) }).$mount('#app') 此时,就可以在正常业务过程中通过vuex去存取状态了,截取以下登录逻辑片段方便理解 image.png 3.axios的引用 Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。 详细使用示例及API 移步 https://www.kancloud.cn/yunye/axios/234845 封装axios /src/utils/request.js import axios from 'axios' import { MessageBox, Message } from 'element-ui' import store from '@/store' import { getToken } from '@/utils/auth' // 新建axios服务 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // 这里取环境变量VUE_APP_BASE_API timeout: 5000 // request timeout }) // 请求拦截器 service.interceptors.request.use( config => { // do something before request is sent if (store.getters.token) { // let each request carry token // ['X-Token'] is a custom headers key // please modify it according to the actual situation config.headers['X-Token'] = getToken() } return config }, error => { // do something with request error console.log(error) // for debug return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response => { const res = response.data if (res.code !== 20000) { Message({ message: res.message || 'Error', type: 'error', duration: 5 * 1000 }) // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; if (res.code === 50008 || res.code === 50012 || res.code === 50014) { // to re-login MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { confirmButtonText: 'Re-Login', cancelButtonText: 'Cancel', type: 'warning' }).then(() => { store.dispatch('user/resetToken').then(() => { location.reload() }) }) } return Promise.reject(new Error(res.message || 'Error')) } else { return res } }, error => { console.log('err' + error) // for debug Message({ message: error.message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) export default service 此时我们就可以在定义API的时候用到封装的axios了 示例代码 /src/api/user.js import request from '@/utils/request' export function login(data) { return request({ url: '/vue-management-system/user/login', method: 'post', data }) } export function getInfo(token) { return request({ url: '/vue-management-system/user/info', method: 'get', params: { token } }) } export function logout() { return request({ url: '/vue-management-system/user/logout', method: 'post' }) } 定义好的API在业务代码中的使用参考以下代码片段 如需使用接口别忘记在代码最上方引入对应接口方法名 import {getInfo } from '@/api/user' image.png 4.Mock.js的引用 mock的具体使用请移步 https://github.com/nuysoft/Mock/wiki/Getting-Started 新建mock目录,在mock目录下新建index.js和mock-server.js文件 index.js import Mock from 'mockjs' import { param2Obj } from '../src/utils' import user from './user' const mocks = [ ...user, ] // for front mock // please use it cautiously, it will redefine XMLHttpRequest, // which will cause many of your third-party libraries to be invalidated(like progress event). export function mockXHR() { // mock patch // https://github.com/nuysoft/Mock/issues/300 Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send Mock.XHR.prototype.send = function() { if (this.custom.xhr) { this.custom.xhr.withCredentials = this.withCredentials || false if (this.responseType) { this.custom.xhr.responseType = this.responseType } } this.proxy_send(...arguments) } function XHR2ExpressReqWrap(respond) { return function(options) { let result = null if (respond instanceof Function) { const { body, type, url } = options // https://expressjs.com/en/4x/api.html#req result = respond({ method: type, body: JSON.parse(body), query: param2Obj(url) }) } else { result = respond } return Mock.mock(result) } } for (const i of mocks) { Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) } } // for mock server const responseFake = (url, type, respond) => { return { url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), type: type || 'get', response(req, res) { console.log('request invoke:' + req.path) res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) } } } export default mocks.map(route => { return responseFake(route.url, route.type, route.response) }) mock-server.js const chokidar = require('chokidar') const bodyParser = require('body-parser') const chalk = require('chalk') const path = require('path') const mockDir = path.join(process.cwd(), 'mock') function registerRoutes(app) { let mockLastIndex const { default: mocks } = require('./index.js') for (const mock of mocks) { app[mock.type](mock.url, mock.response) mockLastIndex = app._router.stack.length } const mockRoutesLength = Object.keys(mocks).length return { mockRoutesLength: mockRoutesLength, mockStartIndex: mockLastIndex - mockRoutesLength } } function unregisterRoutes() { Object.keys(require.cache).forEach(i => { if (i.includes(mockDir)) { delete require.cache[require.resolve(i)] } }) } module.exports = app => { // es6 polyfill require('@babel/register') // parse app.body // https://expressjs.com/en/4x/api.html#req.body app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) const mockRoutes = registerRoutes(app) var mockRoutesLength = mockRoutes.mockRoutesLength var mockStartIndex = mockRoutes.mockStartIndex // watch files, hot reload mock server chokidar.watch(mockDir, { ignored: /mock-server/, ignoreInitial: true }).on('all', (event, path) => { if (event === 'change' || event === 'add') { try { // remove mock routes stack app._router.stack.splice(mockStartIndex, mockRoutesLength) // clear routes cache unregisterRoutes() const mockRoutes = registerRoutes(app) mockRoutesLength = mockRoutes.mockRoutesLength mockStartIndex = mockRoutes.mockStartIndex console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`)) } catch (error) { console.log(chalk.redBright(error)) } } }) } user.js 为相应返回mock数据的定义 const tokens = { admin: { token: 'admin-token' }, editor: { token: 'editor-token' } } const users = { 'admin-token': { roles: ['admin'], introduction: 'I am a super administrator', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', name: 'Super Admin' }, 'editor-token': { roles: ['editor'], introduction: 'I am an editor', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', name: 'Normal Editor' } } export default [ // user login { url: '/vue-management-system/user/login', type: 'post', response: config => { const { username } = config.body const token = tokens[username] // mock error if (!token) { return { code: 60204, message: 'Account and password are incorrect.' } } return { code: 20000, data: token } } }, // get user info { url: '/vue-management-system/user/info\.*', type: 'get', response: config => { const { token } = config.query const info = users[token] // mock error if (!info) { return { code: 50008, message: 'Login failed, unable to get user details.' } } return { code: 20000, data: info } } }, // user logout { url: '/vue-management-system/user/logout', type: 'post', response: _ => { return { code: 20000, data: 'success' } } } ] 相关定义文件写好,此时需要去vue.config.js的devserver配置一下拦截 image.png 5.其他配置项 到此步骤有一个需要注意的地方分享给大家 因为此项目我们使用的是vue-cli3 在vue-cli2中打包时可以修改 “build” 和 “config”中的文件来区分不同的线上环境 而vue-cli3号称0配置,无法直接修改打包文件 其实可以通过为 .env 文件增加后缀来设置某个模式下特有的环境变量。比如,如果你在项目根目录创建一个名为 .env.development 的文件,那么在这个文件里声明过的变量就只会在 development 模式下被载入。 所以我们项目中会有以下两个文件 image.png .env.development 内容定义如下 # just a flag ENV = 'development' # base api VUE_APP_BASE_API = '/dev-api' 此时我们就可以通过process.env.VUE_APP_BASE_API获取到对应的值了 可参考src\utils\request.js 中baseURL的定义 image.png 今天就进行到这里,本教程可能注重过程,忽略了很多概念的东西和细节,一些地方都会标注使用到的功能依赖的路径跳转,如需深入了解可以先去读读文档大概了解一些概念再来也无妨 以上 如有问题,欢迎批评指正 后续会在此基础上继续更新,感谢欢迎关注支持

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):