前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅学前端:Vue篇(五)

浅学前端:Vue篇(五)

原创
作者头像
传说之下的花儿
发布2023-11-15 15:00:13
2090
发布2023-11-15 15:00:13
举报

这里选择了 vue-element-admin 这个项目骨架,它采用的技术与我们之前学过的较为契合

  • vue 2
  • element-ui 2
  • vue-router 3
  • vuex 3
  • axios

1. 安装

代码语言:javascript
复制
 git clone https://gitee.com/panjiachen/vue-element-admin.git study03_vue2_client_action
 ​
 cd study03_vue2_client_action
 ​
 # 列出所有分支
 git branch -a
 ​
 # 我们当前在的master分支是只支持英文的,需要切换分支
 # git checkout -b 创建并切换分支
 git checkout -b i18n remotes/origin/i18n
 ​
 # 将git的地址凡是以git://打头的,都替换为https://打头
 # 因为npm的过程需要访问以为git仓库,如果是git:// 打头,下载的时候可能会出现问题
 git config --global url."https://".insteadOf git://
 ​
 npm install
 ​
 npm run dev
  • 需要切换分支到 i18n,否则不支持国际化(中文)功能
  • npm install 要多试几次,因为中间会连接 gitbub 下载一些依赖,网络不稳定会导致失败
  • npm run dev 运行后回自动打开浏览器,使用的端口是 9527

2. 后端路径

此时系统已经运行起来了 ,会有同学有疑问,它没有后端服务器的支撑,是怎么完成整个登录的流程的呢,整个登录的流程是如何走通的呢?

实际上点击登录按钮之后,是会发一个真正的请求,只不过这个请求不是发给后台的,是发给9527自己的,9527里有一段自己的代码来处理请求,只不过他返回了一个mock的响应(假的响应),这个加的响应就包含了登录需要的一些模拟数据。

具体的跟着视频

开发环境下执行下面命令

代码语言:javascript
复制
 npm run dev
  • 会同时启动 mock-server

根据刚才登录发起的请求,通过后缀user/login可以找到两个文件:

我们想要让他不把请求发到自己mock的服务端,而是发给我们自己的后端,需要修改这个baseURL,根据刚才请求的前缀可以找到开发环境的baseURL在文件 .env.development 中:

在开发环境下,后端访问路径起始路径配置在文件 .env.development

代码语言:javascript
复制
 # base api
 # VUE_APP_BASE_API = '/dev-api'
 VUE_APP_BASE_API = 'http://localhost:8080/api'
  • 默认向后台的请求都发给 http://localhost:9527/dev-api 的 mock-server 获得的都是模拟数据
  • 需要跟真实后台联调时,可以改动以上地址为 VUE_APP_BASE_API = 'http://localhost:8080/api'

修改baseURL之后需要重启服务器

发送请求的 axios 工具被封装在 src/utils/request.js 中

代码语言:javascript
复制
 import axios from 'axios'
 import { MessageBox, Message } from 'element-ui'
 import store from '@/store'
 import { getToken } from '@/utils/auth'
 ​
 // create an axios instance
 const service = axios.create({
   baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
   // withCredentials: true, // send cookies when cross-domain requests
   timeout: 5000 // request timeout
 })
 ​
 // ...

原有代码的 URI 路径都是这样的:

代码语言:javascript
复制
 /vue-element-admin/user/login
 /vue-element-admin/user/info
 /vue-element-admin/user/logout
 ...

如果觉得不爽,可以来一个全局替换:

代码语言:javascript
复制
 /user/login
 /user/info
 /user/logout
 ...

token 的请求头修改一下,在 src/utils/request.js 中

代码语言:javascript
复制
 ...
 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['Authorization'] = getToken()
     }
     return config
   },
   error => {
     // do something with request error
     console.log(error) // for debug
     return Promise.reject(error)
   }
 )
 ...

登录流程

1. src/views/login/index.vue
代码语言:javascript
复制
 <script>
 import { validUsername } from '@/utils/validate'
 import LangSelect from '@/components/LangSelect'
 import SocialSign from './components/SocialSignin'
 ​
 export default {
   // ...
   methods: {    
     handleLogin() {
       this.$refs.loginForm.validate(valid => {
         if (valid) {
           this.loading = true
           this.$store.dispatch('user/login', this.loginForm)
             .then(() => {
               this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
               this.loading = false
             })
             .catch(() => {
               this.loading = false
             })
         } else {
           console.log('error submit!!')
           return false
         }
       })
     }
     // ...
   }
 }
 </script>

这里调用了 store 的 actions,user/login

  • 因为是异步调用,因此只能用 actions
  • 登录成功会优先跳转至 this.redirect 路径、否则跳转至 /
  • / 查看 src/router/index.js 的路由表可知,会重定向至 /dashboard
2. src/store/modules/user.js
代码语言:javascript
复制
 import { login, logout, getInfo } from '@/api/user'
 // ...
 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)
       })
     })
   }
   // ...
 }
  • 发请求用了 src/api/user.js,请求成功使用 commit 将 token 存入 mutations,同时往 cookie 存储了一份
  • 这里的 response 其实是真正的 response.data,见后面的说明
  • 评价
    • 向 cookie 或 sessionStorage 存储 token 即可,token 无需做成响应式,不必放入 store
    • 作者使用了 Promise API,其实可以改变为 await 方式,提高可读性
3. src/api/user.js
代码语言:javascript
复制
 import request from '@/utils/request'
 ​
 export function login(data) {
   return request({
     url: '/user/login',
     method: 'post',
     data
   })
 }
 ​
 // ...
  • 其中 request 相当于我们之前封装的 myaxios
4. src/utils/request.js
代码语言:javascript
复制
 import axios from 'axios'
 import { MessageBox, Message } from 'element-ui'
 import store from '@/store'
 import { getToken } from '@/utils/auth'
 ​
 // create an axios instance
 const service = axios.create({
   baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
   // withCredentials: true, // send cookies when cross-domain requests
   timeout: 5000 // request timeout
 })
 ​
 // ... 
 ​
 service.interceptors.response.use(
   // ...
   response => {
     const res = response.data
     if (res.code !== 20000) {
       // ...
     } else {
       return res
     }
   },
   error => {
     // ...
   }
 )
 ​
 export default service
  • 其中响应拦截器发现响应正确,返回 resp.data 这样,其它处代码解构时少了一层 data
5. src/permission.js

登录成功后,只是获得了 token,还未获取用户信息,获取用户信息是在路由跳转的 beforeEach 里做的

关键代码

代码语言:javascript
复制
 import router from './router'
 ​
 // ...
 ​
 router.beforeEach(async(to, from, next) => {
   // ...
   const hasToken = getToken()
 ​
   if (hasToken) {
     if (to.path === '/login') {
       // ...
     } else {
       // ...
       const { roles } = await store.dispatch('user/getInfo')
       // ...
     }
   } else {
     // ...
   }
 })
  • 登录后跳转至 / 之前进入这里的 beforeEach 方法,方法内主要做两件事
    • 一是调用 actions 方法获取用户角色,见 6
    • 二是根据用户角色,动态生成路由,见 7
6. src/store/modules/user.js

这里用其中 getInfo 方法获取用户信息,其中角色返回给 beforeEach

代码语言:javascript
复制
 import { login, logout, getInfo } from '@/api/user'
 // ...
 const actions = {
   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 { roles, name, avatar, introduction } = data
 ​
         if (!roles || roles.length <= 0) {
           reject('getInfo: roles must be a non-null array!')
         }
 ​
         commit('SET_ROLES', roles)
         commit('SET_NAME', name)
         commit('SET_AVATAR', avatar)
         commit('SET_INTRODUCTION', introduction)
         resolve(data)
       }).catch(error => {
         reject(error)
       })
     })
   }
 }
7. src/router/index.js

路由表中路由分成两部分,静态路由与动态路由

代码语言:javascript
复制
 export const constantRoutes = [
   // ...
   {
     path: '/login',
     component: () => import('@/views/login/index'),
     hidden: true
   },
   {
     path: '/',
     component: Layout,
     redirect: '/dashboard',
     children: [
       {
         path: 'dashboard',
         component: () => import('@/views/dashboard/index'),
         name: 'Dashboard',
         meta: { title: 'dashboard', icon: 'dashboard', affix: true }
       }
     ]
   }
   // ...
 ]
  • 其中 hidden: true 的路由只做路由跳转,不会在左侧导航菜单展示

动态路由

代码语言:javascript
复制
 export const asyncRoutes = [
   {
     path: '/permission',
     component: Layout,
     redirect: '/permission/page',
     alwaysShow: true, // will always show the root menu
     name: 'Permission',
     meta: {
       title: 'permission',
       icon: 'lock',
       roles: ['admin', 'editor'] // you can set roles in root nav
     },
     children: [
       {
         path: 'page',
         component: () => import('@/views/permission/page'),
         name: 'PagePermission',
         meta: {
           title: 'pagePermission',
           roles: ['admin'] // or you can only set roles in sub nav
         }
       },
       {
         path: 'directive',
         component: () => import('@/views/permission/directive'),
         name: 'DirectivePermission',
         meta: {
           title: 'directivePermission'
           // if do not set roles, means: this page does not require permission
         }
       },
       {
         path: 'role',
         component: () => import('@/views/permission/role'),
         name: 'RolePermission',
         meta: {
           title: 'rolePermission',
           roles: ['admin']
         }
       }
     ]
   },
 ​
   {
     path: '/icon',
     component: Layout,
     children: [
       {
         path: 'index',
         component: () => import('@/views/icons/index'),
         name: 'Icons',
         meta: { title: 'icons', icon: 'icon', noCache: true, roles: ['admin'] }
       }
     ]
   }
   // ...
 }
  • 动态路由中关联了角色信息,根据用户的角色决定那些路由可用,但这样做的缺点是把角色和路由绑定死了
8. src/layout/index.vue

它对应的是我们之前介绍的 Container.vue 完成主页布局的,路由路径是 /

其中又由多部分组成,其中固定不变的是

  • 侧边栏
  • 导航栏
  • 标签栏
  • 设置

变化的是中间的 dashboard 部分(AppMain),它由 router-view 配合子路由切换显示

  • 进入 / 后,就会 redirect 重定向到 /dashboard 子路由
  • 进入首页后,会有一个 /api/transaction/list 的后台请求报 404,作为练习,把它补充完整

第三方登录

  1. 9527 打开新窗口,请求 https://gitee.com/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=code
  2. gitee 认证通过,重定向至 8080,并携带 code
  3. 8080 发送请求 https://gitee.com/oauth/token 携带 client_id、client_secret、code,gitee 返回 access_token 给 8080
    • 这时走的是 https 协议,并且不经过浏览器,能够保证数据传输的安全性
    • 重定向到 8080 时,如果被有心人拿到了 code,也没事,因为接下来会把 client_secret 发给 gitee 验证(client_secret 应当只存在 8080),只要 client_secret 不泄露,就可以保证安全
    • 如果改成前端拿 code 换 access_token,那就意味着 access_token 得保存在前端,所有保存在前端的都有风险
  4. 8080 可以访问 gitee 的 api 了,拿到用户信息,存入数据库,返回 8080 的 token
  5. 8080 可以通过 window.opener.postMessage 把 token 给 9527 的老窗口
    • 这里又会涉及到跨域,不过 9527 与 8080 直接存在信任关系,设置一下就好
  6. 9527 再走之前的逻辑就可以了,在 router 的 beforeEach 方法里,用 8080 token 换用户信息

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 安装
  • 2. 后端路径
  • 登录流程
    • 1. src/views/login/index.vue
      • 2. src/store/modules/user.js
        • 3. src/api/user.js
          • 4. src/utils/request.js
            • 5. src/permission.js
              • 6. src/store/modules/user.js
                • 7. src/router/index.js
                  • 8. src/layout/index.vue
                  • 第三方登录
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档