CV 系列 - 微信网页登录回顾 - Vue Vue-Router Vuex Vant-ui Axios Webpack 串讲

923 阅读5分钟

好久没搞 Vue 了,以前都是很随便的糊 Vue 代码,这次认真糊了一下,发出来给带家一起回顾一下康康对不对 致力于 CV 推广,CV 越快,下班越快

这里只做最简洁的也比较实用的配置,各位带佬根据自己的实际需求去修改

有错或可以优化的地方还请各位老师帮忙指出,各位带善人点个赞吧。

Vuex 配置

在 src 目录下新建 store 文件夹,里面再建两个文件,store.js 和 md_wxuser.js

我的主文件基本上都跟文件夹同名,如果我用 index 替代 store以及其他文件也是这样的风格,这会让我在文件栏那里看到一堆的 index.js,切换文件时会发现,这踏马一堆的 index.js 谁是谁啊,以下其他文件也是如此

// md_user.js
const UPDATEWXUSER = 'UPDATEWXUSER' // 我也不知道为什么要这样写,大家都这么写我也就这么写了,有没有带佬给弟弟我解释一下
export default {
  state: {
    // 不是在微信开发者工具里测试或开发登录功能时,我会把 wxuser 给注释掉,把下面注释掉的放出来,这样就直接在 Chrome 里面开发就可以了
    wxuser: null,
    // wxuser: {
    //   nickname: 'WSND',
    //   sex: '1',
    //   province: '广东',
    //   city: '深圳',
    //   country: '中国',
    //   unionid: 'xxxxxxxxxxxxxxxxxxxx',
    //   role: 'lecturer',
    //   access_token: 'dddddddddddddddddddd'
    // }
  },
  mutations: {
    // 这里写个 mutation,之后会在 vue-router 的生命周期里面使用
    // 存储微信用户信息
    [UPDATEWXUSER](state, value) {
      state.user = value
    }
  },
  actions: {
    // 习惯了用 actions 来 commit mutation,actions 做数据更新操作可以使用 async await 的好处等等。
    updateWxuser({ commit }, value) {
      commit(UPDATEWXUSER, value)
    }
  },
  getters: {
    // 就返回上面的 wxuser 没啥好讲的......
    wxuser: state => state.wxuser
  }
}
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import md_wxuser from './md_user.js' // 导入上面我们写好的 md_user.js

Vue.use(Vuex)

const store = new Vuex.Store({
  modules:{ // 使用 modules 方式建立 store 以后项目大了会让你的项目比较清晰
    wxuser: md_user
  }
})

export default store

vuex 的配置到此结束

Axios 配置

在 src 文件夹下建立 api 文件夹,文件夹下建立三个文件,http.js,install.js,interface.js

// http.js
import axios from 'axios'
import QS from 'qs'
import store from '../store/store' //导入 Vuex store,
import { Toast } from 'vant' // 导入 vant 的提示组件

// 请求超时时间 -> 1分钟
axios.defaults.timeout = 60000

// 请求拦截器
axios.interceptors.request.use(
  config => {
    const { access_token } = store.getters.wxuser || {} // 假设你与后台通信验证是 access_token 字段
    // 验证没那么高要求的基本上都是在 header 配置的吧,可能你们配置 token 或者 openid,这里举例 access_token
    config.headers['access_token'] = access_token || ''
    
    // 这里举个这个例子想告诉大家配置都不一定要按照我所有的代码
    // 具体看你们实际的方案来改,给大家展示的只是一个思路
    // 自己可以灵活更改一点,此处因为我们的后台需要我 access_token 通过 post 请求 header 为 content-type
    // application/x-www-form-urlencoded 透过 post 的 data 传过去
    // config.data = QS.stringify({
    //   ...QS.parse(config.data),
    //   access_token
    // })
    return config
  },
  error => {
    return Promise.error(error)
  }
)

// 响应拦截器
axios.interceptors.response.use(
  response => {
    if (response.status === 200) {
      // 这里看个人需求,如果后端规范的话,就在这里做 Toast 轻提示,这里的 Toast 用的是 vant 的组件
      // const { code, msg } = response.data
      // if (code === -1) {
      //   Toast.fail(msg)
      // }
      return Promise.resolve(response)
    } else {
      return Promise.reject(response)
    }
  },
  // 服务器状态码不是200的情况
  error => {
    if (error && error.response) {
      switch (error.response.status) {
        case 400:
          error.message = '请求错误'
          break
        case 401:
          error.message = '未授权,请登录'
          break
        case 403:
          error.message = '拒绝访问'
          break
        case 404:
          error.message = `请求地址出错: ${error.response.config.url}`
          break
        case 408:
          error.message = '请求超时'
          break
        case 500:
          error.message = '服务器内部错误'
          break
        case 501:
          error.message = '服务未实现'
          break
        case 502:
          error.message = '网关错误'
          break
        case 503:
          error.message = '服务不可用'
          break
        case 504:
          error.message = '网关超时'
          break
        case 505:
          error.message = 'HTTP版本不受支持'
          break
        default:
          error.message = '请求错误'
          break
      }
    }
    // Toast 提示错误
    Toast.fail(error.message)
    return Promise.reject(err)
  }
)

// get 和 post 方法都用 Promise 封装,resolve 和 reject 拿到响应数据或处理错误

/**
 * get方法,对应get请求
 * @param {String} 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) => {
    // 防呆解释,这里我把 params 格式化是因为我们的后台要求 Content-Type 为 application/x-www-form-urlencoded
    // 如果你们的 Content-Type 为 application/json 的话就不用做 QS.stringify(params) 的处理
    let data = QS.stringify(params)
    axios({
      url,
      method: 'post',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      data
    })
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}
// interface.js
// 这个文件只举简单的例子
import { get, post } from './http' // 导入我们封装好的 get post

// 懒得在 dev prod env 设置 BASE_URL 的同学 直接在这里写吧
const BASE_URL = 'http://192.168.1.xxx:8040'

// 这里 return 回来的是我们直接写的 post,post return 出来的就是 Promise对象,那么 loginAndGetUser return 回来了 Promise,
// 我们就可以在组件里用 await 的方式简洁的写,let res = await this.$api.loginAndGetUser(),
// 在业务里减少回调可以帮我们更好的理解代码,有时候 await 也可以让我们按顺序完成异步请求
export const loginAndGetUser = params => {
  return post(`${BASE_URL}/getWaitChat`, {
    ...params
  })
}

// 这里我们导出一个对象方便装在 Vue 上调用
export default {
  loginAndGetUser
}
// install.js
import apiList from './interface'

const install = Vue => {
  if (install.installed) return

  install.installed = true

  Object.defineProperties(Vue.prototype, {
    // 此处挂载在 Vue 原型的 $api 对象上
    // 在组件实例里面 直接 this.$api.loginAndGetUser() 调用Api
    $api: {
      get() {
        return apiList
      }
    }
  })
}

export default install

Vue-Router 配置

src 下新建 router 文件夹,文件夹下新建 router.js 文件

import Vue from 'vue'
import Router from 'vue-router'
import store from '../store/store' // 导入 store
import { post } from '../api/http' // 导入封装好的 post
import { Dialog } from 'vant' // 此处本人最近的项目需要,非必须

Vue.use(Router)

const router = new Router({
  mode: 'history',
  base: 'msgmanage', 
  // 提醒,用了 base,记得去/config/index.js 下面 assetsPublicPath 
  // 改为 '/xxxxxx/' 这里的xxxx跟你的 base 同名,例如 assetsPublicPath:
  // '/msgmanage/',理解不了的去问你们的后端为什么这样做,这里不做解释
  routes: [
    {
      path: '/',
      name: 'index',
      component: () => import('../pages/index/index.vue')
    }
  ]
})

// 在 router 里做登录处理
// 微信网页登录原理这里就不讲了,只讲怎么优化流程
router.beforeEach(async (to, from, next) => {
  const appid = 'xxxxxxxxxxxxxxxxxx' // 你的公众号appid
  const redirect_uri = `${window.location.href}` // redirect_uri 相当于访问的域名,这样写就可以了
  const {
    query: { code = null }, // 根据实际需要,这里可以只把 url 里的 code 结构出来就可以了
    name,
    path,
    meta
  } = to // to 里面的对象可以自行打印出来,取出你会用到的东西
  // 我们之前在 vuex store 里存储了 wxuser 数据页面一刷新,wxuser就没了,所以我们这里做一下页面本地存储
  // 如果刷新页面的话直接从 sessionStorage 读回来,
  if (sessionStorage.getItem('wxuser')) {
    // 存储用户信息
    store.dispatch('updateUserInfo', JSON.parse(sessionStorage.getItem('wxuser')))
  }
  // 如果我们是正常进来这里的 wxuser 是 null
  const wxuser = store.getters.user
  // 我们在本地做开发的时候上面的 wxuser 就不是 null ,不会执行这里面的微信网页登录业务逻辑
  if (wxuser === null) {
    // 如果没有 code 跳转 获取 code
    if (!code) {
      return (window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=123&connect_redirect=1#wechat_redirect`)
    }
    // 用微信的 code 跟后台登录,把data 和 result 解构出来
    let { data, result } = await post(
      'http://192.168.1.xxx:8040/login',
      {
        code
      }
    )
    // 我们后台规定正常和成功的请求时result.errCode的值为0
    if (result.errCode === 0) {
      // 防止页面刷新做的存储,对应上面的页面刷新先读取sessionStorage
      sessionStorage.setItem('wxuser', JSON.stringify(data))
      // 存储至 vuex store
      store.dispatch('updateUserInfo', data)
    } else {
      // 个人业需求 未授权使用 vant 的 Dialog 组件提示 并且 return 直接页面白屏中断后续
      Dialog.alert({
        title: '未授权',
        message: `您的授权码是

        ${result.errMsg} 

        复制您的授权码发送给管理员完成授权后重新打开该网页`
      })
      return
    }
  }

  next() // 不要忘了 next()
})

export default router

Webpack 配置

要在微信开发者工具正常使用微信跳转微信登录需要修改 webpack 的配置以及本地系统上的 hosts 静态映射

// /config/index.js
module.exports = {
    dev: {
        host: '192.168.1.140', // 改为你本地的ip地址
        port: 80,// 端口改为 80 端口
    }
}
// /build/webpack.dev.conf.js
const devWebpackConfig = merge(baseWebpackConfig, {
    devServer: {
        disableHostCheck: true // 加上这个属性
    }
})
// 本地系统 hosts 文件
192.168.1.140        xxx.testest.com 
# 这里的 xxx.testest.com  域名去找你们后端拿,不给你就让他爬,其他的方法弟弟我也不懂
# 在微信开发者网页开发模式浏览器地址栏直接输入xxx.testest.com 就可以映射到你的项目了

main.js

最后这里直接粘贴复制吧,没啥好讲的

import Vue from 'vue'
import App from './App'
import router from './router/router' // 导入 router
import store from './store/store' // 导入 vuex 的 store
import api from './api/install' // 导入接口
import vant from 'vant' // vant 挺好用的,推荐使用

Vue.use(vant)


Vue.use(api)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

有错或可以优化的地方还请各位老师帮忙指出,各位带善人点个赞吧。