前端之H5與App互動總結
- 互動方式
- 封裝---讓頁面更簡潔、易維護
- 如何完美抓住異步呼叫的時機---發布訂閱模式
- 拓展和總結
互動方式
前端通過將自身的方法掛載到window物件上,App端可以找到并異步回呼,通過方法引數的形式將資料傳到前端,掛載到window上的方法名字需要兩端協議約定,
JS:
let token = '';
function foo(token_: string) {
token = token_;
}
//當App端呼叫getToken的時候觸發系結的foo方法
window.getToken = foo;
App:
// 偽代碼
window.getToken('123abc');
封裝—讓頁面更簡潔、易維護
封裝的目的:1、App有兩種系統(IOS、Android),如果直接寫在頁面上,會使頁面有很多冗余的判斷,
if ( /(Android)/i.test(navigator.userAgent)) {
//Android
//...
} else if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
//IOS
//...
}
所以我們單獨將IOS和Android封裝成兩個獨立的類,再通過工廠模式判斷當前的系統型別,自動生產對應的類(以下代碼統一用TS做演示)
TS:
class AppFactory {
static getUserInstance() {
//這里考慮到全域使用的情況
//所以采用單例的模式
if (this.isIos()) {
return IosUser.getInstance();
} else if(this.isAndroid()) {
return AndroidUser.getInstance();
} else {
return PcUser.getInstance();
}
}
private static isIos() {
return /(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)
}
private static isAndroid() {
return /(Android)/i.test(navigator.userAgent)
}
}
//現在,我們在頁面上再也不用寫if判斷了
//只需要這樣寫就可以拿到當前的系統對應的類了
AppFactory.getUserInstance()
下面我們繼續完善IOS類和Android類,我們需要遵循面向抽象類編程的思維,而不是具體的某一類,所以它們還需要一個公共的父類User,
TS:
//抽象父類
abstract class User {
protected some:string|null = null;
//通過init將協議約定的方法掛載到window上
//這里this指標會發生隱式系結到window,需要使用bind強制系結到自身
init() {
window.setSome = this.setSome.bind(this);
}
private setSome(some_: string) {
this.some = some_;
}
public getSome_() {
return this.some;
}
abstract applySome():void; //這是由H5主動呼叫App端的方法,由各自子類去實作
}
//Android類
class AndroidUser extends User{
//上面提到兩個類會使用單例模式,以下不在贅述
private static androidUser: AndroidUser|null;
private constructor() {
super();
}
static getInstance() {
if (!this.androidUser) {
this.androidUser = new AndroidUser();
return this.androidUser;
}
return this.androidUser;
}
public applySome() {
//someFunc兩端協議約定的方法名
window.someFunc.applySome()
}
}
//Ios類
class IosUser extends User {
private static iosUser: IosUser|null;
private constructor() {
super();
}
static getInstance() {
if (!this.iosUser) {
this.iosUser = new IosUser();
return this.iosUser;
}
return this.iosUser;
}
public applySome() {
//對比AndroidUser類的applySome方法
//可以看出呼叫App端方法時,兩個系統的處理方式有差別
window.webkit.messageHandlers.applySome.postMessage(null)
}
}
//底層封裝完畢后,在頁面呼叫就會非常清晰、優雅
//例如我們需要拿some這個欄位
AppFctory.getUserInstance.init(); //這一段代碼在全域只需要初始化一次
let some = AppFctory.getUserInstance.getSome();
如何完美抓住異步呼叫的時機—發布訂閱模式
上面的代碼,已經可以支持我們優雅的在頁面與App端進行互動,但是還存在一個嚴重的Bug,App端是異步呼叫我們的方法,按照上面的寫法,App端還沒呼叫我們的方法就進行賦值操作,這樣是肯定拿不到值的,
如何解決呢?這里我使用的是發布訂閱模式,當監聽到App端呼叫我們的方法后,通知頁面,
TS:
//首先我們需要一個調度中心的類 NotificationCenter
class NotificationCenter {
public eventId: symbol;
constuctor() {
this.eventId = Symbol('eventId');
}
public register<T extends Event>(observe: object|symbol, event: Class<T>, cb: ()=>void) {
let map = (<any>event).prototype[this.eventId]
= (<any>event).prototype[this.eventId] || new Map<object|symbol, ()=>void>();
map.set(observe, cb);
}
public notify(event: Event) {
let map: Map<object|symbol, () => void>
= (<any>event)[this.eventId] || new Map();
for (let [key, value] of map) {
value();
}
}
public unRegister(evemt: Event, observe: object|symbol) {
let map = Map<object|symbol, () => void>
= (<any>event)[this.eventId] || new Map();
if(!map.has(observe)) {
return;
}
map.delect(observe);
}
}
interface Class<T> {
prototype: T;
}
這里實作方式大家可以不用太在意細節,主要的思想還是運用發布訂閱模式的注冊和監聽,將這個類掛到我們的User類上,在頁面注冊監聽,當方法被App端呼叫后,在最后發起通知,這樣就可以監聽到方法被成功呼叫了,
TS:
class User {
public nc_: NotificationCenter = new NotificationCenter();
public setSome() {
//...
this.nc_.notify() //通知頁面方法已經呼叫完畢
}
}
//頁面
AppFactory.getUserInstance.nc_.register(..., () => {
//監聽到變化后執行
let some = AppFactory.getUserInstance.getSome_();
})
拓展和總結
以上基本的架構就成型了,可能互動的時候會通過JSON的形式,那么就需要定義一個JSON類來處理格式等等,可以很好的拓展和集成,其實兩端互動本質上很簡單,但如果互動非常頻繁就會導致頁面冗余判斷過多,難于維護,以上是我自己的一點經驗和總結,也算是拋磚引玉,如有不足的地方,希望大家多多指正、評價,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/255885.html
標籤:其他
