使用 CML 的渲染能力,需要集成對(duì)應(yīng)平臺(tái)的 SDK。請(qǐng)?jiān)谧髠?cè)目錄中根據(jù)你需要接入的平臺(tái)來(lái)查看文檔。
CML iOS SDK 使用 Weex、React Native 與 WebView 作為基礎(chǔ)渲染引擎,提供了基礎(chǔ)的組件功能之外,還支持用戶(hù)擴(kuò)展自己的功能組件。
CML 最低支持的 iOS deployment target 為:iOS 9.0 CML 使用Cocoapods進(jìn)行管理,使用npm管理react_native。
組件名 | 依賴(lài)版本 | 備注 |
---|---|---|
Cocoapods | 1.3.1 | - |
npm | 最新版本即可 | - |
Weex SDK | 0.19.0.2 | - |
React Native | 0.57.6 | - |
React | 16.6.1 | - |
當(dāng) SDK 下載下來(lái)后,首先進(jìn)入/chameleon-sdk-iOS/Chameleon/react_native,并運(yùn)行npm install進(jìn)行更新。(這也是 React Native 的更新辦法。)
接下來(lái)我們以 Demo 工程為例(要注意工程路徑位置,在工程實(shí)際配置中需要注意 :path 的內(nèi)容)。
在 Podfile 中,寫(xiě)入:
platform :ios, '9.0'
target 'Chameleon_Example' do
##CML pod 'Chameleon', :path => '../Chameleon/'
## 如果需要Weex,則寫(xiě)入weex依賴(lài)。
pod 'WeexSDK', '~> 0.19.0.2'
## 如果需要react_native,則寫(xiě)入react_native依賴(lài)。
pod 'React', :path => '../Chameleon/react_native/node_modules/react-native', :subspecs => [
'Core',
'CxxBridge', ## 如果RN版本 >= 0.45則加入此行
'DevSupport', ## 如果RN版本 >= 0.43,則需要加入此行才能開(kāi)啟開(kāi)發(fā)者菜單
'RCTText',
'RCTNetwork',
'RCTWebSocket', ## 這個(gè)模塊是用于調(diào)試功能的
]
pod 'yoga', :path => '../Chameleon/react_native/node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../Chameleon/react_native/node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../Chameleon/react_native/node_modules/react-native/third-party-podspecs/GLog.podspec'
pod 'Folly', :podspec => '../Chameleon/react_native/node_modules/react-native/third-party-podspecs/Folly.podspec'
end
將 podfile 保存,并運(yùn)行 pod install。
解決辦法:刪除 工程->Build Phrases->[CP] Copy Pods Resources->Output files下的 copy 路徑。
描述 | 作用 |
---|---|
CML | SDK 源碼與依賴(lài)文件夾 |
Example | react_native 依賴(lài) |
####CMLsdk_src
目錄 | 功能描述 |
---|---|
CMLSDKEngine 類(lèi) | 初始化 SDK、注冊(cè)自定義的 Module 等功能 |
CMLCommon | CML 抽象層。抽象了基礎(chǔ)的渲染頁(yè)面、緩存、配置、預(yù)加載等功能。 |
CMLReactNative | 針對(duì) ReactNative 額外配置的部分 |
CMLWeex | 針對(duì) Weex 額外配置的部分 |
CMLWeb | 針對(duì) WebView 額外配置的部分 |
當(dāng) Bundle 下載失敗、渲染出現(xiàn)嚴(yán)重錯(cuò)誤時(shí),會(huì)自動(dòng)降級(jí)至 H5。 降級(jí)設(shè)計(jì)圖
當(dāng) H5 渲染失敗時(shí),倘若設(shè)置了默認(rèn)的本地 bundle,會(huì)使用本地 bundle 進(jìn)行降級(jí)。
當(dāng)渲染出現(xiàn)錯(cuò)誤時(shí),F(xiàn)E 可以通過(guò) JSBridge 通知客戶(hù)端觸發(fā)降級(jí)。
使用+ (void)registerModule:(NSString *)moduleName className:(NSString *)className;注冊(cè)自己的擴(kuò)展。
module 是 Native 提供給前端頁(yè)面調(diào)用的,完成一組操作的方法集合,用于擴(kuò)展 Native 的能力。在 CML 頁(yè)面中,開(kāi)發(fā)者引入相關(guān) js 庫(kù)后即可調(diào)用 Module 中的方法。
import bridge from 'chameleon-bridge';
// 主動(dòng)調(diào)用客戶(hù)端方法
export function sayHello() {
bridge.callNative(
'moduleDemo', // 模塊名
'sayHello', // 方法名
{}, // 參數(shù)
res => {} // 回調(diào)方法
);
}
Module 的使用分兩種情況,一種是使用 CML SDK 內(nèi)置的 Module,一種是用戶(hù)自定義實(shí)現(xiàn)自己的 Module。
內(nèi)置的 Module 在 js 前端代碼里直接使用即可,目前內(nèi)置的 Module 有:
API 文檔里描述的能力,部分實(shí)現(xiàn)就是由上述 Module 支撐的。
示例可參看CMLStorageModule 示例
注冊(cè)自己的 Module 關(guān)聯(lián)文件:
詳細(xì)說(shuō)明
預(yù)加載和緩存都是為了節(jié)省 JSBundle 下載的時(shí)間,加快 UI 的渲染。
預(yù)加載是將下載 JSBundle 的動(dòng)作提前完成,在需要用到的時(shí)候直接從本地讀取并渲染。實(shí)際項(xiàng)目使用中,可以將需要預(yù)加載的 url 配置到預(yù)加載地址列表里,在 app 啟動(dòng)時(shí)提前從服務(wù)端獲取,通過(guò) CML SDK 提供的預(yù)加載能力提前下載下來(lái)。
對(duì)于沒(méi)有預(yù)加載的 JSBundle 在渲染前需要先下載,下載完成后 CML SDK 會(huì)緩存此 JSBundle,下次渲染同一個(gè) JSBundle 時(shí),如果此 JSBundle 沒(méi)有更新則不會(huì)下載新的,達(dá)到節(jié)省時(shí)間和流量提升渲染速度的目的。
CMLCache 是一個(gè)對(duì) js 進(jìn)行下載、緩存的一個(gè)模塊,根據(jù)協(xié)議來(lái)實(shí)現(xiàn) js 增量更新功能。主要有以下內(nèi)容
|
|——CMLWeexCache 緩存模塊接口類(lèi)
|——CMLCacheInfo JsBundle文件緩存實(shí)現(xiàn)邏輯
|——CMLCacheItem jsBundle文件內(nèi)存對(duì)象,描述每一個(gè)jsBundle文件的緩存狀態(tài)
|——CMLConfig 配置類(lèi),業(yè)務(wù)方可通過(guò)這個(gè)類(lèi)設(shè)置是否開(kāi)啟緩存功能、預(yù)加載js路徑、緩存大小限制、默認(rèn)兜底頁(yè)鏈接等
對(duì) jsBundle 進(jìn)行預(yù)加載、獲取、緩存的處理對(duì)外接口。
在該文件里,我們將拿到的 URL 解析出此頁(yè)面需要加載的 jsbundle 標(biāo)識(shí),然后根據(jù) jsBundle 標(biāo)識(shí)來(lái)檢測(cè)是否在本地已預(yù)加載,如果此 jsBundle 已預(yù)加載成功,則直接讀本地緩存渲染;否則先從網(wǎng)絡(luò)下載 jsBundle,然后渲染并緩存本地。
后續(xù)我們將支持在一個(gè) URL 中下發(fā)多段 jsbundle 標(biāo)識(shí),每段 jsbundle 標(biāo)識(shí)代表這個(gè)頁(yè)面的一部分,然后在根據(jù)每段 jsbundle 標(biāo)識(shí),分別從本地緩存里獲取去尋找對(duì)應(yīng)的 js 代碼,如果不存在則從網(wǎng)絡(luò)去下載這一段 jsBundle 并保存在本地,然后 SDK 中會(huì)將最終得到的多段 jsBundle,組合成一個(gè)完整頁(yè)面的 jsBundle 加載出來(lái)。
jsBundle 的內(nèi)存管理器,是加載、獲取、緩存等處理的實(shí)際操作者。 對(duì)本地緩存 jsBundle 的 maxSize 加以限制,如果超過(guò) maxSize,則優(yōu)先清除老的 jsBundle 緩存
對(duì) jsBundle 的封裝,包括 jsBundle 文件在本地存儲(chǔ)的路徑、內(nèi)存索引;CMLCacheInfo 就是通過(guò) CMLCacheItem 來(lái)對(duì) jsBundle 進(jìn)行操作。
緩存相關(guān)配置
緩存相關(guān)配置定義位于 CMLWeexConfig (CMLConfig)
//設(shè)置服務(wù)類(lèi)型為weex
[CMLEnvironmentManage chameleon].serviceType = CMLServiceTypeWeex;
//設(shè)置默認(rèn)錯(cuò)誤web鏈接
[CMLEnvironmentManage chameleon].weexService.config.defaultErrUrl = @“defaultErrUrl”
Chameleon 功能和緩存功能都是默認(rèn)開(kāi)啟的,如果有特殊需要,可以手動(dòng)關(guān)閉;另外在這里還有緩存限制 maxSize、緩存目錄等配置
[CMLEnvironmentManage chameleon].weexService.config.isFeatureAvailable = NO;
[CMLEnvironmentManage chameleon].weexService.config.isEnableCacheFeature = NO;
預(yù)加載
//設(shè)置預(yù)加載 URL 列表
[CMLEnvironmentManage chameleon].weexService.config.prefetchContents = @[@"http%3A%2F%2F172.22.139.32%3A8000%2Fweex%2Fchameleon-bridge.js%3Ft%3D1546502643623"];
//開(kāi)啟預(yù)加載
[[CMLEnvironmentManage chameleon].weexService setupPrefetch];
獲取加載 jsBundle 的 URL
CMLWeexCache *cache = (CMLWeexCache *)[CMLEnvironmentManage chameleon].weexService.cache;
//在緩存中獲取JSBundle的URL(本地有緩存則獲取到本地緩存的URL,本地?zé)o緩存則獲取到遠(yuǎn)端的URL)
[cache getBundleCacheOfJSBundleUrl:self.bundleUrl completion:^(NSString *url, NSDictionary *parameter) {
//加載jsBundle
[self.render renderWithURL:[NSURL URLWithString:url] options:@{@"query" : [param copy]} data:nil];
}
CML Android SDK 是 CML 整體框架的一部分,主要任務(wù)是完成 CML JsBundle 在 Android 端的本地渲染。SDK 底層采用 Weex 作為渲染引擎,同時(shí)擴(kuò)展一些一般工程通用的基礎(chǔ)能力,如緩存能力、降級(jí)能力等。
項(xiàng)目一級(jí)目錄結(jié)構(gòu)如下:
|+ app SDK使用示例
|+ cmlsdk SDK接入層,抽象 CML 引擎能力、實(shí)現(xiàn)通用擴(kuò)展能力
|+ cmlweex 包裝 Weex 渲染引擎
|+ cmlweb 包裝 Web 渲染引擎
|+ js-bundle-mgr 實(shí)現(xiàn) js bundle 預(yù)加載、緩存
|+ rich-text-component 富文本組件
|+ sdk-image 圖片選擇、圖片拍攝組件
|+ sdk-location 位置組件
cmlsdk 模塊單獨(dú)拿出來(lái)看下目錄結(jié)構(gòu):
|- cmlsdk
|+ adapter 定義了擴(kuò)展能力的接口以及默認(rèn)實(shí)現(xiàn),無(wú)默認(rèn)實(shí)現(xiàn)的能力需要第三方項(xiàng)目根據(jù)自己的實(shí)際業(yè)務(wù)需求去實(shí)現(xiàn)
|+ bridge 定義了 js 和 Native 通信的接口,實(shí)現(xiàn)協(xié)議相關(guān)的處理能力,以及實(shí)現(xiàn)了協(xié)議層使用入口
|+ bundle js bundle 相關(guān)定義,目前只有一個(gè)類(lèi)用來(lái)描述 js bundle 相關(guān)信息
|+ common 通用能力的基礎(chǔ)封裝類(lèi)
|+ container 渲染容器的抽象能力定義
|+ extend CML 提供的一些能力
|+ Module 擴(kuò)展能力管理,收集 sdk 默認(rèn)提供的以及第三方用戶(hù)自己實(shí)現(xiàn)的 Module,根據(jù) bridge 層指令執(zhí)行具體某個(gè) Module 的某個(gè) method
|+ utils 工具類(lèi)集合
|+ widget 自定義的widget,目前只有一個(gè) title bar,用做 webview 渲染容器的action bar
|- CmlBaseLifecycle 生命周期的接口定義
|- CmlConstant 常量定義
|- CmlEngine CML SDK 使用入口
|- CmlEnvironment 運(yùn)行環(huán)境和運(yùn)行參數(shù)配置入口、擴(kuò)展能力設(shè)置入口
|- CmlInstanceManage 頁(yè)面運(yùn)行實(shí)例的管理類(lèi),每一個(gè)容器實(shí)例運(yùn)行時(shí),其對(duì)應(yīng)的Instance會(huì)注冊(cè)到這里
|- ICmlEngine 引擎的抽象接口
|- ICmlInstance 容器實(shí)例抽象接口
|- ICmlActivityInstance 全屏容器實(shí)例抽象接口
|- ICmlViewInstance 視圖容器實(shí)例抽象接口
CML Android SDK 的使用步驟如下:
此類(lèi)是 Chameleon/k??mi?l??n/ SDK 的入口類(lèi),提供基本的初始化入口和 Chameleon容器的調(diào)起能力。具體包含以下能力
CmlEnvironment 主要提供了開(kāi)發(fā)期間需要的一些能力,如
以及一些常量的定義,如
富文本是 CML 里唯一一個(gè)默認(rèn)注冊(cè)的組件,主要有以下內(nèi)容
|
|-richinfo 主要是富文本需要定義的協(xié)議、點(diǎn)擊事件的回調(diào)等
|-utils 工具類(lèi),主要是加載assets下默認(rèn)的字體
|-CmlRichTextComponent 繼承與與CmlComponent的富文本組件
|-CmlRichTextEngine 富文本入口類(lèi),
github 地址點(diǎn)這里
根目錄 assets 目錄下的 cml-demo-say.zip 是個(gè)簡(jiǎn)單的示例工程,用來(lái)演示 Native 和 Weex 容器或 Web 容器的雙向通信
module 是 Native 提供給前端頁(yè)面調(diào)用的,完成一組操作的方法集合,用于擴(kuò)展 Native 的能力。在 CML 頁(yè)面中,開(kāi)發(fā)者引入相關(guān) js 庫(kù)后即可調(diào)用 Module 中的方法。
import bridge from 'chameleon-bridge';
// 主動(dòng)調(diào)用客戶(hù)端方法
export function sayHello() {
bridge.callNative(
'moduleDemo', // 模塊名
'sayHello', // 方法名
{}, // 參數(shù)
res => {} // 回調(diào)方法
);
}
Module 的使用分兩種情況,一種是使用 CML SDK 內(nèi)置的 Module,一種是用戶(hù)自定義實(shí)現(xiàn)自己的 Module。
內(nèi)置的 Module 在 js 前端代碼里直接使用即可,目前內(nèi)置的 Module 有:
API 里描述的能力,部分實(shí)現(xiàn)就是由上述 Module 支撐的。
module 擴(kuò)展 3 個(gè)重要的注解
詳細(xì)說(shuō)明
先看個(gè)例子,對(duì) Adapter 有個(gè)直觀印象和基本概念。Chameleon SDK 里打印日志使用的是默認(rèn)的 android.util.Log, 如果想替換它可以按照如下步驟實(shí)行:
如果用戶(hù)想替換 SDK 默認(rèn)提供的日志打印,可以實(shí)現(xiàn) CmlLoggerAdapter 接口,并按如下方式注冊(cè)進(jìn) SDK:
// 接口實(shí)現(xiàn)
public class MyLoggerDefault implements CmlLoggerAdapter {
@Override
public void d(String tag, String msg) {
// 這里實(shí)現(xiàn)自己的日志打印
}
...
}
// 接口注冊(cè)
public class MyApplication extends Application implements ICmlConfig {
@Override
public void onCreate() {
super.onCreate();
CmlEngine.getInstance().init(this, this);
}
@Override
public void configAdapter() {
CmlEnvironment.setLoggerAdapter(new MyLoggerDefault()); // 注冊(cè)自己的Adapter
...
}
...
}
以上就完成了日志打印能力的替換。
SDK Adapter 定義和默認(rèn)實(shí)現(xiàn)如下:
// 日志接口定義
public interface CmlLoggerAdapter {
void d(String tag, String msg);
...
}
// 日志接口默認(rèn)實(shí)現(xiàn)
public class CmlLoggerDefault implements CmlLoggerAdapter {
@Override
public void d(String tag, String msg) {
Log.d(tag, msg);
}
...
}
如果用戶(hù)注冊(cè)了自己的 Log Adapter 實(shí)現(xiàn)則優(yōu)先使用,否則使用 SDK 默認(rèn)提供的實(shí)現(xiàn)。
日志打印通過(guò) CmlLogUtil 類(lèi)調(diào)用,注冊(cè)自己的 Logger Adapter 后,打印日志的相關(guān)方法就會(huì)回調(diào)到自定義的方法實(shí)現(xiàn)里,使用示例:
// 日志打印
public void launchPage(@NonNull Activity activity, String url, HashMap<String, Object> options) {
if (TextUtils.isEmpty(url)) {
CmlLogUtil.e(TAG, "CmlEngine launchPage, url is empty.");
return;
}
...
}
Adapter 的目的是定義一系列能力接口來(lái)隔離具體的實(shí)現(xiàn),方便 SDK 使用者在需要時(shí)靈活替換成自己的實(shí)現(xiàn)。Chameleon SDK 框架層在使用 Adapter 相關(guān)能力時(shí)都是面向接口的,使用者只需要實(shí)現(xiàn)相關(guān)能力的 Adapter 接口并通過(guò) SDK 注冊(cè)接口進(jìn)行注冊(cè),即可輕松替換成自己的實(shí)現(xiàn)并進(jìn)行能力擴(kuò)展。
Chameleon SDK 并沒(méi)有完整的實(shí)現(xiàn)所有 Adapter 接口,也就是說(shuō)一部分有默認(rèn)實(shí)現(xiàn)的 Adapter 可以直接使用,未提供默認(rèn)實(shí)現(xiàn)的需要使用者自己實(shí)現(xiàn),否則框架將無(wú)法使用對(duì)應(yīng)的接口能力。
Chameleon SDK 定義了如下的 Adapter 接口
接口 | 功能 | 默認(rèn)實(shí)現(xiàn) |
---|---|---|
ICmlDegradeAdapter | 降級(jí) | 無(wú) |
ICmlImgLoaderAdapter | 圖片加載 | 有 |
CmlLoggerAdapter | 日志 | 有 |
ICmlNavigatorAdapter | url 跳轉(zhuǎn) | 有 |
ICmlStatisticsAdapter | 統(tǒng)計(jì)信息輸出 | 無(wú) |
ICmlWebSocketAdapter | WebSocket | 有 |
CmlHttpAdapter | Http 請(qǐng)求 | 有 |
CmlJsonAdapter | json 解析 | 有 |
CmlDialogAdapter | 對(duì)話(huà)框 | 有 |
CmlToastAdapter | 提示浮層 | 有 |
CmlStorageAdapter | key->value 存儲(chǔ) | 有 |
CmlThreadAdapter | 線程 | 有 |
降級(jí)、對(duì)話(huà)框、提示浮層 Adapter 在 SDK 實(shí)際使用時(shí)替換可能性較大,分別說(shuō)明。
降級(jí)
ICmlDegradeAdapter 降級(jí)接口沒(méi)有提供默認(rèn)實(shí)現(xiàn) CmlDegradeDefault 默認(rèn)會(huì)關(guān)閉 Native 渲染容器,并打開(kāi) Web 容器加載降級(jí) url。
public class CmlDegradeDefault implements ICmlDegradeAdapter {
@Override
public DegradeViewWrapper getDegradeView(int degradeCode) {
return new DegradeViewWrapper() {
CmlWebView webView;
@Override
public View getView(@NonNull Context context) {
webView = new CmlWebView(context);
webView.onCreate();
return webView;
}
@Override
public void onDestroy() {
if (null != webView) {
webView.onDestroy();
}
}
@Override
public void loadURL(@NonNull Context context, @NonNull String url, @Nullable HashMap<String, Object> options) {
if (null != webView) {
webView.render(url, null);
}
}
};
}
@Override
public void degradeActivity(@NonNull Activity activity, @NonNull String url, @Nullable HashMap<String, Object> options, int degradeCode) {
if (url.contains("?")) {
url = url.substring(0, url.indexOf("?"));
}
CmlEngine.getInstance().launchPage(activity, url, null);
}
}
degradeActivity 會(huì)在如下降級(jí)場(chǎng)景發(fā)生時(shí)回調(diào)
對(duì)話(huà)框
此接口定義以下兩種對(duì)話(huà)框能力
CmlModalTip 實(shí)現(xiàn)了此接口,通過(guò) CmlModalModule 類(lèi)暴露給 JS 側(cè)調(diào)用,前端用法參考 API交互反饋
提示浮層
此接口定義以下浮層提示能力
CmlModalTip 實(shí)現(xiàn)了此接口,通過(guò) CmlModalModule 類(lèi)暴露給 JS 側(cè)調(diào)用,前端用法參考 API交互反饋
圖片加載
CmlDefaultImgLoaderAdapter ,默認(rèn)使用 Glide,需要用戶(hù)手動(dòng)集成 Glide
日志打印
CmlLoggerDefault,默認(rèn)使用系統(tǒng) log 輸出
跳轉(zhuǎn)
默認(rèn)使用 Intent.ACTION_VIEW 處理
統(tǒng)計(jì)信息輸出
沒(méi)有默認(rèn)實(shí)現(xiàn),不關(guān)心可以不用實(shí)現(xiàn)
WebSocket
CmlDefaultWebSocketAdapter,默認(rèn)使用 OkHttp3,需要用戶(hù)手動(dòng)集成 OkHttp3
Http 請(qǐng)求
執(zhí)行 http 請(qǐng)求,并監(jiān)聽(tīng) http 響應(yīng)
json 解析
轉(zhuǎn)換成 json 字符串和反解成 json 對(duì)象
key->value 存儲(chǔ)
線程
定義工作線程和 ui 線程
預(yù)加載和緩存都是為了節(jié)省 JSBundle 下載的時(shí)間,加快 UI 的渲染。
預(yù)加載是將下載 JSBundle 的動(dòng)作提前完成,在需要用到的時(shí)候直接從本地讀取并渲染。實(shí)際項(xiàng)目使用中,可以將需要預(yù)加載的 url 地址列表在 app 啟動(dòng)時(shí)提前從服務(wù)端獲取,通過(guò) CML SDK 提供的預(yù)加載能力提前下載下來(lái)。
對(duì)于沒(méi)有預(yù)加載的 JSBundle 在渲染前需要先下載,下載完成后 CML SDK 會(huì)緩存此 JSBundle,下次渲染同一個(gè) JSBundle 時(shí),如果此 JSBundle 沒(méi)有更新則不會(huì)下載新的,達(dá)到節(jié)省時(shí)間和流量提升渲染速度的目的。
JsBundleMgr 是一個(gè)對(duì) js 進(jìn)行下載、緩存的一個(gè)模塊,根據(jù)協(xié)議來(lái)實(shí)現(xiàn) js 增量更新功能。主要有以下內(nèi)容
|
|——cache 基于DiskLrucache來(lái)實(shí)現(xiàn)緩存功能
|——code js代碼的獲取及管理
|——net 采用httpUrlConnect實(shí)現(xiàn)下載功能
|——utils 工具包
|——CmlJsBundleConstant 常量的管理
|——CmlJsBundleEngine 實(shí)現(xiàn)了CmlJsBundleManager接口,入口類(lèi)
|——CmlJsBundleEnvironment 當(dāng)前環(huán)境的設(shè)置,如debug環(huán)境等
|——CmlJsBundleManager 實(shí)現(xiàn)此接口可自己定義JsBundle的管理
|——CmlJsBundleMgrConfig 配置類(lèi),設(shè)置預(yù)加載js路徑、緩存大小等,默認(rèn)預(yù)加載及運(yùn)行時(shí)緩存大小是4M,可自行設(shè)置
對(duì) js 代碼進(jìn)行預(yù)加載、獲取、緩存的管理。在該包里,我們將拿到的 url 根據(jù)協(xié)議來(lái)拆分成多個(gè) url1、url2 等,然后在根據(jù) url1、url2 等來(lái)獲取對(duì)應(yīng)的 js 代碼,首先從本地緩存里獲取去尋找對(duì)應(yīng)的 js 代碼,如果不存在則從網(wǎng)絡(luò)去下載并保存在本地
一些文件管理、拆分 url、網(wǎng)絡(luò)判斷的工具類(lèi)
緩存文件名、預(yù)加載優(yōu)先級(jí)的管理,預(yù)加載優(yōu)先級(jí)有以下三種類(lèi)型
實(shí)現(xiàn)了 CmlJsBundleManager 接口,主要有以下三個(gè)方法
實(shí)現(xiàn)此接口可以自己定義 JsBundleMgr 的實(shí)現(xiàn)
添加依賴(lài)
compile 'com.didiglobal.chameleon:js-bundle-mgr:latest.version'
預(yù)加載
CmlJsBundleEnvironment.DEBUG = true;
List<CmlModel> cmlModels = new ArrayList<>();
CmlModel model = new CmlModel();
model.bundle = CmlUtils.parseWeexUrl(url1);
model.priority = 2;
cmlModels.add(model);
model = new CmlModel();
model.priority = 2;
model.bundle = CmlUtils.parseWeexUrl(url2);
cmlModels.add(model);
CmlJsBundleMgrConfig config = new CmlJsBundleMgrConfig.Builder().setPreloadList(cmlModels).build();
CmlJsBundleEngine.getInstance().initConfig(this, config);
CmlJsBundleEngine.getInstance().startPreload();
獲取 Js 代碼
CmlJsBundleEngine.getInstance().initConfig(this, new CmlJsBundleMgrConfig.Builder().build());
String url = CmlUtils.parseWeexUrl(url);
CmlJsBundleEngine.getInstance().getWXTemplate(url, new CmlGetCodeStringCallback() {
@Override
public void onSuccess(String codes) {
Log.i(TAG, "onSuccess: " + codes);
}
@Override
public void onFailed(String errMsg) {
Log.i(TAG, "onFailed: " + errMsg);
}
});
獲得 SDK 信息
無(wú)
返回 promise
返回值 | 類(lèi)型 | 說(shuō)明 |
---|---|---|
version | String | 版本號(hào) |
同步方法,判斷 webview 或 Native 頁(yè)面是否在 sdk 環(huán)境中,目前只用于內(nèi)部封裝方法使用。
無(wú)
返回值 | 類(lèi)型 | 說(shuō)明 |
---|---|---|
value | Boolean | true:在 sdk 環(huán)境中;false:不在 sdk 環(huán)境中 |
import bridge from 'chameleon-bridge';
const inSDK = bridge.inSDK(); // true/false
降級(jí)到 cmlUrl 對(duì)應(yīng)的 h5 地址。
js 調(diào)用 Native sdk
import bridge from 'chameleon-bridge';
// 主動(dòng)調(diào)用客戶(hù)端方法
export function sayHello() {
bridge.callNative(
'moduleDemo', // 模塊名
'sayHello', // 方法名
{}, // 參數(shù)
res => {} // 回調(diào)方法
);
}
監(jiān)聽(tīng)客戶(hù)端調(diào)用 js
import bridge from 'chameleon-bridge';
// 監(jiān)聽(tīng)客戶(hù)端調(diào)用js
export function listenTell() {
bridge.listenNative(
'moduleDemo', // 模塊名
'NaTellJS', // 方法名
res => {
// 回調(diào)方法中處理返回的數(shù)據(jù)
}
);
}
瀏覽器在加載靜態(tài)資源的時(shí)候一般的話(huà)會(huì)使用兩種 HTTP 緩存管理機(jī)制:
類(lèi)似的,在使用chameleon sdk加載 JS 包的時(shí)候也會(huì)提供兩種緩存管理機(jī)制:
基于LRU的緩存策略,簡(jiǎn)單來(lái)說(shuō)就是實(shí)現(xiàn)了一個(gè)緩存池,每次請(qǐng)求先從緩存池中搜索一下,如果有就直接使用緩存池中的 JS 包,如果沒(méi)有,就從網(wǎng)絡(luò)上請(qǐng)求 JS 包并將其緩存在緩存池中,每一份 JS 包緩存按照最后使用時(shí)間排序,當(dāng)緩存池滿(mǎn)了以后,將最早使用過(guò)的緩存從緩存池中清理出去,保證客戶(hù)端上的資源占用可控。
與普通瀏覽器實(shí)現(xiàn)的協(xié)商式緩存類(lèi)似,用戶(hù)只需在靜態(tài)資源服務(wù)端配置好靜態(tài)資源的協(xié)商式緩存頭部,即可實(shí)現(xiàn)與普通瀏覽器一致的方式使用協(xié)商式緩存。
當(dāng) JS 包升級(jí)迭代需要在客戶(hù)端內(nèi)使用最新的包時(shí),在使用兩種緩存方式下,相應(yīng)的存在兩周更新方式,下面會(huì)詳細(xì)介紹
由于強(qiáng)制緩存,客戶(hù)端不會(huì)主動(dòng)的去向服務(wù)器請(qǐng)求最新的更新包,會(huì)導(dǎo)致客戶(hù)端一直使用老的版本。
為了能夠打到規(guī)避這種情況,我們提供了一種可供參考的解決方案:
設(shè)置chameleon.config.js中的hash: true,具體可參考工程化配置之文件指紋, CML 項(xiàng)目構(gòu)建出的 JS 包文件名會(huì)類(lèi)似如下
test_project_c6bdf9074a821f01e70f.js
得到以下可以訪問(wèn)的資源地址
https://www.static.com/test_project_c6bdf9074a821f01e70f.js
將入口頁(yè)面中的 cmlUrl cml_addr 替換成 encodeURIComponent 后的最新資源地址即可,比如
原 cmlUrl:
https://www.static.com/test_project.html?cml_addr=https%3A%2F%2Fwww.static.com%2Ftest_project_21f01e70fc6bdf9074a8.js
新的 cmlUrl
https://www.static.com/test_project.html?cml_addr=https%3A%2F%2Fwww.static.com%2Ftest_project_c6bdf9074a821f01e70f.js
最佳實(shí)踐
由于強(qiáng)制緩存下每次修改都需要修改入口頁(yè)面的 cmlUrl 中的 cml_addr 參數(shù),可能會(huì)導(dǎo)致修改頻繁影響效率,所以建議通過(guò)后端讀取 map.json 的方式下發(fā)跳轉(zhuǎn) cmlUrl 進(jìn)行統(tǒng)一管理。
具體請(qǐng)求過(guò)程如下圖所示:
這樣每次 bundle 修改后可以自動(dòng)完成更新
可以直接使用 http 的緩存更新策略,不需額外的配置。
在某些場(chǎng)景下,為了能夠讓頁(yè)面更快的呈現(xiàn)在用戶(hù)面前,需要讓客戶(hù)端提前下載一些 js 包,這時(shí)就需要用到預(yù)加載。
更多建議: