引言
在现代前端开发中,我们经常会遇到一个问题:同一套业务代码需要运行在不同平台上,比如 iOS App、Android App、Web 端,甚至小程序。
每个平台都有自己的差异:有的支持硬件能力(相机、震动、保存图片),有的完全不支持。直接在业务层调用原生功能,很容易出错:报错、崩溃,或者逻辑不统一。
为了统一管理这些差异,我们可以设计一个 前端跨平台服务(PlatformService) ,而 IPlatform 就是这个服务的核心契约。
Platform 的作用:
- 1.定义标准接口:告诉业务层"我可以调用哪些功能"
- 2.提供安全兜底:即便某个平台不支持某个功能,也不会崩溃
- 3.支撑 统一入口 + 生命周期管理 + JSBridge 通信,让跨平台调用变得简单可靠
简单来说,
IPlatform就像一份"合同":业务层只关心接口,不用管底层每个平台的差异。
在本文中,我们将手把手讲解如何从 接口定义 → BasePlatform → 具体平台实现 → JSBridge → 统一入口 → 生命周期管理,完整搭建一个企业级前端跨平台架构。
1.定义标准interface
在动手写下一行代码之前,我们必须先跳出实现细节,思考一个核心问题:我们的平台究竟要对外提供什么样的能力?
定义标准 Interface(接口)不仅是写代码,它实际上是在制定协议。
它本质上就是在回答三个问题:
- 1.我要什么? (输入参数)
- 2.我给什么? (返回结果)
- 3.我保证能干什么? (功能定义)
所以第一步,我们是声明约定平台需要有哪些功能
我们只需要声明,一个标准的平台应该具备什么样的功能函数 (不需要实现:空函数)
这一层是抽象封装层,而不是具体实现
1.1功能分类
平台的功能大致能分为三类 平台信息类 、系统能力类 、 通信能力类
1. 平台信息类
这类接口主要用于获取环境数据,不涉及任何操作。
-
例如:
- 平台判断: 当前是 iOS 还是 Android?
- 设备信息: 屏幕宽高是多少?电量还剩多少?
- 网络状态: 当前是 Wi-Fi 还是 5G?
- 用户信息: 获取当前登录人的 ID 和 Token
2. 系统功能类
这类接口涉及调用硬件或底层能力,需要平台去执行动作。
-
例如:
- 拍照/相册: 唤起摄像头拍照,或从相册选图。
- 扫码: 调起系统原生的扫码界面。
- 地理位置: 获取经纬度坐标。
- 本地存储: 往手机本地存入或读取数据。
3. 通信能力类
这类接口负责处理事件和通知。
-
例如:
- 事件监听: 监听用户按了返回键,或监听网络断开。
- 消息推送: 在通知栏弹出消息。
- 实时进度: 文件上传或下载时的进度回调。
1.2封装interface示例
这样我们可以得到一个定义:一个标准平台应该符合的标准interface
js
// file: iplatform.ts
/**
* 平台能力分类:
* 1. 平台信息类(只读,描述性信息)
* 2. 系统能力类(有副作用,需要权限)
* 3. 通信能力类(底层 JSBridge)
*/
export interface IPlatformService {
// ==========================
// 平台信息类
// ==========================
/**
* 获取系统信息
* @returns Promise<{ os: string, version: string }>
*/
getSystemInfo(): Promise<{ os: string; version: string }>;
/**
* 获取网络状态
* @returns Promise<{ connected: boolean; type: string }>
*/
getNetworkStatus(): Promise<{ connected: boolean; type: string }>;
// ==========================
// 系统能力类
// ==========================
/**
* 保存图片到相册
* @param url 图片地址
* @returns Promise<true> 成功,false 或 reject 表示失败或不支持
*/
saveImg(url: string): Promise<boolean>;
/**
* 触发振动
* @param duration 振动时长,单位 ms
* @returns Promise<void>
*/
vibrate(duration: number): Promise<void>;
/**
* 调用相机拍照
* @returns Promise<{ localPath: string }>
*/
takePhoto(): Promise<{ localPath: string }>;
// ==========================
// 通信能力类 (Bridge)
// ==========================
/**
* 调用 Native 方法
* @param method 方法名
* @param params 参数对象
* @returns Promise<any>
*/
callNative(method: string, params?: Record<string, any>): Promise<any>;
/**
* 注册 Native 回调事件
* @param eventName 事件名
* @param callback 回调函数
*/
onNative(eventName: string, callback: (data: any) => void): void;
/**
* 移除 Native 回调事件
* @param eventName 事件名
* @param callback 可选,不传则移除该事件所有回调
*/
offNative(eventName: string, callback?: (data: any) => void): void;
}
注意: 我们一般不把NativeCall封装到interfacePlatform.ts
你现在大概率会想
既然interface声明了JS_call_Native -----JS通信原生的能力
那么是不是应该也有 Native_call-JS---------原生通信JS的能力
从对称性的直觉上看,这非常合理
但是 架构的设计不追求对称,而是职责清晰
Platform它表达的是:
"JS 主动能向平台要什么能力
JS → Native 是「能力调用」
- 1.主动
- 2.有返回值
- 3.有结果
- 4.有生命周期
🏀 所以它天然适合被定义成 函数
Native → JS 本质上是什么?
其实是事件通知
我们来看 Native 主动找 JS 的真实场景:
- 1.登录态失效
- 2.网络状态变化
- 3.支付结果通知
- 4.App 前后台切换
- 5.推送点击
你可以想想:
这些是"JS 主动要的吗"?
答案是:不是
Native → JS 是「事件 / 通知」
它的本质是:
"平台在某个时刻,告诉 JS 一件事发生了"
所以我们一般不把Native封装到InterfacePlatform里面
(很多新手在刚开始学习这个跨平台服务的时候都会犯这个问题,把Native_Call_Js也封装进去了)
1.3小结
InterfacePlatform 用于定义"平台应该具备哪些能力"
它是平台能力的声明与约束,而不涉及任何具体的实现。
2.安全兜底BasePlatform
2.1为什么需要BasePlatform
当我们写完interface定义完一个平台需要哪些能力之后,我们是不是就开始写这个平台的能力了呢?
比如说: AndroidService.js / IosService.js /WebService.js
其实不是
在实际业务开发中,一个非高频的情况就是
你定义的平台功能,不一定是全部的平台都能实现
比方说:你在interface定义一个叫震动的功能
js
/**
* 触发振动
* @param duration 振动时长,单位 ms
* @returns Promise<void>
*/
vibrate(duration: number): Promise<void>; //注意是声明的空实现
这个功能 iosService.js /Android.js (苹果端/安卓端) 可以实现
但是这个功能在web端 webService.js可以实现吗?
你在接口里定义了一个炫酷的功能:vibrate()(震动)。
- Android 说:"没问题,我有振动器 API。"
- iOS 说:"简单,我有 Core Haptics。"
- Web 傻了:"我跑在浏览器里,有的浏览器根本不支持震动,或者用户用的是台式机,牛魔的,你让我怎么震? "
我们知道,我们功能平台的类(ios/Android/web)是继承interface定义的类
所以,你声明了的功能是必须被实现的
那么我们在web Service要怎么实现这个震动的函数的呢?
显然是实现不了的
但是如果我们空着不写,业务层调用了webService.vibrate(),程序就会报错崩溃。
这就引出了 BasePlatform 的必要性。
与其让每个具体的平台去纠结----怎么处理我做不到的事,
不如我们在中间加一层 '安全防护网' ,这就是 BasePlatform。
2.2BasePlatform干了什么?
简单来说
BasePlatform 就是在接口(理想)与实现(现实)之间,加了一个 "默认值系统" 。
意思是,它给全部在interface定义了的函数都写了一个默认的基础实现
我们学前端的同学可以这样思考
它就类似于我们前端的prop的default
当父组件没有传递具体的prop的时候,我们可以用default

这样保证了 前端在使用这个prop的时候,不会因为父组件没有传递,就报undefined的错误
在我们跨平台设计这里也是
在Baseplatform,我们给全部在interface的函数都定义了一个默认的基础实现
以震动功能为例
js
//震动功能
vibrate(_duration: number) {
// 无副作用,直接 resolve
return Promise.resolve() //一个无副作用的兜底实现
}
这样,我们的子类即使无法实现这个功能函数
那么业务层调用了webService.vibrate(),程序也不会因为某个平台没有实现,而报错崩溃。
2.3完整的BasePlatform示例
注意:BasePlatfrom 实现了interface的全部定义
而平台子类(ios/Android/web)继承BasePlatfrom
js
// BasePlatform.ts
import { IPlatformService } from './iplatform'
/**
* BasePlatform = 所有具体平台的安全兜底
* 不支持的功能统一返回 reject,行为失败返回 resolve(false)
*/
export class BasePlatform implements IPlatformService {
// ==========================
// 平台信息类
// ==========================
getSystemInfo() {
return Promise.reject(new Error('getSystemInfo not supported on this platform'))
}
getNetworkStatus() {
return Promise.reject(new Error('getNetworkStatus not supported on this platform'))
}
// ==========================
// 系统能力类
// ==========================
saveImg(_url: string) {
return Promise.reject(new Error('saveImg not supported on this platform'))
}
vibrate(_duration: number) {
// 无副作用,直接 resolve
return Promise.resolve()
}
takePhoto() {
return Promise.reject(new Error('takePhoto not supported on this platform'))
}
// ==========================
// 通信能力类 (Bridge)
// ==========================
callNative(_method: string, _params?: Record<string, any>) {
return Promise.reject(new Error(`callNative(${_method}) not supported on this platform`))
}
onNative(_eventName: string, _callback: (data: any) => void) {
// 默认不做任何事情
}
offNative(_eventName: string, _callback?: (data: any) => void) {
// 默认不做任何事情
}
}
而对于有能力实现这些功能的平台类来说
比如说 AndroidService.js
子类继承之后,只需要覆盖这个方法就行
js
//继承BasePlatform
class WebPlatform extends BasePlatform {
//然后重载这个函数
vibrate(duration: number) {
if (navigator.vibrate) {
return navigator.vibrate(duration) ? Promise.resolve(true) : Promise.resolve(false)
}
return Promise.resolve(false)
}
}
对于比如说 WebService.js不能够支持这个功能的子类,不需要重载函数
比如说 WebService.js
js
class WebService extends BasePlatform {
//无须重载
执行WebService.vibrate() 函数时候,自动运行父类的逻辑
}
这样就保证了 统一规则 + 安全兜底 + 减少重复代码
这样你在实现业务的时候就很 爽

1.调用的时候 : 对于写 UI 逻辑的同学,他只需要写 platform.vibrate()。
他不需要关心现在是 Web 还是 App,也不需要担心这行代码会不会把整个程序搞崩。因为他知道,底层已经有人帮他"兜底"了。
2.实现的时候: 对于负责 Web 端的同学,他可以"偷懒"。
既然 Web 实现不了震动,他甚至不需要在 WebService.js 里写这个函数。BasePlatform 就像一个漏斗,子类没接住的功能,父类全接住了
3.具体平台实现Concrete Implementation
既然有了接口(合同)和 BasePlatform(兜底),现在需要一个真正干活的人。
3.1什么是具体平台实现?
- 它是继承了 BasePlatform 的子类。
- 负责在特定平台上把接口方法"填满",让功能真正可用。
- 核心目标:对外保持接口一致,对内处理平台差异
3.2为什么需要具体平台实现?
-
不同平台 的功能接口不同不同:
- 1.iOS 可以调用 Core Haptics 来震动
- 2.Android 可以调用 Vibrator API
- 3.Web 浏览器可能不支持震动
-
如果没有具体实现:
- 1.业务层只能使用默认兜底方法(BasePlatform),功能"白板"
- 2.用户体验受限,功能无法真正落地
BasePlatform 是"安全网",具体实现才是"真正动手的人"。
3.3具体平台实现的原则
-
1.只覆盖自己支持的方法
- 子类继承 BasePlatform,先默认兜底
- 只需要实现平台支持的接口,不支持的保持 BasePlatform 的默认实现
-
2.对内处理差异,对外保持接口一致
- 内部处理权限、参数转换、回调逻辑
- 对业务层来说,API 依然是统一的
saveImg()、vibrate()、takePhoto()
-
3.调用底层 JSBridge / 原生能力
- 所有系统能力最终依赖 JSBridge
3.4具体平台示例
以iosService平台为例
js
// platform_ios.ts
import { BasePlatform } from './BasePlatform'
import { IPlatformService } from './iplatform'
/**
* iOS 平台具体实现
* 继承 BasePlatform,覆盖自己支持的方法
*/
export class CPlatformAppiOS extends BasePlatform implements IPlatformService {
// ==========================
// 系统能力类
// ==========================
/**
* 保存图片到相册
*/
saveImg(url: string): Promise<boolean> {
// 调用底层 JSBridge 的 callNative
return this.callNative('SaveImg', { url })
.then(() => true)
.catch(() => false) // 如果调用失败,统一返回 false
}
/**
* 触发振动
*/
vibrate(duration: number): Promise<void> {
return this.callNative('Vibrate', { duration })
}
/**
* 调用相机拍照
*/
takePhoto(): Promise<{ localPath: string }> {
return this.callNative('TakePhoto')
}
// ==========================
// 通信能力类 (Bridge)
// ==========================
/**
* 调用 Native 方法
*/
callNative(method: string, params?: Record<string, any>): Promise<any> {
return new Promise((resolve, reject) => {
if (!(window as any).WebViewJavascriptBridge) {
return reject(new Error('WebViewJavascriptBridge not ready'))
}
(window as any).WebViewJavascriptBridge.callHandler(
method,
params,
(response: any) => {
resolve(response)
}
)
})
}
/**
* 注册 Native 回调事件
*/
onNative(eventName: string, callback: (data: any) => void) {
(window as any).WebViewJavascriptBridge.registerHandler(eventName, callback)
}
/**
* 移除 Native 回调事件
*/
offNative(eventName: string, callback?: (data: any) => void) {
// iOS 端 JSBridge 通常没有直接移除接口,可做空实现或维护回调映射
}
}
说明
-
1.继承 BasePlatform
- 只覆盖 iOS 支持的方法
- 不支持的功能(比如某些特殊能力)仍然沿用 BasePlatform 默认实现 → 安全兜底
-
2.统一返回规范
- 成功 → resolve / 返回数据
- 失败 → reject 或 resolve(false)
-
3.内部调用 JSBridge
- iOS 端通过
WebViewJavascriptBridge与 Native 通信 - 所有系统能力最终通过
callNative调用
- iOS 端通过
-
4.对外接口一致
- 业务层调用
saveImg()、vibrate()、takePhoto()时,无需关心底层差异
- 业务层调用
3.5对于分层架构的理解
当我们把代码写到 具体实现 (Concrete Implementation )这一层时,我们其实是在做翻译工作。
我们把统一的 震动功能 vibrate() 指令,翻译成了各个平台听得懂的方言。因为有了前面的 Interface 和 BasePlatform 铺路,
这种翻译工作变得异常轻松且安全。即便哪天你要接入一个 '鸿蒙平台'
你只需要写一个 HarmonyService 并继承 BasePlatform,哪怕你只实现了一个功能,剩下的 99 个功能也会自动拥有'保底'能力。"
4.底层通信JSbridge
在前一步,我们看到 iOS 具体平台实现
js
/**
* 调用相机拍照
*/
takePhoto(): Promise<{ localPath: string }> {
return this.callNative('TakePhoto')
注意这个 //this.callNative('TakePhoto')
}
这里的 callNative 方法只是把请求"交给了JSbridge通信桥梁"
4.1什么是JSbridge?
这里我不打算花篇幅介绍JSbridge的通信机制
我这里只简短介绍一下,初步了解可以去看我的
什么是H5混合开发? 新手指南--- 里面对JSbridge有初步的介绍
(我以后有空会写一篇博客介绍JSbridge的通信机制)
简单来说:
- 1.当前端需要调用手机的硬件能力(比如拍照、震动、保存图片)时,JS 本身无法直接操作原生能力
- 2.所以我们需要通过 JSBridge 发起请求给 App 客户端
- 3.App 客户端接收到请求 → 执行操作 → 将结果返回给 JS
协作链条
JS (发出指令) → Bridge (格式转换) → Native (执行动作) → OS (调用硬件)
JSbridge 就是 原生语言(Android和IOS) 和 前端JS之间的通信桥梁
它主要由两个部分组成 JS_Call_Native.js 和Native_Call_JS.js
4.2JS_Call_Native.js
主要功能:
- JS 向 Native 发起请求
- 负责参数封装、回调注册
- 在 iOS 中会调用
WebViewJavascriptBridge.callHandler() - 在 Android 中会调用
window.AndroidBridge.method(...)
以JS_Call_Android为例
- 1.默认通信机制是 API 注入
- 2.客户端会在 WebView 中注入一个全局对象,例如
window.AndroidBridge - 3.JS 可以通过这个对象发送事件和参数
以Android_call_js为例
js
// CJSCallAndroid.ts
/**
* JS 调用 Native 的桥梁类
* JS 通过 callNative 调用 Android 注入的方法
*/
export class CJSCallAndroid {
private mBridge: any;
constructor() {
// 获取原生注入的 WebViewJavascriptBridge 对象
this.mBridge = (window as any).WebViewJavascriptBridge;
}
/**
* 调用 Native 方法
* @param event Native 注册的方法名
* @param params JS 传给 Native 的参数
* @param callback Native 执行完成后回调给 JS
*/
callNative(event: string, params: any, callback: (ret: any) => void) {
this.mBridge.callHandler(event, params, (retString: string) => {
// Native 返回的结果通常是 JSON 字符串,需要解析
let ret;
try {
ret = JSON.parse(retString);
} catch {
ret = retString;
}
// 调用回调,将结果交给业务逻辑
callback(ret);
});
}
}
默认通信机制是API注入
(不知道什么是API注入的,去看什么是H5混合开发? 新手指南)
主要是在拿到客户端开发的同学给我们注入的全局对象
js
constructor() {
// 获取原生注入的 WebViewJavascriptBridge 对象
this.mBridge = (window as any).WebViewJavascriptBridge;
}
通过这个全局对象,进行通信
传递 事件 和 参数
js
/**
* 调用 Native 方法
* @param event Native 注册的方法名
* @param params JS 传给 Native 的参数
* @param callback Native 执行完成后回调给 JS
*/
callNative(event: string, params: any, callback: (ret: any) => void) {
this.mBridge.callHandler(event, params, (retString: string) => {
// Native 返回的结果通常是 JSON 字符串,需要解析
let ret;
try {
ret = JSON.parse(retString);
} catch {
ret = retString;
}
// 调用回调,将结果交给业务逻辑
callback(ret);
});
}
前端的同学可以理解为前端中的"事件监听"
1.前端发送一个事件 (携带参数)
2.客户端监听这个事件,并执行对应的处理函数
3.客户端将结果result返回给前端
业务中使用示例
js
// JS 调用 Native 拍照
const jsCall = new CJSCallAndroid(); //创建实例
//事件名 事件参数 回调函数
jsCall.callNative('takePhoto', { quality: 80 }, (res) => {
console.log('拍照结果:', res.localPath);
});
4.3Native_Call_JS.js
主要功能:
- Native 向 JS 发送事件或回调
- 比如拍照完成后返回图片路径
- JS 端通过注册好的回调接收结果
以Android_call_js为例
js
// CAndroidCallJS.ts
/**
* Native 调用 JS 的桥梁类
* 通过 register 方法,JS 可以暴露方法给 Native 调用
*/
export class CAndroidCallJS {
private mBridge: any;
constructor() {
// 获取原生注入的 WebViewJavascriptBridge 对象
this.mBridge = (window as any).WebViewJavascriptBridge;
}
/**
* 初始化 Bridge
* Native 调用前必须先 init
*/
init() {
this.mBridge.init((msg: string, resp: (data: any) => void) => {
// Native 发消息时必须调用 resp 回复
resp({});
});
}
/**
* 注册 JS 方法供 Native 调用
* @param funcName 方法名,Native 调用时使用
* @param handler JS 方法实现,接收 Native 传来的参数并返回结果
*/
register(funcName: string, handler: (data: any) => any) {
this.mBridge.registerHandler(
funcName,
(data: string, callback: (ret: string) => void) => {
// data 是 Native 传来的 JSON 字符串
let parsedData;
try {
parsedData = data ? JSON.parse(data) : null;
} catch {
parsedData = data;
}
// 执行 JS 方法,获取返回结果
const ret = handler(parsedData);
// 将结果返回给 Native
callback(JSON.stringify(ret));
}
);
}
}
JS 通过注册全局方法接收 Native 主动发送的事件
js
/**
* 注册 JS 方法供 Native 调用
* @param funcName 方法名,Native 调用时使用
* @param handler JS 方法实现,接收 Native 传来的参数并返回结果
*/
register(funcName: string, handler: (data: any) => any) {
this.mBridge.registerHandler(
funcName,
(data: string, callback: (ret: string) => void) => {
// data 是 Native 传来的 JSON 字符串
let parsedData;
try {
parsedData = data ? JSON.parse(data) : null;
} catch {
parsedData = data;
}
// 执行 JS 方法,获取返回结果
const ret = handler(parsedData);
// 将结果返回给 Native
callback(JSON.stringify(ret));
}
);
}
Native 通过 evaluateJavascript 或类似方法调用 JS
(前端同学不需要关注)
业务中使用示例
js
// Native 调用 JS 方法
const androidCallJS = new CAndroidCallJS(); //创建
androidCallJS.init();//注意这里需要初始化
androidCallJS.register('showAlert', (data) => {
alert(`Native 调用 JS: ${JSON.stringify(data)}`);
return { ok: true };
});
4.4不同平台实现的演示
iosService.js实现
js
//导入接口
import { CJSCalliOS } from './js_call_ios';
//导入父类
import { BasePlatform } from './base_platform';
export class CPlatformAppiOS extends BasePlatform {
private bridge = new CJSCalliOS();
// 调用拍照
takePhoto(options?: any) {
return this.bridge.callNative('TakePhoto', options);
}
// 调用震动
vibrate() {
return this.bridge.callNative('Vibrate');
}
}
AndroidService.js实现
js
import { CJSCallAndroid } from './js_call_android';
import { BasePlatform } from './base_platform';
export class CPlatformAppAndroid extends BasePlatform {
private bridge = new CJSCallAndroid();
takePhoto(options?: any) {
return this.bridge.callNative('takePhoto', options, (res) => res);
}
vibrate() {
return this.bridge.callNative('vibrate', {}, (res) => res);
}
}
WebService.js
js
import { BasePlatform } from './base_platform';
export class CPlatformAppWeb extends BasePlatform {
//你可以重载 也可以不重载
takePhoto() {
alert('Web 平台不支持拍照');
return Promise.resolve(false);
}
vibrate() {
if (navigator.vibrate) {
navigator.vibrate(200);
return Promise.resolve(true);
}
return Promise.resolve(false);
}
}
5.统一入口 (The Factory)
5.1统一入口的背景
当我们写好了:
- 1.接口(IPlatformService) → 定义"我需要什么能力"
- 2.BasePlatform → 安全兜底,保证调用不会报错
- 3.Concrete Platform → 各个平台真实实现功能
业务层已经可以调用平台能力了
例如:拍照
js
const platform = new CPlatformAppiOS();
platform.takePhoto();
但问题来了:
-
业务层真的要知道当前运行的是哪个平台吗?
- iOS →
CPlatformAppiOS - Android →
CPlatformAppAndroid - Web →
CPlatformWeb
- iOS →
-
如果业务层直接 new,不仅增加耦合,还容易出错:
- 写成
new CPlatformAppiOS()→ Android 或 Web 上会报错 - 业务层每次都要写判断逻辑 → 冗余、重复
- 写成
总结问题 :
业务层的核心需求是 "我只想调用功能,不关心底层是哪个平台"
既然业务层只想"调用功能",那谁来决定使用哪个具体平台呢?
我们需要一个 统一入口,在应用启动时根据运行环境自动选择平台实现,并对外暴露统一接口。
5.2统一入口实例化示例
js
import { CPlatformAppiOS } from './platform_app_ios';
import { CPlatformAppAndroid } from './platform_app_android';
import { CPlatformAppWeb } from './platform_app_web';
import { getPlatformRuntime } from './platform_detect';
export class PlatformService {
private static _instance: any;
static instance() {
if (!this._instance) {
const runtime = getPlatformRuntime(); // ← 运行时判断平台
switch (runtime) {
case 'iOS_APP':
this._instance = new CPlatformAppiOS();
break;
case 'ANDROID_APP':
this._instance = new CPlatformAppAndroid();
break;
default:
this._instance = new CPlatformAppWeb();
}
}
return this._instance;
}
}
1.单例模式 → _instance 保证全局只有一个平台实例
2.运行时判断 → getPlatformRuntime() 返回当前环境
3.动态分发 → 根据平台实例化对应的具体平台实现
PlatformService 就是这个分发中心 / 工厂:
- 1.运行时判断当前平台
- 2.实例化对应的 Concrete Platform
- 3.返回统一接口给业务层
这样业务层就可以直接调用:
js
//拍照
PlatformService.instance().takePhoto();
//震动
PlatformService.instance().vibrate();
不需要关心到底是 iOS、Android 还是 Web
6.生命周期管理
在前面我们已经实现了:
- 1.接口(IPlatformService) → 定义了平台能力
- 2.BasePlatform → 安全兜底,避免业务层调用报错
- 3.Concrete Platform → 各平台真实实现能力
- 4.JSBridge → 底层通信桥梁
- 5.统一入口(PlatformService / Factory) → 根据环境实例化对应平台
此时,如果业务层一直使用 PlatformService.instance() ,平台实例会长期驻留在内存中,可能会有以下问题:
- 1.资源未释放 → 比如注册了监听事件、定时器等
- 2.事件重复注册 → 再次初始化时可能重复触发
- 3.内存泄漏 → 长期运行的 SPA / Hybrid App,尤其是移动端
所以,我们需要为 平台实例增加生命周期管理。
6.1生命周期四阶段
以 Concrete Platform 为例,通常可以设计四个阶段:
| 阶段 | 方法 | 作用 |
|---|---|---|
| 构造 | create() |
保存依赖,不创建资源 |
| 初始化 | setup() |
创建资源,只执行一次(比如 JSBridge、监听器) |
| 激活 | active() |
提供 API,响应业务调用 |
| 销毁 | destroy() |
释放资源、解除监听,确保不可逆 |
6.2Interface 定义标准
在 IPlatformService 或类似的接口里,只定义生命周期方法的"合同" :
js
interface ILifecycle {
create(): void;
setup(): void;
active(): void;
destroy(): void;
}
6.3 BasePlatform 提供基础实现
BasePlatform / NullPlatform 会给出 兜底实现,比如:
js
class BasePlatform implements ILifecycle {
create() {}
setup() {}
active() {}
destroy() {}
}
目的:
- 1.避免具体平台忘写某个生命周期方法 → 安全兜底
- 2.提供默认行为 → 业务层可以放心调用
- 3.如果某个功能在某个平台不支持,也不会报错
6.4 Concrete Platform 具体实现
在具体平台类(CPlatformAppiOS、CPlatformAppAndroid)里 覆盖 BasePlatform 的方法:
以ios为例
js
//依旧继承Base
class CPlatformAppiOS extends BasePlatform {
setup() {
// 真正初始化 JSBridge
this.bridge = window.WebViewJavascriptBridge;
}
active() {
// 对外提供 API
console.log('iOS 平台激活完成');
}
destroy() {
// 清理资源、解除监听
this.bridge = null;
}
}
只有具体平台知道如何真正"激活"和"释放"资源
BasePlatform 只是提供空壳 / 安全兜底
6.5使用示例
js
<template>
<div>
<h2>平台信息</h2>
<!-- 显示操作系统和版本 -->
<p>系统: {{ systemInfo.os }} {{ systemInfo.version }}</p>
<h2>操作示例</h2>
<!-- 点击按钮触发拍照 -->
<button @click="takePhoto">拍照</button>
<!-- 如果拍照完成,显示照片本地路径 -->
<p v-if="photoPath">照片路径: {{ photoPath }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'
//导入
import platform from './platform'
export default defineComponent({
name: 'PlatformDemo',
setup() {
// 保存系统信息
const systemInfo = ref<{ os: string; version: string }>({ os: '', version: '' })
// 保存拍照得到的本地图片路径
const photoPath = ref<string>('')
// 生命周期钩子
// 组件挂载完成后,获取系统信息
onMounted(async () => {
try {
// 调用平台统一接口获取系统信息
systemInfo.value = await platform.getSystemInfo()
} catch (err) {
console.error('获取系统信息失败:', err)
}
})
// 拍照
const takePhoto = async () => {
try {
// 调用平台接口唤起相机拍照
const photo = await platform.takePhoto()
// 拍照完成后保存图片路径到响应式变量
photoPath.value = photo.localPath
} catch (err) {
console.error('拍照失败:', err)
}
}
return {
systemInfo, // 系统信息
photoPath, // 拍照路径
takePhoto, // 拍照方法
}
},
})
</script>
总结
理解这套体系,你可以写出一套 可扩展、可维护、跨平台统一调用的前端平台服务。
本文内容基于实际企业级项目中的 IPlatform 架构设计编写
文中示例和思路均来源于真实项目的改写,本文的全部示例不涉及源码
可供读者在企业级项目中参考和落地。