深入小程式系列之一:小程式核心原理及模擬
什麼是小程式
小程式是一種新的移動應用程式格式,是一種依賴 Web 技術,但也整合了原生應用程式功能的混合解決方案。
目前市面上小程式平臺微信、支付寶、百度、頭條、京東、凡泰等;小程式一些特性有助於填補 Web 和原生平臺之間的鴻溝,因此小程式受到了一些超級應用程式的歡迎。
-
它不需要安裝,支援熱更新。
-
具備多個 Web 檢視以提高效能。
-
它提供了一些透過原生路徑訪問作業系統功能(原生介面)或資料的機制。
-
它的內容通常更值得信賴,因為應用程式需要由平臺驗證。
-
小程式可以分發到多個小程式平臺(Web、原生應用,甚至是 OS)。這些平臺還為小程式提供了入口,幫助使用者輕鬆找到所需的應用。
小程式核心功能
1、分離檢視層與邏輯層
在小程式中,檢視層通常與邏輯層分離。
-
檢視層 View 負責渲染小程式頁面,包括 Web 元件和原生元件渲染,可以將其視為混合渲染。例如,Web 元件渲染可以由 WebView 處理,但 WebView 不支援某些 Web 元件渲染,或者是效能受限;小程式還依賴於某些原生元件,例如地圖、影片等。
-
邏輯層 Service 是用主要用於執行小程式的 JS 邏輯。主要負責小程式的事件處理、API 呼叫和生命週期管理。擴充套件的原生功能通常來自宿主原生應用程式或作業系統,這些功能包括拍照、位置、藍芽、網路狀態、檔案處理、掃描、電話等。它們透過某些 API 呼叫。當小程式呼叫原生 API 時,它會將 API 呼叫傳遞給擴充套件的原生功能,以便透過 JSBridge 進一步處理,並透過 JSBridge 從擴充套件的原生功能獲取結果。Service 為每個 Render 建立連線,傳輸需要渲染的資料以進一步處理。
-
如果事件由小程式頁面中的元件觸發,則此頁面將向 Service 傳送事件以進一步處理。同時,頁面將等待 Service 傳送的資料來重新渲染小程式頁面。
-
渲染過程可被視為無狀態,並且所有狀態都將儲存在 Service 中。
檢視層和邏輯層分離有很多好處:
-
方便多個小程式頁面之間的資料共享和互動。
-
在小程式的生命週期中具有相同的上下文可以為具備原生應用程式開發背景的開發人員提供熟悉的編碼體驗。
-
Service 和 View 的分離和並行實現可以防止 JS 執行影響或減慢頁面渲染,這有助於提高渲染效能。
-
因為 JS 在 Service 層執行,所以 JS 裡面操作的 DOM 將不會對 View 層產生影響,所以小程式是不能操作 DOM 結構的,這也就使得小程式的效能比傳統的 H5 更好。
小程式雙執行緒模型模擬
先看一下執行結果
接下來我們將用 iOS 程式碼來模擬上述的雙執行緒模型。首先我們來實現檢視層與邏輯層的資料通訊
如上圖所示,檢視層與邏輯層都分別透過 JS Bridge 的 publish 和 subscribe 來實現資料的收發。
模擬實現
1、檢視層呼叫 JSBridge.publish把事件傳遞給原生;引數: {eventName: ‘’, data: {}}
//點選按鈕,通知JS執行業務邏輯 function onTest() { console.log('aaa') FinChatJSBridge.subscribe('PAGE_EVENT', function (params) { document.getElementById('testId').innerHTML = params.data.title }) FinChatJSBridge.publish('PAGE_EVENT', { eventName: 'onTest',data: {} }) }
2、原生 view 層收到 page 的事件,把事件傳遞轉發給 service 層處理
if ([message.name isEqualToString:@"publishHandler"]) { NSString *e = message.body[@"event"]; [self.service callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; }
3、原生 service 層收到原生 view 層的事件,透過 jsbridge 把事件及引數傳遞給檢視 ervice 層執行 js 邏輯
NSString *js = [NSString stringWithFormat:@"ServiceJSBridge.subscribeHandler('%@',%@)",eventName,jsonParam]; [self evaluateJavaScript:js completionHandler:nil];
4、檢視 service,收到事件後,執行 JS 業務程式碼
var Page = { setData: function(data) { //向原生檢視層傳送更新資料資訊 ServiceJSBridge.publish('PAGE_EVENT', { eventName: 'onPageDataChange', data: data }) }, methods: { onTest: function() { // 執行JS方法,模擬小程式的setData,把資料更新到檢視層 Page.setData({ title: '我來自JS程式碼更新' }) console.log('my on Test') } } } var onWebviewEvent = function(fn) { ServiceJSBridge.subscribe('PAGE_EVENT', function(params) { console.log('FinChatJSBridge.subscribe') var data = params.data, eventName = params.eventName fn({ data: data, eventName: eventName }) }) } var doWebviewEvent = function(pEvent, params) { // do dom ready if (Page.methods.hasOwnProperty(pEvent)) { // 收到檢視層的事件,執行JS對應的方法 Page.methods[pEvent].call(params) } }
5、執行業務 JS 程式碼後,把資料更新傳遞給檢視層去更新 UI 介面展示資料
ServiceJSBridge.publish('PAGE_EVENT',{ eventName:'onPageDataChange', data: data })
6、原生 service 層收到檢視 service 層的事件,把事件傳遞給原生檢視層
if ([message.name isEqualToString:@"publishHandler"]) { NSString *e = message.body[@"event"]; [self.controller callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; }
7、原生檢視層把收到的事件,傳遞給檢視 view 層
NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.subscribeHandler('%@',%@)",eventName,jsonParam]; [self evaluateJavaScript:js completionHandler:nil];
8、檢視 view 層,收到事件後,更新介面
FinChatJSBridge.subscribe('PAGE_EVENT',function(params){ document.getElementById('testId').innerHTML = params.data.title })
訂閱資料回撥
訂閱資料回撥 // 首先訂閱資料回撥 JSBridge.subscribe('PAGE_EVENT', function(params) { // ... 這裡對返回的資料進行處理 }) // 向JS Bridge釋出資料 // eventName: 用於標識事件名 // data: 為傳遞的資料 JSBridge.publish('PAGE_EVENT', { eventName: 'onTest', data: {} }) WKWebView 初始化
WKWebView 初始化
WKUserContentController *userContentController = [WKUserContentController new]; NSString *souce = @"window.__fcjs_environment='miniprogram'"; WKUserScript *script = [[WKUserScript alloc] initWithSource:souce injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:true]; [userContentController addUserScript:script]; [userContentController addScriptMessageHandler:self name:@"publishHandler"]; WKWebViewConfiguration *wkWebViewConfiguration = [WKWebViewConfiguration new]; wkWebViewConfiguration.allowsInlineMediaPlayback = YES; wkWebViewConfiguration.userContentController = userContentController; if (@available(iOS 9.0, *)) { [wkWebViewConfiguration.preferences setValue:@(true) forKey:@"allowFileAccessFromFileURLs"]; } WKPreferences *preferences = [WKPreferences new]; preferences.javaScriptCanOpenWindowsAutomatically = YES; wkWebViewConfiguration.preferences = preferences; self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:wkWebViewConfiguration]; self.webView.clipsToBounds = YES; self.webView.allowsBackForwardNavigationGestures = YES; [self.view addSubview:self.webView]; NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"view.html" ofType:nil]; NSURL *fileURL = [NSURL fileURLWithPath:urlStr]; [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
WKWebView 事件回撥處理
// 執行檢視層事件回撥 - (void)callSubscribeHandlerWithEvent:(NSString *)eventName param:(NSString *)jsonParam { NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.subscribeHandler('%@',%@)",eventName,jsonParam]; [self evaluateJavaScript:js completionHandler:nil]; } - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void(^)(id result,NSError *error))completionHandle { [self.webView evaluateJavaScript:javaScriptString completionHandler:completionHandler]; } #pragma mark - WKScriptMessageHandle // 檢視層JSBridge請求接收處理 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name isEqualToString:@"publishHandler"]) { NSString *e = message.body[@"event"]; [self.service callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; } }
檢視層程式碼
function onTest() { console.log('aaa') FinChatJSBridge.subscribe('PAGE_EVENT', function(params) { document.getElementById('testId').innerHTML = params.data.title }) FinChatJSBridge.publish('PAGE_EVENT', { eventName: 'onTest', data: {} }) }
<div id="testId">我來自檢視層!</div> <input type="button" value="呼叫JS邏輯層setData" style="border-radius:15px;background:#ed0c50;border: #EDD70C;color: white;font-size: 14px; width: 80%;" onclick="onTest();" />
邏輯層程式碼
// page 對像模擬 var Page = { setData: function(data) { ServiceJSBridge.publish('PAGE_EVENT', { eventName: 'onPageDataChange', data: data }) }, methods: { onTest: function() { Page.setData({ title: '我來自JS程式碼更新' }) console.log('my on Test') } } } var onWebviewEvent = function(fn) { ServiceJSBridge.subscribe('PAGE_EVENT', function(params) { var data = params.data, eventName = params.eventName fn({ data: data, eventName: eventName }) }) } var doWebviewEvent = function(pEvent, params) { // do dom ready if (Page.methods.hasOwnProperty(pEvent)) { Page.methods[pEvent].call(params) } } onWebviewEvent(function(params) { var eventName = params.eventName var data = params.data return doWebviewEvent(eventName, data) })
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69971226/viewspace-2686199/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入小程式系列(三) ReactNative和小程式混編React
- 深入小程式系列之二、Flutter 和小程式混編Flutter
- 安卓小程式模擬定位測試安卓
- 模擬微信小程式頁面Page方法微信小程式
- myvue 模擬vue核心原理Vue
- 微信小程式教程系列微信小程式
- 深入解讀-微信小程式SDK微信小程式
- 深入wepy小程式元件化框架元件化框架
- 小程式踩坑系列一
- 小程式點睛之一:如何將小程式非同步回撥介面 Promise 化非同步Promise
- 小程式開發,小程式代理,小程式加盟,小程式創業創業
- 開發小細節系列之一
- 微信小程式開發深入解讀微信小程式
- 小程式3:ATM小程式
- 微信小程式開發系列二:微信小程式的檢視設計微信小程式
- 微信小程式開發系列教程三:微信小程式的除錯方法微信小程式除錯
- 微信小程式開發系列七:微信小程式的頁面跳轉微信小程式
- 微信小程式之小白教程系列 第二篇 微信小程式 -- 入口微信小程式
- 微信小程式 | 49,小程式入門集錦系列文章20篇微信小程式
- 小程式居然可以用WXS模擬實現過濾器!過濾器
- erlang小demo2_gen_server模擬遊戲伺服器程式管理Server遊戲伺服器
- 模擬費用流小記
- 探索小程式底層架構原理架構
- 微信小程式入門教程之一:初次上手微信小程式
- 小程式系列之網路請求
- 【LiteApp系列】何為愛奇藝小程式?APP
- 微信小程式之小白教程系列 第一篇 微信小程式 — Hello World微信小程式
- 微信小程式之小白教程系列 第一篇 微信小程式 -- Hello World微信小程式
- ThinkPHP小程式導航,小程式商店,小程式推薦平臺PHP
- 實現微信小程式編譯和執行環境系列(核心篇一)微信小程式編譯
- 微信小程式小技巧微信小程式
- 小程式
- 小程式原理之: WXSS 編譯編譯
- 微信小程式:小程式碼、小程式二維碼、普通二維碼微信小程式
- Windows原理深入學習系列-Windows核心提權Windows
- 微信小程式開發系列 (四) :微信小程式的頁面跳轉路由設計微信小程式路由
- 【小程式踩坑系列5】小程式內多重呼叫原生promise,無返回,無報錯,程式碼卡住Promise
- 盲盒小程式開發的核心功能