vue2项目从0搭建(二):配置代理,登录功能和菜单权限

前言:

发送ajax,fetch,websocket请求获取服务端的数据,配置代理是必须的环节

登录功能和菜单权限是后台管理系统中非常经典且十分重要的业务,这里涉及的知识点也是比较多的,坑也多,面试也是很重要的一环。

这里必须得会,没错是必须

配置服务代理

创建两个node服务

在和src平级的目录创建一个服务器文件,我这里给的名字是node_server,里面创建两个服务

下载express
npm i express
服务1:server1.js
const express = require('express')

const app1 = express()
app1.get('/server1',(req,res)=>{
    res.send('我是server1')
})

//开启3001接口的服务
app1.listen(3001)
服务2:server2.js
const express = require('express')

const app2 = express()
app2.get('/server2',(req,res)=>{
    res.send('我是server2')
})

//开启3000接口的服务
app2.listen(3002)

然后打开两个终端,分别启用这两个服务

如图所示,server2.js同理

 测试自己的服务

没问题,模拟了最简单的服务

vue工程配置代理服务器(核心代码)

在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中文文档

下载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')

测试demo

    created(){
      this.$axios.get('/server1/login').then(res=>{
        console.log(res,'接口1请求回来的数据')
      })
      this.$axios.get('/server2/getRole').then(res=>{
        console.log(res,'接口2请求回来的数据')
      })
    }

 

登录功能和token校验

创建登录接口

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>

使用路由拦截器进行token校验(核心代码)

在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)
给vuex配置新的全局值
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不生效白屏的问题(这里的代码已经解决了这个问题,但我没有解释)

其实很多人对路由前置守卫中动态添加路由没有太多的理解,开发中不撞几次墙都不行的,这里有一个解释我没有纯手敲,而是引用别人的博客内容,感觉他写的比较清晰明白,大家请看:

Vue 动态添加用户的权限路由(动态路由) - 知乎

懒得去看的直接看我截图总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/152995.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【SOLO】实例分割论文SOLO: Segmenting Objects by Locations详解

&#x1f6a9;&#x1f6a9;实例分割论文专栏快速跳转&#x1f6a9;&#x1f6a9;【实例分割】 目录 &#x1f31e;&#x1f31e;1.摘要 &#x1f333;&#x1f333;2.创新点 &#x1f33c;&#x1f33c;3.网络结构 &#x1f383;&#x1f383;3.1背景 &#x1f383;&…

4-flask-cbv源码、Jinja2模板、请求响应、flask中的session、flask项目参考

1 flask中cbv源码 2 Jinja2模板 3 请求响应 4 flask中的session 5 flask项目参考 1 flask中cbv源码 ***flask的官网文档&#xff1a;***https://flask.palletsprojects.com/en/3.0.x/views/1 cbv源码执行流程1 请求来了&#xff0c;路由匹配成功---》执行ItemAPI.as_view(item…

【java学习—十五】线程的生命周期(4)

文章目录 线程的生命周期1. 相关概念 线程的生命周期 1. 相关概念 线程的生命周期&#xff1a;线程从生到死的整个经历。 JDK 中用 Thread.State 枚举表示了线程的几种状态 要想实现多线程&#xff0c;必须在主线程中创建新的线程对象。 Java 语言使用 Thread 类及其子类的…

UnitTest + Selenium 完成在线加法器自动化测试

1. 任务概述 利用 UnitTest 与 Selenium 编写自动化用例&#xff0c;测试在线加法器中的整数单次加法功能【如123 】 人工操作流程&#xff08;测试 12 是否等于 3&#xff09;&#xff1a; 打开在线加法器点击按钮1&#xff0c;再点击按钮&#xff0c;再点击按钮2&#xff0c…

adb手机调试常用命令

查看手机型号 adb shell getprop ro.product.model 查看电池状况 adb shell dumpsys battery 查看分辨率 adb shell wm size 查看屏幕密度 adb shell wm density 查看显示屏参数 adb shell dumpsys window displays 查看android_id adb shell settings get secure android…

安科瑞为数据中心绿色高质量发展贡献力量

安科瑞 崔丽洁  0前言 目前&#xff0c;数字经济的迅猛发展激发了数据中心的算力需求&#xff0c;数据中心规模与功耗密度不断提高&#xff0c;能耗问题日益突出。短期内&#xff0c;数据中心的能耗、碳排放量仍会呈现上升趋势。面对国家“双碳”压力&#xff0c;我国数据中心…

Active Directory 域服务基础知识

Active Directory 域服务&#xff08;AD DS&#xff09;是 Microsoft 提供的传统本地域服务&#xff0c;它是 Active Directory&#xff08;AD&#xff09;中的核心组件和服务器角色&#xff0c;Active Directory&#xff08;AD&#xff09;是 Windows 操作系统环境中的专用专有…

vue2项目修改编译巨慢

前言&#xff1a;我们的一个vue项目在给新同事后他说编译贼慢&#xff0c;一个小修改项5分钟才能自动编译成功&#xff0c;我把项目放到新电脑上也巨慢&#xff0c;升级了nodejs好使了一些&#xff0c;但还是慢&#xff0c;最后引入webpack后巨快&#xff0c; 在项目的package…

初识Java 18-1 泛型

目录 简单泛型 元组库 通过泛型实现栈类 泛型接口 泛型方法 可变参数和泛型方法 通用Supplier 简化元组的使用 使用Set创建实用工具 本笔记参考自&#xff1a; 《On Java 中文版》 继承的层次结构有时会带来过多的限制&#xff0c;例如&#xff1a;编写的方法或类往往…

前端学习笔记--React

1. 什么是React? React 是一个用于构建用户界面的JavaScript库核心专注于视图,目的实现组件化开发我们可以很直观的将一个复杂的页面分割成若干个独立组件,每个组件包含自己的逻辑和样式 再将这些独立组件组合完成一个复杂的页面。 这样既减少了逻辑复杂度&#xff0c;又实现…

12-1- GAN -简单网络-线性网络

功能 随机噪声→生成器→MINIST图像。 训练方法 1 判别器的训练,首先固定生成器参数不变,其次判别器应当将真实图像判别为1,生成图像判别为0 loss=loss(real_out, 1)+loss(fake_out, 0) 2 生成器的训练,首先固定判别器参数不变,其次判别器应当将生成图像判别为1 loss…

你应该知道关于Python的这几个技巧!

随着大数据时代的到来&#xff0c;我们每天都在接触爬虫相关的事情&#xff0c;这其中就不得不提及Python这门编程语言。我已经使用Python编程有多年了&#xff0c;即使今天我仍然惊奇于这种语言所能让代码表现出的整洁和对DRY编程原则的适用。这些年来的经历让我学到了很多的小…

MySQL覆盖索引的含义

覆盖索引&#xff1a;SQL只需要通过索引就可以返回查询所需要的数据&#xff0c;而不必通过二级索引查到主键之后再去查询数据&#xff0c;因为查询主键索引的 B 树的成本会比查询二级索引的 B 的成本大。 也就是说我select的列就是我的索引列&#xff08;或者主键&#xff0c;…

XD6500S— LoRa SIP模块

XD6500S是一系列LoRa SIP模块&#xff0c;集成了射频前端和LoRa射频收发器SX1262系列&#xff0c;支持LoRa和FSK调制。收发器SX1262系列&#xff0c;支持LoRa和FSK调制。LoRa技术是一种扩频协议&#xff0c;针对LPWAN 应用的低数据速率、超远距离和超低功耗通信进行了优化。通信…

KMP算法详讲(问题导向,通俗易懂)

KMP算法是一种高效的字符串匹配算法&#xff0c;相比于BF算法的时间复杂度为O(n*m)&#xff0c;它的时间复杂度降低到了O(nm)。这种算法的高效性在于它利用了主串的指针不回溯&#xff0c;而只移动模式串的指针位置。然而&#xff0c;对于初学者来说&#xff0c;KMP算法并不容易…

全面掌握:性能测试计划的制胜法宝

一&#xff0e;简介 简介部分就不用过多描述了&#xff0c;无非项目的背景&#xff0c;进行此次性能测试的原因&#xff0c;以及性能测试覆盖的范围等等&#xff0c;几乎所有项目文档都在开端对项目进行简单的阐述。 二&#xff0e;性能测试需求 寻找的被测试对象和压力点 …

windows 部署 weblogic 12.1.3

1、安装 1&#xff09;下载 地址&#xff1a;WebLogic Server 12c (12.2.1), WebLogic Server 11g (10.3.6) and Previous Releases 2&#xff09;安装 weblogic server java -Xmx1024m -jar fmw_12.1.3.0.0_wls.jar 出现图形界面按需配置&#xff0c;注意配置的安装路径不能…

11月编程榜最新出炉,第一名很离谱

这段时间&#xff0c;随着人工智能的崛起&#xff0c;Python的地位水涨船高。有不少朋友感觉到危机重重。 其中&#xff0c;最明显的&#xff0c;是市场环境的变化&#xff1a; 外部招聘&#xff1a;Python岗位日均需求量高达15000&#xff01;不仅是程序员&#xff0c;内容编…

【分享课】11月16日晚19:30PostgreSQL分享课:PG缓存管理器主题

PostsreSQL分享课分享主题: PG缓存管理器主题 直播分享平台&#xff1a;云贝教育视频号 时间&#xff1a;11月16日 周四晚 19: 30 分享内容: 缓冲区管理器结构 缓冲区管理器的工作原理 环形缓冲区 脏页的刷新

uniapp使用Canvas实现电子签名

来源&#xff1a; 公司的一个需求&#xff0c;需要给新注册的会员和客商需要增加签署协议功能&#xff1b; 之前的思路&#xff1a; 1、使用vue-signature-pad来实现电子签名&#xff0c;但是安卓手机不兼容&#xff1b; 2、uniapp插件市场来实现&#xff0c;但是对HBuilderX…