Recently, in the development of applets, which take into account both content preview for new users and services for registered users, the basic requirements are as follows:
- The applet has three tab pages, so all users can browse the contents of the first page to see the quality of services we can provide.
- After entering the other two pages, if the user is not logged in, the logon button is displayed, and if the user is logged in, the service content is displayed.
- The user logs on to a page and takes effect globally.
This seemingly simple requirement has undergone the following iterations:
- Store login status and credentials in App.globalData.authorize, check the onload life cycle of each page that needs authorization, render service content for true, and display login button for false;
- But if you open page A that needs authorization but don't sign in, and then open page B to sign in, you will return to page A and the login button will be in your eyes, because the onload callback function of page A only executes once;
- In order to share the status of B page login in A page in time, we get another login status in the onshow life cycle of A page. However, when opening A page, there will be a short white screen, and users may even see the whole process of button becoming service content.
Looking through the applet API documentation, I don't find the life cycle to monitor login, even if it is not, because we have our own account system, and the server certification is complete before the real login success.
So I decided to wrap the original Page function myself and add an onauth lifecycle:
First, customize the triggering and monitoring of login events, officially EventChannel Need backward compatibility, vertical is a subscription callback, so I'm not as good as myself:
/** * @file utils/event.js */ /** * @const EMPTY_HANDLER * @desc Empty event callback, cancelled event will be pointed to this function */ const EMPTY_HANDLER = () => {}; /** * @const eventSet - Event Listening Function Set */ const eventSet = { authorize: [] }; /** * @function emit - Send global events * @param {String} type - Event Type * @param {Object} event - Event Object */ export const emit = (type, event) => (eventSet[type] || []).forEach(item => item(Object.freeze(event))); /** * @function on - Register Global Events * @param {String} type - Event Type * @param {Function} callback - event callbacks */ 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 - Cancel listening to an event * @param {String} type - Event Type * @param {Number} id - Event ID to cancel, the value returned by registEvent */ export const off = (type, id) => { if (!eventSet[type]) return eventSet[type][id - 1] = EMPTY_HANDLER // If an event of a type has been completely cancelled, set it to an empty array const noListener = !eventSet[type].reduce((pre, cur) => (cur && cur === EMPTY_HANDLER) || pre, false); if (noListener){ eventSet[type] = [] }; }
Then there's a magic change to the Page function:
/** * @file utils/auth-page.js */ import { on } from '/event.js'; export const AuthPage = function(options){ const { onAuth, data, onLoad } = options; const userInfo = { nickName: '', // Nickname? account: '', // Account number avatar: { // Head portrait small: '', middle: '', large: '' }, title: 'student', // Title phoneNumber: 0, // Phone number gender: 'secret', // Gender 'class': '' // class } if (options.data){ options.data.authorized = false; options.data.userInfo = userInfo } else { options.data = { authorized: false, userInfo: userInfo } } /** * Still calling the original Page method */ Page(Object.assign( options, { onLoad: function () { const { authorize, userInfo } = getApp().globalData; // Execute the onload event expected by the developer onLoad instanceof Function && onLoad.bind(this)(arguments); // When the page is initialized, if authorized, perform the authorization callback directly // Otherwise, the authorization callback will be registered as an authorization event callback 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)); } } } } )); }
Finally, in the login component:
import { emit } from '../../utils/event.js'; wx.login({ success: res => { // ...some complicated login processes are omitted here getApp().globalData.authorize = { authorized: true }; emit('authorize', res); } })
Then, you can listen for login events by introducing AuthPage in two tab pages that require login to replace the original Page function and writing onAuth callbacks in the configuration item.