Vue Vuex 通过定义函数实现异步请求模拟为同步请求分析

你猜 阅读:187 2021-03-31 13:19:31 评论:0

问题产生原因描述:今天在构建基于SpringBoot+ Vue + Vuex 构建动态菜单项,遇到一个问题:用户在登入成功后,在路由守护代码中

// 导航守卫 
// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆 
router.beforeEach((to, from, next) => { 
  if (to.path === '/login') { 
    next(); 
  } else { 
    let token = localStorage.getItem('token'); 
    if (token === null || token === '') { 
      next('/login'); 
    } else { 
      console.log('12345') 
      // 查询动态表单 
      initAdminMenu(router, store) 
      // 跳转至指定页面 
      next(); 
    } 
  } 
}); 
 

查询动态菜单时,

前端控制台错误信息:

输出请求路径地址404,请求地址:http://192.168.1.74:9090/shiro/permission/-1

造成的原因是:

用户登入成功后,写入Vuex store 的数据是异步方式写入,用户登入成功逻辑代码 :(重点逻辑代码片段login()方法)

 
<template> 
  <div class="login-container"> 
    <el-form label-position="left" 
      label-width="0px" 
      status-icon> 
        <h3 class="login_title">系统登录</h3> 
        <el-form-item> 
          <el-input 
            type="text" 
            v-model="loginForm.username" 
            auto-coplete="off" 
            placeholder="账号"> 
          </el-input> 
        </el-form-item> 
        <el-form-item> 
          <el-input 
            type="password" 
            v-model="loginForm.password" 
            auto-coplete="off" 
            placeholder="密码"> 
          </el-input> 
        </el-form-item> 
        <el-form-item style="width: 100%"> 
          <el-button type="primary" @click.native.prevent="login" style="width: 100%">登录</el-button> 
          <!-- 
          <el-button type="primary" @click.native.prevent="register" style="width: 100%; margin-top: 30px">注册</el-button> 
          --> 
        </el-form-item> 
      </el-form> 
  </div> 
</template> 
  
<script> 
import { post, waitsleep } from '@/utils/http' 
export default { 
  data () { 
    return { 
      loginForm: { 
        username: '', 
        password: '' 
      }, 
      userToken: '' 
    }; 
  }, 
  
  methods: { 
    login () { 
      let _this = this; 
      if (this.loginForm.username === '' || this.loginForm.password === '') { 
        alert('账号或密码不能为空'); 
      } else { 
        var params = { 
        username: _this.loginForm.username, 
        password: _this.loginForm.password 
      } 
        post('/common/login',params).then(res => { 
           console.log(res); 
          _this.userToken = res.token; 
          // 将用户token保存到vuex中 
          _this.$store.commit('setToken', res.token); 
          _this.$store.commit('setUserName', res.username); 
          _this.$store.commit('setUserId', res.uid); 
 
          _this.$router.push('/admin'); 
          alert('登陆成功'); 
        }) 
         
      } 
    } 
  } 
}; 
</script> 
<style scoped> 
.login-container{ 
  border-radius: 15px; 
  background-clip: padding-box; 
  margin: 10% auto; 
  width: 350px; 
  padding: 35px 35px 15px 35px; 
  background: #fff; 
  border: 1px solid #eaeaea; 
  box-shadow: 0 0 25px #cac6c6; 
} 
.login_title{ 
  margin: 0px auto 40px auto; 
  text-align: center; 
  color: #505458; 
} 
</style> 
 

造成上述报错的问题是_this.$store.commit() 异步请求方式。

公司前段工程师,提供的建议是:vuex 异步转同步,并提供了以下功能代码片段:

export function waitsleep (method) { 
    const start = Number(Date.now()) 
    return new Promise((resolve) => { 
      (function selfRecursion () { 
        setTimeout(() => { 
          let flag 
          if (typeof method === 'function') { 
            flag = method() 
          } 
          if (typeof method === 'number') { 
            flag = Number(Date.now()) - start < method 
          } 
          if (flag) { 
            selfRecursion() 
          } else { 
            resolve() 
          } 
        }, 10) 
      })() 
    }) 
  }

Vue 功能代码修改:在路由守护代码中,用户请求动态菜单接口必须确保store.uid 不等于 -1(系统默认用户ID为-1),才能执行动态菜单查询。以下是axios 封装功能代码:

http.js:axios 请求头、请求响应、get请求、post请求、delete请求、waitsleep函数:定时函数实现异步方法模拟同步方法。

import axios from 'axios' 
// 设置后端请求地址 
axios.defaults.baseURL='http://192.168.1.74:9090/shiro' 
// post请求头 
axios.defaults.headers.post['Content-Type'] = 'application/json' 
// 携带凭证信息 
axios.defaults.withCredentials = true 
 
// 添加请求拦截器,在请求头中加token 
axios.interceptors.request.use( 
    config => { 
      if (window.localStorage.getItem('token')) { 
        config.headers.token = window.localStorage.getItem('token'); 
      } 
      return config; 
    }, 
    error => { 
      return Promise.reject(error); 
    }); 
   
  // 添加响应拦截器,移除token 
  axios.interceptors.response.use( 
    response =>{ 
      return response; 
    }, 
    error=>{ 
      if(error.response){ 
        switch(error.response.status){ 
          case 401: 
            localStorage.removeItem('token'); 
            this.$router.push('/login'); 
        } 
      } 
    }) 
 
/** 
 * get方法,对应get请求 
 * @param {Stringf} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */ 
export function get (url, params) { 
    return new Promise((resolve, reject) => { 
      axios.get(url, { 
        params: params 
      }).then(res => { 
        resolve(res.data) 
      }).catch(err => { 
        reject(err.data) 
      }) 
    }) 
  } 
   
  /** 
   * post方法,对应post请求 
   * @param {String} url [请求的url地址] 
   * @param {Object} params [请求时携带的参数] 
   */ 
  export function post (url, params) { 
    return new Promise((resolve, reject) => { 
      axios.post(url, params).then(res => { 
        resolve(res.data) 
      }).catch(err => { 
        reject(err.data) 
      }) 
    }) 
  } 
   
  /** 
   * del 方法,对应del 请求 
   * @param {String} url [请求的url地址]  
   * @param {Object} params [请求时携带的参数]  
   */ 
  export function del (url, params) { 
    return new Promise((resolve, reject) => { 
      axios.delete(url, params).then(res => { 
        resolve(res.data) 
      }).catch(err => { 
        reject(err.data) 
      }) 
    }) 
  } 
 
  export function waitsleep (method) { 
    const start = Number(Date.now()) 
    return new Promise((resolve) => { 
      (function selfRecursion () { 
        setTimeout(() => { 
          let flag 
          if (typeof method === 'function') { 
            flag = method() 
          } 
          if (typeof method === 'number') { 
            flag = Number(Date.now()) - start < method 
          } 
          if (flag) { 
            selfRecursion() 
          } else { 
            resolve() 
          } 
        }, 10) 
      })() 
    }) 
  }

routerFormat.js:动态路由函数

/** 
 * 构建动态菜单路由对象 
 * @param {Object} routes [后台返回路由数据] 
 */ 
export function formatRoutes (routes) { 
    console.log(routes) 
    let fmtRoutes = [] 
    routes.forEach(routes => { 
      if (routes.child) { 
        routes.child = formatRoutes(routes.child) 
      } 
   
      let fmtRoute = { 
        path: routes.permissionPath, 
        component: resolve => { 
          require(['@/components/admin/' + routes.permisionComponent + '.vue'], resolve) 
        }, 
        name: routes.permissionName, 
        nameZh: routes.permissionZh, 
        iconCls: routes.permisionIcon, 
        children: routes.child 
      } 
      fmtRoutes.push(fmtRoute) 
    }) 
    return fmtRoutes 
  } 
  

initAdmin.js:动态菜单项核心功能代码:

import {get, waitsleep} from '@/utils/http' 
import {formatRoutes} from '../utils/routerFormat' 
 
export default function initAdminMenu (router, store) { 
  if (store.state.adminMenus.length > 0) { 
    return 
  } 
 
  // 定时函数:定时函数实现异步方法模拟同步方法 
  waitsleep(() => store.state.uid === -1).then(() => { 
    // 动态权限请求 
    get('/permission/' + store.state.uid).then(resp => { 
    console.log(resp.date) 
    console.log(resp.date) 
    var fmtRoutes = formatRoutes(resp.date) 
    router.addRoutes(fmtRoutes) 
    store.commit('initAdminMenu', fmtRoutes) 
    }) 
  }) 
} 

动态菜单效果展示:

声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
排行榜
关注我们

一个IT知识分享的公众号