2018-11-25 | weChat | UNLOCK

小程序登陆和常用Api

登陆流程的改变

原来我们在首次进入小程序时,会通过 getUserInfo 调起用户授权的弹窗,但是根据微信小程序,最新更新解释,开发工具,体验版本将不再支持这个授权方式而是通过 button 组件让用户自主去点击已完成授权目的(这个对开发者来说真的是很蛋疼),

解决方案: 设置一个用户登陆的过渡页面

  <button wx:if="{{canIUse}}" open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">授权登录</button>
  <view wx:else>请升级微信版本</view>  //让用户自己去点击这个授权登录的按钮,然后再去进行后续的业务逻辑

小程序登陆逻辑

wx.login() 去获取用户的 code 之后,发送 code 给后台换取 session_keyunionidopenid 这个请求的过程一般是在打开登陆界面的时候就已经调用了(此时已经取得了 session_key),当用户点击登陆的时候才会去调取登陆接口,拿获得 session_key 去换取用户信息,此时还拿到了服务器返回的 token,用于用户以后的登陆,作为登陆的另外一个依据

  • code 临时登录凭证, 有效期五分钟, 通过 wx.login() 获取
  • session_key 会话密钥, 服务端通过 code2Session 获取
  • openId 用户在该小程序下的用户唯一标识, 永远不变, 服务端通过 code 获取
  • unionId 用户在同一个微信开放平台帐号(公众号, 小程序, 网站, 移动应用)下的唯一标识, 永远不变
  • appId 小程序唯一标识
  • appSecret 小程序的 app secret, 可以和 code, appId 一起换取 session_key
// code 换取 session_key

async function fetchSessionKey(code) {
  const { session_key, unionid, token, openid = '', user = {} } = await request(
    {
      url: '/api/app/v1/miniapp/session',
      method: 'post',
      data: { code }
    }
  )
  user.session_key = session_key
  user.unionid = unionid
  user.openid = openid
  authority.set(user)
  if (token) {
    user.token = token
    authority.set(user)
    const { back } = getCurrentOptions()
    if (!back) return
    wx.reLaunch({
      url: decodeURIComponent(back)
    })
  } else {
    const back = getCurrentUrl()
    // 重定向登陆页面
    if (!back.startsWith('/pages/login/main')) {
      wx.redirectTo({
        url: `/pages/login/main?back=${encodeURIComponent(back)}`
      })
    }
    authority.set({ session_key, unionid })
  }
  return true
}

export default () => {
  return new Promise((resolve, reject) => {
    const user = authority.get() || {}
    if (user.token) return resolve(user) // todo
    const errorHandle = e => {
      console.error(e)
      wx.showToast({
        title: '获取code失败',
        icon: 'none'
      })
      reject(e)
    }
    wx.login({
      success({ code }) {
        fetchSessionKey(code)
          .then(resolve)
          .catch(errorHandle)
      },
      fail(e) {
        errorHandle(e)
      }
    })
  })
}

wx.getStorageSync And wx.setStorageSync

  1. 读取信息的时候要使用 try/catch 捕捉错误
  2. key 要唯一
  3. 清除数据之前要保存重要的信息: 用于登陆所需的信息
const key = 'user_info' // 定义储存的key,一定要确保是单一的
const maxAge = 1000 * 60 * 60 * 24 * 60 // 设置过期时间,60天过期

export default {
  get() {
    // 获取存储到localStorage中的数据,一般是用户信息,使用try/catch捕捉错误
    try {
      const user = wx.getStorageSync(key)
      if (!user || user.time + maxAge < Date.now()) return null
      // 如果储存的时间过期了,直接返回null,重新发送登陆接口获取
      return user || {}
    } catch (e) {
      return {}
    }
  },
  set(user) {
    if (!user) return null
    // 存储数据,没有数据直接返回null,外部捕捉这个null来判定是否成功
    user.time = Date.now()
    // 设置储存时间,用来判断是否过期
    const oldUser = this.get() || {}
    const newUser = { ...oldUser, ...user } //将以前的数据和新的数据一起整合在一起
    console.log(newUser, 'newuser')
    wx.setStorageSync(key, newUser) //微信存储数据的静态方法
    return newUser
  },
  clear() {
    const user = this.get() || {}
    const { regionCode = '', currentCity = '', shop_id = '' } = user
    // 即使是清除数据,也保存重要的登陆信息
    wx.clearStorageSync()
    this.set({ regionCode, currentCity, shop_id })
    // 清空完之后再次储存
    return user
  }
}

登陆信息存储于本地

const key = 'user_info' // 定义储存的key,一定要确保是单一的
const maxAge = 1000 * 60 * 60 * 24 * 60 // 设置过期时间,60天过期

export default {
  get() {
    // 获取存储到localStorage中的数据,一般是用户信息,使用try/catch捕捉错误
    try {
      const user = wx.getStorageSync(key)
      if (!user || user.time + maxAge < Date.now()) return null
      // 如果储存的时间过期了,直接返回null,重新发送登陆接口获取
      return user || {}
    } catch (e) {
      return {}
    }
  },
  set(user) {
    if (!user) return null
    // 存储数据,没有数据直接返回null,外部捕捉这个null来判定是否成功
    user.time = Date.now()
    // 设置储存时间,用来判断是否过期
    const oldUser = this.get() || {}
    const newUser = { ...oldUser, ...user } //将以前的数据和新的数据一起整合在一起
    wx.setStorageSync(key, newUser) //微信存储数据的静态方法
    return newUser
  },
  clear() {
    const user = this.get() || {}
    const { regionCode = '', currentCity = '', shop_id = '' } = user
    // 即使是清除数据,也保存重要的登陆信息
    wx.clearStorageSync()
    this.set({ regionCode, currentCity, shop_id })
    // 清空完之后再次储存
    return user
  }
}

小程序表单和 video

小程序的坑: textarea 和 video 当被激活时他们的层级永远是最高的,无法去处理
textarea 的处理方法: 当需要在 textarea 的上方加上一个弹窗活着页面时,需要手动的把 textarea 隐藏,换一种方法来实现
video 暂时还不知道怎么处理,同时还要注意视频的播放问题,在离开页面的时候要强制的关闭视频

小程序实现下拉刷新的操作

async onPullDownRefresh() {
    wx.showLoading();
    ....
    // 相应的上拉事件处理函数 
    setTimeout(() => {
      wx.hideLoading();
      wx.stopPullDownRefresh();
    }, 1000);
  },

页面触底之后的上拉操作

async onReachBottom() {
    // 首屏时只展示部分数据,未展示所有的数据
    // 判断当前的数据量是不是后台接口返回的总数据量
    if (this.List.length < this.total) {
    // 数据没有完全被加载,上拉一次再次加载一次数据
      this.pageData.page = this.pageData.page + 1;
      const List = this.List;
      const data = await fetchExpertsDetail(this.pageData);
      this.List = [...answerList, ...data.answer]; // 合并新请求的数据和老的数据
    } else {
    // 如果全加载了,给个提示
      console.log('到底啦');
    }
  },

小程序的路由跳转

要注意跳转到非 tabBar 页和 跳转到 tabBar 导航页

  1. 跳转到到导航页: wx.switchTab(Object object) 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
  2. 跳转到非导航页: wx.navigateTo(Object object) 保留当前页面,跳转到应用内的某个页面,使用 wx.navigateBack 可以返回到原跳转的页面,原页面没有被销毁。
  3. wx.redirectTo(Object object) 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。

跳转的 query 参数
     微信小程序的页面的 query 参数是通过 onLoad 获取的,mpvue 进行了优化,直接通过 this.$root.$mp.query 获取相应的参数数据,其调用需要在 onLoad 生命周期触发之后使用