太原网站制作_太原网站建设_太原做网站_秘密武器开发者中心
历史搜索

如何在Vue3项目中实现微信授权登录

游客2025-01-23 09:30:01
目录文章目录
  1. 前言
  2. 准备工作
  3. 实现思路
  4. 上代码
  5. 总结

如何在Vue3项目中实现微信授权登录 1

上代码

根据以上思路,现在开始编码环节。笔者采用的是 Vue3,Vue2 的开发者还请根据情况做适当调整。

为了方便唤起用户授权登录逻辑,笔者打算将授权登录封住为一个 login 页面,这样做的好处是我们在任何判断到需要登录的地方直接通过 Vue Router 的 push 方法跳转到登录页面即可。

通常情况下,我们的应用并不是所有页面都需要登录后才能访问,只有在访问特定页面时候,才需要用户登录,那么我们就需要标识哪些页面需要进行登录鉴权。这里我们可以利用 Vue Router 的 meta 属性来进行标识,官方文档对 meta 解释如下:

有时,你可能希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到。

刚好 Vue Router 官方就有示例,如下:

const routes = [
  {
    path: '/posts',
    component: PostsLayout,
    children: [
      {
        path: 'new',
        component: PostsNew,
        // 需要登录后才能访问的页面
        meta: { requiresAuth: true }
      },
      {
        path: ':id',
        component: PostsDetail,
        // 任何人都可访问的页面
        meta: { requiresAuth: false }
      }
    ]
  }
]

接下来我们就可以在 Vue Router 的全局守卫beforeEach中获取到这个元信息从而做登录跳转了。

router.beforeEach((to, from) => {
  // 而不是去检查每条路由记录
  // to.matched.some(record => record.meta.requiresAuth)
  if (to.meta.requiresAuth && !userStore.isLogin) {
    // 此路由需要授权,请检查是否已登录
    // 如果没有,则重定向到登录页面
    return {
      path: '/login',
      // 保存我们所在的位置,以便以后再来
      query: { redirect: to.fullPath },
    }
  }
})

需要补充说明的是,userStore.isLogin的实现。这里和我们实际采用的登录态维护方案有关,如果是采用token方式的话,那就是检查token是否已经存在。笔者采用了vuex作为来保存token,然后借助插件来将Store中的数据持久化到localStorage

接下来我们来看具体的实现:

login.vue: 登录组件

<template>
  <div ></div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

import { jump2Auth, getUserInfo } from '@/hooks/useWechatAuth'
import { userStore } from '@/store/modules/user'
import { redirectTo, getRouteQuery } from '@/hooks/usePage'

export default defineComponent({
  name: 'Login',
  setup() {
    let code = getRouteQuery().code as string
    // 3.如果有 code,则已经授权
    if (code) {
      getUserInfo(code as string).then((res: any) => {
        // 记录 token
        userStore.saveToken(res.access_token)
        const redirect = userStore.userState.landPageRoute || '/'
        // 跳转到授权前访问的页面
        redirectTo(redirect)
      })
    } else {
      // 1.记录上一个页面的地址
      const { redirect } = getRouteQuery()
      if (redirect) {
        userStore.setLandPage(redirect as string)
      }
      // 2.跳转授权
      const callbackUrl = window.location.origin + window.location.pathname
      jump2Auth(callbackUrl)
    }
  },
})
</script>

可以看到,login 页面其实并没有什么内容,跳转到该页面后,我们会直接重定向到微信授权的页面,授权回调回来也会回到该页面,此时我们再通过获取路由参数的方式获取 code 参数。

@/hooks/usePage.ts: 该文件主要是封装了 router 相关的常用方法。

import router from '@/router'
import { cloneDeep } from 'lodash'
import { toRaw } from 'vue'

/**
 * 重定向
 * @param path 路径
 */
export function redirectTo(path: string) {
  const { replace } = router
  replace({
    path,
  })
}

/**
 * 获取路由上 query 参数
 */
export function getRouteQuery() {
  const { currentRoute } = router
  const { query } = currentRoute.value
  return cloneDeep(query)
}

@/hooks/useWechatAuth.ts:该文件封装了微信授权与后端交互的请求。

import { useAxios } from '@/hooks/useAxios'

/**
 * 获取微信授权的跳转地址
 * @param callbackUrl 授权后回调链接
 * @returns
 */
export function jump2Auth(callbackUrl: string) {
  useAxios({
    url: '/api/wechat/auth',
    params: {
      redirect_url: callbackUrl,
    },
  }).then((authUrl: any) => {
    if (process.env.NODE_ENV === 'development') {
      window.location.href = callbackUrl + '?code=test'
    } else {
      window.location.href = authUrl
    }
  })
}

/**
 * 提交 code 进行登录
 * @param code
 * @returns
 */
export async function getUserInfo(code: string) {
  const userInfo = await useAxios({
    method: 'POST',
    url: '/api/wechat/auth',
    params: {
      code,
    },
  })
  return userInfo
}

@/store/modules/user.ts: 全局状态存储,主要是记录 token 和登录前访问页面。

import { Module, VuexModule, Mutation, getModule, Action } from 'vuex-module-decorators'
import store from '@/store'
import { initialUnencryptedStorage } from '../globals'

interface UserState {
  token: string
  landPageRoute: string
}

const NAME = 'user'
// name: 模块名字
// namespaced 表示开启命名空间
// dynamic 设置为 true 时,表示创建动态模块,运行时将模块注册到存储中
// preserveState 如果数据有持久化,该变量为 true 时可以从 storage 中拿取初始值
@Module({
  namespaced: true,
  name: NAME,
  dynamic: true,
  store,
  preserveState: Boolean(initialUnencryptedStorage[NAME]),
})
export class User extends VuexModule {
  userState: UserState = {
    token: '',
    /** 登录前访问页面 */
    landPageRoute: '',
  }

  get isLogin(): boolean {
    return !!this.userState.token
  }

 
  @Mutation
  saveToken(token: string): void {
    this.userState.token = token
  }

  @Mutation
  setLandPage(route: string): void {
    this.userState.landPageRoute = route
  }
}

export const userStore = getModule(User)

笔者借助vuex-persistedstate插件将store中数据存储到了localStorage,这样做的好处是用户关闭页面后,再次访问,即不用重新触发微信授权流程,大大优化了用户体验。

总结

不得不说,Vue3 在代码抽象和复用这块上,写起来着实舒服很多,希望大家也也尝试着按照官方实践那样,多将逻辑代码解耦抽离,生成一个个的 hook,这样代码就显得优雅多啦。该方案经过笔者尝试论证,不论是代码整洁优雅程度,还是业务需求的实现上,都几乎完美(请容我装一波 b)。当然,这里可能存在我没发现的 bug 或者痛点,毕竟从来没有十全十美的架构嘛,这里也欢迎看官大佬们和我交流探讨,提供更好的方案和想法。

标签:vue3

本文是由用户"游客"发布,所有内容的版权归原作者所有。没有经过书面许可,任何单位或个人不得以任何形式复制、转载、引用本网站的内容。否则将追究法律责任。

相关专题