微信小程序 【关键部分】

1. 动机

最近在开发小程序,小程序既需兼顾针对新用户的内容预览,又要为注册用户提供服务,简单梳理下,基本需求如下:

  1. 小程序共三个tab页,所有用户都可以浏览首页内容,了解我们可以提供的优质服务;
  2. 进入其他两个页面之后,如果用户没有登录,那就显示登录按钮,如果登录了,则显示服务内容;
  3. 用户在一个页面登陆之后,全局生效。

就这么个看起来很简单的需求,也经过了如下迭代:

  1. 将登录状态和凭据存储在 App.globalData.authorize 中,每个需要授权的页面 onload 生命周期检查 App.globalData.authorize.authorized ,为 true 时渲染服务内容,为 false 则显示登录按钮;
  2. 但如果打开了需要授权的页面 A 但是没有登录,再打开页面 B 登录,这时候回到 A 页面,登录按钮赫然在眼,这是因为 A 页面的 onload 回调函数只执行了一次;
  3. 为了能在 A 页面及时共享 B 页面登录后的状态,在 A 页面的 onshow 生命周期里再获取了一次登录状态,但这样一来,打开 A 页面的时候,会出现短暂的白屏,用户甚至有可能看到按钮变成服务内容的整个过程。

翻遍小程序 API 文档 ,也没有发现用于监听登录的生命周期,就算有也用不了,因为我们有着自己的账号体系,服务端认证完毕才算真正的登录成功。
所以我决定自己包装原有的 Page 函数,添加一个 onauth 生命周期——

2. 事件总线

首先是自定义登录事件的触发与监听,官方的 EventChannel 需要向后兼容,横竖是个订阅回调,那我还不如自己撸一个得了:

/*** @file utils/event.js*//*** @const EMPTY_HANDLER* @desc 空事件回调,被取消事件将被指向此函数*/
const EMPTY_HANDLER = () => {};/*** @const eventSet - 事件监听函数集*/
const eventSet = {authorize: []
};/*** @function emit - 发送全局事件* @param {String} type - 事件类型* @param {Object} event - 事件对象*/
export const emit = (type, event) => (eventSet[type] || []).forEach(item => item(Object.freeze(event)));/*** @function on - 注册全局事件* @param {String} type - 事件类型* @param {Function} callback - 事件回调函数*/
export const on = (type, callback) => {if (!eventSet[type]) {eventSet[type] = [];}if (!callback instanceof Function) {throw new Error('callback must be a Function!');}return eventSet[type].push(callback)
};/*** @function off - 取消对某事件的监听* @param {String} type - 事件类型 * @param {Number} id - 需要取消的事件ID,即 registEvent 所返回的值*/
export const off = (type, id) => {if (!eventSet[type]) returneventSet[type][id - 1] = EMPTY_HANDLER// 如果某类事件已经全被取消的话,将其置为空数组const noListener = !eventSet[type].reduce((pre, cur) => (cur && cur === EMPTY_HANDLER) || pre, false);if (noListener){eventSet[type] = []};
}

有关订阅-回调的知识请自行百度,简而言之就是一个杂志亭,订阅者订阅了某款杂志,当发布者发布该款杂志的时候,杂志亭就将杂志送到订阅者手里,双方需要约定的就是杂志名称一致,也就是 on方法和 emit 方法的第一个参数。

3. 定义AuthPage

然后是对 Page 函数的魔改:

/*** @file utils/auth-page.js*/import { on } from '/event.js';export const AuthPage = function(options){const { onAuth, data, onLoad } = options;const userInfo = {nickName: '',  // 昵称account: '',  // 账号avatar: {  // 头像small: '',middle: '',large: ''},title: 'student',  // 头衔phoneNumber: 0,  // 电话号码gender: 'secret',  // 性别'class': ''  // 班级}if (options.data){options.data.authorized = false;options.data.userInfo = userInfo} else {options.data = {authorized: false,userInfo: userInfo}}/*** 仍旧调用原始的 Page 方法*/Page(Object.assign(options,{onLoad: function(...arg) {const { authorize, userInfo } = getApp().globalData;// 执行开发者期望的 onload 事件onLoad instanceof Function && onLoad.bind(this)(...arg);// 页面初始化时,若已经授权,直接执行授权回调// 否则将授权回调注册为授权事件回调if (onAuth instanceof Function){if (authorize.authorized){onAuth.bind(this)({type: 'authorize',authorized: true,token: authorize.token,userInfo: userInfo});} else {on('authorize', onAuth.bind(this));}}}}));
}

4. 触发登录事件

自定义的 onAuth 生命周期里订阅了一个 authorize 事件,登录之后需要发布相应的事件来触发其回调,触发的函数需要写在登录组件里:

import { emit } from '../../utils/event.js';wx.login({success: res => {// ...这里省略了一些复杂的登录流程getApp().globalData.authorize = {authorized: true};emit('authorize', res);}
})

然后,在两个需要登录的 tab 页引入 AuthPage 替换原有的 Page 函数,并在配置项里写 onAuth 回调,就可以监听登录事件了。


补充:

5.适用范围与最佳实践

5.1 适用范围
  1. 部分页面有权限控制,未登录用户与登录用户可以查看的内容不一样,或者不同级别的用户看到的内容不一样;
  2. 除了触发登录事件的页面,其他页面的权限修改过程不能直接呈现给用户,如果没有这个限制的话,判断登录这事可以在 onShow 函数里完成;
  3. 登录机制仅作为一个组件嵌入需要登录入口的所有页面,而不是单独做一个登录页,因为跳转页面这个操作,肯定是直接呈现给用户的了;
  4. AuthPage 不会过多,且切换权限的时候不会有非常耗时的操作。
5.2 最佳实践
    // 页面 AAuthPage({onLoad: function(){const { authorized } = getApp().globalData.authorize;let showText = '你还没有登录';if(authorized){showText = '你已经登陆了';}this.setState({ showText});},onAuth: function(){this.setState({showText: '你已经登陆了'})}});// 页面 B AuthPage({onLoad: function(){const { authorized } = getApp().globalData.authorize;if(authorized){this.setState({showLogin: false,  // 隐藏登录组件showContent: true  // 展示内容组件})} else {this.setState({showLogin: true,  // 展示登录组件showContent: false  // 隐藏内容组件})}},onAuth: function(option){this.setState({showLogin: false,  // 隐藏登录组件showContent: true  // 展示内容组件})}});

这样写的好处就是:用户未登陆的时候,也可以看到 A 页面的一些内容,使得用户可以初步了解应用内容;用户在 B 页面则会看到登录入口;登录之后 B 页面的登陆组件就被隐藏,取代的是应用内容,而此时 A 页面其实已经在后台悄悄更新了,用户对此是毫无感知的(除非后台更新的页面内容过多导致页面卡顿)。

6 总结

由于先前没有给定用例,需要理解整个流程才能上手编码,导致“懂的人不屑用,不懂的人不会用”的尴尬,所以亡羊补牢,增补了使用范围和用例。

仅供参考!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/6974.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Microsoft Remote Desktop Beta for Mac:远程办公桌面连接工具

Microsoft Remote Desktop Beta for Mac不仅是一款远程桌面连接工具,更是开启远程办公新篇章的利器。 它让Mac用户能够轻松访问和操作远程Windows计算机,实现跨平台办公的无缝衔接。无论是在家中、咖啡店还是旅途中,只要有网络连接&#xff0…

鸿蒙UI复用

鸿蒙UI复用 简介BuilderBuilder的使用方式一Builder的使用方式二Builder的使用方式三 Component使用Component复用UI 简介 在页面开发过程中,会遇到有UI相似的结构,如果每个UI都单独声明一份,会产生大量冗余代码,不利于阅读。遇到…

Python头歌合集(题集附解)

目录 一、Python初识-基本语法 第1关:Hello Python! 第2关:我想看世界 第3关:学好Python 第4关:根据圆的半径计算周长和面积 第5关:货币转换 二、turtle简单绘图 第1关:英寸与厘米转换 第2关&#xff1…

鸿蒙应用开发系列 篇二:鸿蒙系统开发工具与环境

文章目录 系列文章硬件与软件需求DevEco Studio扩展工具与框架开发资源系列文章 鸿蒙应用开发系列 篇一:鸿蒙系统概述 鸿蒙应用开发系列 篇二:鸿蒙系统开发工具与环境 (系列计划预告) 鸿蒙系统UI/UX设计 鸿蒙系统应用开发基础 鸿蒙系统高级开发技术 鸿蒙系统特色功能开发 …

线程池和CountDownLatch搭配使用

一,CountDownLatch CountDownLatch是Java并发编程中用于线程间协调的一个同步辅助类。它通过一个初始计数值来控制线程的等待,这个计数值在其他线程执行特定任务时递减。 初始化:创建CountDownLatch实例时,你需要指定一个整数值&…

vue-cli+vue3+vite+ts 搭建uniapp项目全过程(一)

unapp官方提供了cli 脚手架创建 uni-app 项目的文档 Vue3/Vite版要求 node 版本 18、20使用Vue3/Vite版创建不会提示选择模板,目前只支持创建默认模板 本文以vue3vitets为例 1、初始化项目 npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project 执行完生成…

037——加入Kconfig机制

目录 一、什么是Kconfig 1.1 由来 1.2 功能 二、 Kconfig的基本语法 2.1 Kconfig 构建项目解析 2.2 怎么调用子makefile做menuconfig 方法一:使用make命令直接调用子目录 方法二:使用变量来指定子目录 方法三:使用include指令包含子…

在RStudio上用Git功能管理Github上的项目

在本教程中,你将学习以下内容: 1. 创建一个新的仓库。 2. 使用 Git 和 RStudio 在本地机器上使用你的仓库。 3. 使用 GitHub.com 网站或 GitHub Enterprise 完成 GitHub 工作流程。 4. 使用 GitHub Pages 发布和共享你的 R Markdown 文档。 先决条件&a…

Java openrasp记录-02

主要分析以下四个部分: 1.openrasp agent 这里主要进行插桩的定义,其pom.xml中定义了能够当类重新load时重定义以及重新转换 这里定义了两种插桩方式对应之前安装时的独立web的jar的attach或者修改启动脚本添加rasp的jar的方式 其中init操作则需要将ras…

大数据技术主要学什么,有哪些课程

大数据技术是指在海量数据的环境下,采集、存储、处理、分析和管理数据的一系列技术与方法。随着互联网、物联网以及各种智能设备的普及,数据量呈爆炸性增长,传统数据处理手段已难以应对,因此大数据技术应运而生,旨在从…

加州大学欧文分校英语中级语法专项课程04:Intermediate Grammar Project学习笔记(完结)

Intermediate Grammar Project Course Certificate Specialization Certificate Specialization Intro Course Intro 本文是学习 Coursera: Intermediate Grammar Project 这门课的学习笔记。 文章目录 Intermediate Grammar ProjectWeek 01: IntroductionCapstone Introducti…

论文笔记:DeepMove: Predicting Human Mobility with Attentional Recurrent Networks

WWW 2018 1 Intro 根据对百万级用户群的研究,93%的人类移动是可预测的。 早期的mobility预测方法大多基于模式的。 首先从轨迹中发现预定义的移动模式(顺序模式、周期模式)然后基于这些提取的模式预测未来位置。最近的发展转向基于模型的方法进行流动性预测。 利用…

力扣:62. 不同路径

62. 不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径&…

16、Flink 的广播状态 (Broadcast State) 示例

1、Broadcast State 案例 规则流:1,a,b [规则名1 规则为 a 或 b] 图形流:green,a [绿色 a] 问题:如果规则流先于数据流则匹配不上>此时缓冲数据流中的数据【如果规则流为null】 import org.apache.flink.api.common.functions.MapFunct…

redis-内存回收机制

在 Redis 的源码中,redisDb 结构体用于表示一个 Redis 数据库实例。结构体大致如下 typedef struct redisDb {dict *dict; // 存储键值对的字典dict *expires; // 存储键的过期时间的字典dict *blocking_keys; // 阻塞键的字典…

子比主题小黑屋列表

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么?二、使用步骤1.引入库前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文…

五一假期后,必读的10篇大模型论文

1.同时预测多个 token:更好更快的大型语言模型 目前,GPT 和 Llama 等大型语言模型(LLMs)都是通过下一个 token 预测损失来训练的。 在这项工作中,来自 Meta FAIR 的研究团队认为,训练语言模型同时预测多个…

用 Go map 要注意这个细节,避免依赖他!

有的小伙伴没留意过 Go map 输出、遍历顺序,以为它是稳定的有序的,会在业务程序中直接依赖这个结果集顺序,结果栽了个大跟头,吃了线上 BUG。 有的小伙伴知道是无序的,但却不知道为什么,有的却理解错误? 今…

PADS 规则设置-导线不跟随器件-导线允许回路

1、PADS Layout中设置拖动器件时导线不跟着移动 2、PADS Router中设置走线允许回路

【隧道篇 / WAN优化】(7.4) ❀ 01. 启动WAN优化 ❀ FortiGate 防火墙

【简介】几乎所有的人都知道,防火墙自带的硬盘是用来保存日志,以方便在出现问题时能找到原因。但是很少的人知道,防火墙自带的硬盘其实还有另一个功能,那就是用于WAN优化。 防火墙自带的硬盘 在FortiGate防火墙A、B、C、D系列&…