发送ajax,fetch,websocket请求获取服务端的数据,配置代理是必须的环节
登录功能和菜单权限是后台管理系统中非常经典且十分重要的业务,这里涉及的知识点也是比较多的,坑也多,面试也是很重要的一环。
这里必须得会,没错是必须。
在和src平级的目录创建一个服务器文件,我这里给的名字是node_server,里面创建两个服务
npm i express
- const express = require('express')
-
- const app1 = express()
- app1.get('/server1',(req,res)=>{
- res.send('我是server1')
- })
-
- //开启3001接口的服务
- app1.listen(3001)
- const express = require('express')
-
- const app2 = express()
- app2.get('/server2',(req,res)=>{
- res.send('我是server2')
- })
-
- //开启3000接口的服务
- app2.listen(3002)
然后打开两个终端,分别启用这两个服务
如图所示,server2.js同理



没问题,模拟了最简单的服务
在vue.config.js中配置devserver
- const { defineConfig } = require('@vue/cli-service')
- module.exports = defineConfig({
- transpileDependencies: true,
- //关闭语法检查
- lintOnSave: false,
- //配置代理(可配置多个)
- devServer:{
- port:8080,
- open:true,
- proxy:{
- //代理1
- '/server1':{
- target:'http://localhost:3001',
- ws:false,
- changeOrigin:true,
- pathRewrite:{
- '^/server1':''
- }
- },
- //代理2
- '/serve2':{
- target:'http://localhost:3002',
- ws:false,
- changeOrigin:true,
- pathRewrite:{
- '^/server2':''
- }
- }
- }
- }
- })
axios文档:axios中文文档
下载axios
npm i axios
创建一个request.js文件,对axios进行基础的配置
- import axios from 'axios'
-
- const instance = axios.create({
- // baseURL: '',
- // timeout: 1000,
- // headers: {'X-Custom-Header': 'foobar'}
- });
-
-
- // 添加请求拦截器
- instance.interceptors.request.use(function (config) {
-
- return config;
- }, function (error) {
-
- return Promise.reject(error);
- });
-
- // 添加响应拦截器
- instance.interceptors.response.use(function (response) {
-
- //在这里修改一下,将response改为response.data,方便提取响应数据
- return response.data;
- }, function (error) {
-
- return Promise.reject(error);
- });
- console.log(instance,'axios实例')
- export default instance
在main.js中引入并配置到vue实例上
- //...
- import axios from '@/utils/request'
- Vue.prototype.$axios = axios
- new Vue({
- //...
- axios,
- render: h => h(App),
- }).$mount('#app')
- created(){
- this.$axios.get('/server1/login').then(res=>{
- console.log(res,'接口1请求回来的数据')
- })
- this.$axios.get('/server2/getRole').then(res=>{
- console.log(res,'接口2请求回来的数据')
- })
- }


server1.js(登录接口)
- const express = require('express')
-
- const app1 = express()
- app1.get('/login', (req, res) => {
- switch (req.query.user) {
- case 'admin': //管理员
- res.send({
- userName: 'admin',
- token: 'admin_token'
- })
- break
- case 'common': //普通
- res.send({
- userName: 'common',
- token: 'common_token'
- })
- break
- case 'temporary': //临时
- res.send({
- userName: 'temporary',
- token: 'temporary_token'
- })
- break
- }
-
- })
-
- //开启3001接口的服务
- app1.listen(3001)
极简版登录页面(将就着看吧)
- <template>
- <div class="loginCom">
- <div class="loginForm">
- <el-button @click="login('admin')" type="primary">管理人员登录el-button>
- <el-button @click="login('common')" type="primary">普通用户登录el-button>
- <el-button @click="login('temporary')" type="primary">临时用户登录el-button>
- div>
-
- div>
- template>
- <script>
- export default {
- name:'login',
- data(){
- return{
-
- }
- },
- methods:{
- login(userType){
- this.$axios.get(`/server1/login?user=${userType}`,).then(res=>{
- //存储用户信息
- this.$store.dispatch('changeUserInfo',res)
- localStorage.setItem('vue2_userInfo',JSON.stringify(res))
- localStorage.setItem('vue2_token',res.token)
- this.$router.push('/dashboard')
- })
- }
- },
- created(){
- }
- }
- script>
- <style lang="less" scoped>
- .loginCom{
- display: flex;
- width: 100%;
- height: 100vh;
- justify-content: center;
- align-items: center;
- }
- style>

在router/index.js中配置
- //...
- const router = new Router({
- routes:RouteList,
- mode:'history'
- })
-
- router.beforeEach((to,from,next)=>{
- let token = localStorage.getItem('vue2_token')
- //判断是否有token
- if(token){
- //如果是去往登录页面,直接返回到主页
- if(to.path === '/login'){
- next('/dashboard')
- }else{
- //去原定跳转的页面
- next()
- }
- }else{
- //没有token的情况下,所有路由都指向登录页面
- if(to.path === '/login'){
- next()
- }else{
- next('/login')
- }
- }
- })
-
- export default router
这里不做过多解释,仅展示具体代码和demo效果
有需要的可以去vue-router官方文档去看,那里有很详细的解释
server2.js(权限接口)
- const express = require('express')
-
- const app2 = express()
- app2.get('/getRole', (req, res) => {
- switch (req.query.userType) {
- case 'admin': //管理人员权限
- res.send([
- 'instructions', 'communication', 'common', 'admin'
- ])
- break
- case 'common': //普通用户权限
- res.send([
- 'instructions', 'communication', 'common'
- ])
- break
- case 'temporary': //临时用户权限
- res.send([
- 'instructions', 'communication'
- ])
- }
- })
-
- //开启3000接口的服务
- app2.listen(3002)
- const userMode = {
- state:()=>({
- //用户信息
- userInfo:{
- name:'wjt',
- age:28
- },
- //权限字段
- userRole:[],
- //符合权限的异步路由
- asyncRouteList:[]
- }),
- mutations:{
- CHANGE_USEINFO:(state,info)=>{
- state.userInfo = info
- },
- CHANGE_USERROLE:(state,info)=>{
- state.userRole = info
- },
- CHANGE_ASYNCROUTELIST:(state,info)=>{
- state.asyncRouteList = info
- }
- },
- actions:{
- changeUserInfo:(({commit},data)=>{
- commit('CHANGE_USEINFO',data)
- }),
- changeUserRole:(({commit},data)=>{
- commit('CHANGE_USERROLE',data)
- }),
- changeAsyncRouteList:(({commit},data)=>{
- commit('CHANGE_ASYNCROUTELIST',data)
- })
- }
- }
- export default userMode
普通用户页面和管理员页面都是根据用户角色的权限去展示的
- const list = [
- {
- name:'dashboard',
- path:'/dashboard',
- meta:{
- name:'主页',
- },
- component:()=>import('@/pages/dashboard/index.vue'),
- children:[
- {
- name:'communication',
- path:'/dashboard/communication',
- meta:{
- name:'组件通信',
- icon:'el-icon-phone'
- },
- component:()=>import('@/pages/communication/index.vue')
- },
- {
- name:'instructions',
- path:'/dashboard/instructions',
- meta:{
- name:'指令',
- icon:'el-icon-thumb'
- },
- component:()=>import('@/pages/instructions/index.vue')
- },
- {
- name:'common',
- path:'/dashboard/common',
- meta:{
- name:'普通用户页面',
- icon:'el-icon-user'
- },
- component:()=>import('@/pages/common/index.vue')
- },
- {
- name:'admin',
- path:'/dashboard/admin',
- meta:{
- name:'管理员页面',
- icon:'el-icon-s-custom'
- },
- component:()=>import('@/pages/admin/index.vue')
- },
- ]
- },
-
- ]
- export default list
- import Vue from 'vue'
- import Router from 'vue-router'
- import asyncRouteList from './asyncRouteList'
- import globalRouteList from './globalRouteList'
- import store from '@/store/index'
- import {getUserRoleFun} from '@/globalFun/index'
- import { cloneDeep } from 'lodash'
-
- const RouteList = [].concat(globalRouteList)
- let initAsyncList = cloneDeep(asyncRouteList)
- Vue.use(Router)
-
- const router = new Router({
- routes:RouteList,
- mode:'history'
- })
- if(localStorage.getItem('vue2_userInfo')){
- store.dispatch('changeUserInfo',JSON.parse(localStorage.getItem('vue2_userInfo')))
- }
-
- //根据后台返回权限字段匹配路由
- function addAsyncRouterFun(roleInfo){
- let filerOverList = []
- filterRouteFun(initAsyncList[0].children,roleInfo,filerOverList)
- let allAsyncRouteList = asyncRouteList[0]
- allAsyncRouteList.children = filerOverList
- router.options.routes = [].concat(globalRouteList)
- router.addRoutes([allAsyncRouteList]) //动态添加路由
- router.options.routes.push(allAsyncRouteList) //给$router的options添加权限路由
- store.dispatch('changeAsyncRouteList',allAsyncRouteList.children)
- }
-
- //递归匹配多级路由
- function filterRouteFun(initAsyncList,roleInfo,filerOverList){
- initAsyncList.forEach(item=>{
- if(roleInfo.includes(item.name)){
- let RouteItem = cloneDeep(item)
- if(RouteItem.children){
- RouteItem.children = []
- filterRouteFun(item.children,roleInfo,RouteItem.children)
- }
- filerOverList.push(RouteItem)
- }
- })
- }
-
- router.beforeEach(async(to,from,next)=>{
- let token = localStorage.getItem('vue2_token')
- if(token){
- let roleList = store.state.user.userRole
- //没有权限数据
- if(roleList.length<1){
- let role = await getUserRoleFun(store.state.user.userInfo.userName) //请求权限数据
- store.dispatch('changeUserRole',role)
- addAsyncRouterFun(role) //根据权限字段值过滤符合添加的路由配置项
- next({...to,replace:true}) //重新访问,走一遍前置守卫
- }else{
- if(to.path === '/login'){
- next('/dashboard')
- }else{
- next()
- }
- }
-
- }else{
- if(to.path === '/login'){
- next()
- }else{
- next('/login')
- }
- }
- })
-
- export default router
获取权限参数的方法globalFun/index.js
- import axios from '@/utils/request'
- export function getUserRoleFun(userType){
- return new Promise(resolve=>{
- axios.get(`/server2/getRole?userType=${userType}`)
- .then(res=>{
- resolve(res)
- }).catch(error=>{
- console.log(error,'error')
- })
- },reject=>{
- reject('error')
- })
- }
- outLogin(){
- this.$store.dispatch('changeUserInfo',{
- name:'wjt'
- })
- this.$store.dispatch('changeUserRole',[])
- this.$store.dispatch('changeAsyncRouteList',[])
- localStorage.removeItem('vue2_userInfo')
- localStorage.removeItem('vue2_token')
- this.$router.push('/login')
- }
管理员

普通用户

临时用户

ok,结束!
这里写的东西还是比较多的,虽然功能常见,网上也一大堆子,但是我这里的代码基本都能直接用,你只具体的核心代码也都给标注出来了。
其实很多人在实际开发开发业务中未必会有机会写真实的登录和路由权限场景,但是还是必须得会,很重要。
而且这里是有坑的,比如很多人在开发时会遇到两个问题
1.是和devserver有关的一个遮蔽框,虽然你可以把它关掉,但是还是很坑
你需要关闭开发阶段的报错和警告(这里是说框架提供的,而非你业务代码里的)

解决方法:vue.config.js
- devServer:{
- client:{
- overlay:false
- },
- ...
- }
2.addRoutes不生效白屏的问题(这里的代码已经解决了这个问题,但我没有解释)
其实很多人对路由前置守卫中动态添加路由没有太多的理解,开发中不撞几次墙都不行的,这里有一个解释我没有纯手敲,而是引用别人的博客内容,感觉他写的比较清晰明白,大家请看:
懒得去看的直接看我截图总结

我对这篇文章绝对是用心了,感谢大家给个赞或收藏