axios,mockjs和vuex在开发/生产环境基本是耦合的,axios完成请求发送/接收及封装,mockjs进行数据的拦截/模拟,vuex进行本地数据存储及全局变量维护。
以登录场景为例,登录表单el-form标签绑定form.item数据,el-input分别对应item.username和item.passwd,点击登录的button触发login()方法,入参即item数据,login()方法使用被axios统一封装的request(增加baseurl,请求头等)发起http请求,mockjs拦截请求返回模拟数据,axios拿到返回后封装respond(增加或修改相应code,处理权限等),vuex存储模拟的token,其他components比如router可以直接引用token来判断是否有权限跳转。
实现如下的居中登录输入框,登录触发login请求,图中/api/menu/getMenu的url就是login方法
form表单对应的username和password接收参数:
<template>
<el-form
:model="form"
status-icon
:rules="rules"
ref="form"
label-width="100px"
class="login-container"
>
<h3 class="login_title">系统登陆</h3>
<el-form-item
label="用户名"
label-width="80px"
prop="username"
class="username"
>
<el-input
type="input"
v-model="form.username"
auto-complete="off"
placeholder="请输入账号"
></el-input>
</el-form-item>
<el-form-item label="密码" label-width="80px" prop="password">
<el-input
type="password"
v-model="form.password"
auto-complete="off"
placeholder="请输入密码"
></el-input>
</el-form-item>
<el-form-item class="login_submit">
<el-button type="primary" @click="login" class="login_submit"
>登陆</el-button
>
</el-form-item>
</el-form>
</template>
<script>
import { getMenu } from '../../api/menuRequest'
export default {
name: 'login',
data() {
return {
form: {},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{
min: 3,
message: '用户名长度不能小于3位',
trigger: 'blur'
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur'
}
]
}
}
},
methods: {
login() {
getMenu(this.form).then(({ data: res }) => {
console.log(res, 'res')
if (res.code == 20000) {
this.$store.commit('clearMenu')
this.$store.commit('setMenu', res.menu)
this.$store.commit('setToken', res.token)
this.$store.commit('addMenu', this.$router)
this.$router.push({ name: 'main' })
} else {
this.$message.warning(res.data.message)
}
})
}
}
}
</script>
<style lang="less" scoped>
.login-container {
border-radius: 15px;
background-clip: padding-box;
//margin: 180px auto;
position: absolute;
width: 350px;
padding: 35px 35px 15px 35px;
background-color: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
left: 50%;
top: 50%;
transform: translate(-60%, -60%);
}
.login_title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
.login_submit {
margin: 10px auto 0 auto;
}
</style>
axios
引入axios
main.js中
import http from 'axios'
Vue.prototype.$http = http
封装request和response
axios中文文档 使用axios.create([config])自定义配置新建一个 axios 实例service ,拦截器在文档中这样描述:
axios.interceptors.request.use(function (config) {
return config;
}, function (error) {
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
return Promise.reject(error);
});
这里发起请求时候添加messagebox和store判断是否有token,没有提醒,拿到响应后如果是20000那么说明响应ok,但是如果是50008和其他code,说明token过期了,你要重新登录。
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '../store'
const service = axios.create({
baseURL: '',
timeout: 5000
})
service.interceptors.request.use(config => {
if (store.getters.token) {
config.headers = {
URL: this.baseURL
}
}
return config
})
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
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)
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
定义login接口
有统一axios模块,只需要实例化request就是api接口,那么这个request就有url,method,data三个参数,其中data和param分别是body和路径传参。
import request from './request'
export function getMenu(param) {
return request({
url: '/api/menu/getMenu',
method: 'post',
data: param
})
}
mockjs
mockjs,mockjs是如何自动拦截请求的,答案是url路径,路径指的是baseurl+request.url拼接后的全路径,普通的vue项目在根目录的config目录中有dev.env.js和prod.env.js,dev增加MOCK为ture:
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
MOCK: true,
BASE_URL: 'http://127.0.0.1:9999/'
})
在main.js中增加动态判断引入:
process.env.MOCK && require('../src/mock/index')
/src/mock/index.js文件如下:
import Mock from 'mockjs'
import menuApi from './mockData/menuApi'
Mock.mock('/api/menu/getMenu', menuApi.getMenu)
vuex
vuex就是用来全局引用的,npm install store后在main.js中挂载:
import store from './store'
new Vue({
el: '#app',
router,
store,
render: (h) => h(App)
})
直接在store目录先新建index.js,如下把同级modules中的所有需要存储到vuex中都引入:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const modulesFiles = require.context('./modules', true, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules
})
export default store
每个需要维护的一类状态包括state和mutation,mutation被调用来修改state:
import Cookies from 'js-cookie'
export default {
state: {
token: ''
},
mutations: {
setToken(state, val) {
state.token = val
Cookies.set('token', val)
},
clearToken(state) {
state.token = ''
Cookies.remove('token')
},
getToken(state) {
state.token = state.token || Cookies.get('token')
}
}
}
|