導(dǎo)語(yǔ):作為一名前端開發(fā),如果你還停留在應(yīng)用開發(fā)層面,那你就OUT了,快來(lái)跟我一起探討下小程序框架本身底層實(shí)現(xiàn)的一些技術(shù)細(xì)節(jié)吧,既能幫助我們理解整個(gè)小程序的運(yùn)行機(jī)制,方便應(yīng)用開發(fā)疑難雜癥處理,也可以作為裝逼神技~
導(dǎo)語(yǔ):作為一名前端開發(fā),如果你還停留在應(yīng)用開發(fā)層面,那你就OUT了,快來(lái)跟我一起探討下小程序框架本身底層實(shí)現(xiàn)的一些技術(shù)細(xì)節(jié)吧,既能幫助我們理解整個(gè)小程序的運(yùn)行機(jī)制,方便應(yīng)用開發(fā)疑難雜癥處理,也可以作為裝逼神技~ 一、概述 小程序就是基于當(dāng)前的WEB規(guī)范實(shí)現(xiàn)的一種程序,運(yùn)行在微信里面,既然是基于WEB規(guī)范,那自然離不開HTML,CSS和JS,雖然微信官方給出了不一樣的名稱:WXML,WXSS,但本質(zhì)上還是在整個(gè)WEB體系之下構(gòu)建的。 WXML vs HTML,大家想到了什么?XML有沒有,HTML和WXML都是XML的一套子集,只是HTML有完整和通用的文檔類型定義(DTD),WXML相對(duì)簡(jiǎn)單,僅僅是微信自定義的少量標(biāo)簽。 WXSS vs CSS,大家可以理解為就是CSS,只是自動(dòng)做了縮減和兼容處理,比如長(zhǎng)度單位使用rpx,支持import導(dǎo)入。
實(shí)現(xiàn)邏輯部分的JS還是通用的ES規(guī)范,并沒有什么不同,并且runtime還是Webview(IOS WKWEBVIEW, ANDROID X5) 開發(fā)工具 微信web開發(fā)者工具(集成編輯,調(diào)試,預(yù)覽和發(fā)布) 編輯功能比較弱,大家可以自行使用熟練的編輯器,我個(gè)人不太習(xí)慣,我還是使用webstorm開發(fā),但是調(diào)試必須在微信的編輯器,這個(gè)編輯器自動(dòng)監(jiān)聽了文件的修改,每次修改后保存會(huì)自動(dòng)刷新,進(jìn)入指定的首頁(yè)。 建議大家使用雙顯示器模式開發(fā),一般是編輯,一般用于調(diào)試,效率會(huì)提高不少。 大家猜下這個(gè)編輯器是用什么實(shí)現(xiàn)的? 項(xiàng)目結(jié)構(gòu) 官方demo目錄結(jié)構(gòu)和實(shí)際道聚城小程序的目錄結(jié)構(gòu)如下: 規(guī)定項(xiàng)目結(jié)構(gòu) 一個(gè)入口文件:app.js 一個(gè)全局樣式:app.wxss 一個(gè)全局配置:app.json 頁(yè)面:pages下,每個(gè)頁(yè)面再按文件夾劃分,每個(gè)頁(yè)面4個(gè)文件 視圖:wxml,wxss 邏輯:js,json(頁(yè)面配置,不是必須) 注:pages里面還可以再根據(jù)模塊劃分子目錄,孫子目錄,只需要在app.json里注冊(cè)時(shí)填寫路徑就行 以上是必須的文件和目錄,而實(shí)際中我們會(huì)增加別的目錄,如lib,comm和utils等等目錄,如道聚城小程序的目錄結(jié)構(gòu) 打包發(fā)布 開發(fā)完成后,我們就可以通過(guò)這里可視化的按鈕,點(diǎn)擊直接打包上傳發(fā)布,審核通過(guò)后用戶就可以搜索到了。 我們看到下面有幾個(gè)關(guān)于打包的選項(xiàng),怎么實(shí)現(xiàn)的呢? 這就涉及到這個(gè)編輯器的實(shí)現(xiàn)原理和方式了,它本身也是基于WEB技術(shù)體系實(shí)現(xiàn)的,nwjs+react,nwjs是什么:簡(jiǎn)單是說(shuō)就是node+webkit,node提供給我們本地api能力,而webkit提供給我們web能力,兩者結(jié)合就能讓我們使用JS+HTML實(shí)現(xiàn)本地應(yīng)用程序。 既然有nodejs,那上面的打包選項(xiàng)里的功能就好實(shí)現(xiàn)了。 ES6轉(zhuǎn)ES5:引入babel-core的node包 CSS補(bǔ)全:引入postcss和autoprefixer的node包(postcss和autoprefixer的原理看這里) 代碼壓縮:引入uglifyjs的node包 坑:前期寫的時(shí)候,使用了大量ES6的語(yǔ)法,在開發(fā)工具和IOS里都沒發(fā)現(xiàn)問(wèn)題,提交審核,提示有bug,特意提示是android機(jī)器,結(jié)果拿android機(jī)測(cè)試,果然有問(wèn)題,最后定位問(wèn)題,就是ES6語(yǔ)法導(dǎo)致的,在android上使用的x5內(nèi)核,對(duì)ES6的支持不好,要兼容的話,要么使用ES5的語(yǔ)法或者引入babel-polyfill兼容庫(kù)。 打包后的結(jié)構(gòu) 小程序打包后的結(jié)構(gòu)如下,開發(fā)模式下的很多文件都被打包了,具體怎么拿到上線后的正式包和怎么解壓。 所有的小程序基本都最后都被打成上面的結(jié)構(gòu) 1、WAService.js 框架JS庫(kù),提供邏輯層基礎(chǔ)的API能力 2、WAWebview.js 框架JS庫(kù),提供視圖層基礎(chǔ)的API能力 3、WAConsole.js 框架JS庫(kù),控制臺(tái) 4、app-config.js 小程序完整的配置,包含我們通過(guò)app.json里的所有配置,綜合了默認(rèn)配置型 5、app-service.js 我們自己的JS代碼,全部打包到這個(gè)文件 6、page-frame.html 小程序視圖的模板文件,所有的頁(yè)面都使用此加載渲染,且所有的WXML都拆解為JS實(shí)現(xiàn)打包到這里 7、pages 所有的頁(yè)面,這個(gè)不是我們之前的wxml文件了,主要是處理WXSS轉(zhuǎn)換,使用js插入到header區(qū)域 二、小程序架構(gòu) 微信小程序的框架包含兩部分View視圖層、App Service邏輯層,View層用來(lái)渲染頁(yè)面結(jié)構(gòu),AppService層用來(lái)邏輯處理、數(shù)據(jù)請(qǐng)求、接口調(diào)用,它們?cè)趦蓚€(gè)進(jìn)程(兩個(gè)Webview)里運(yùn)行。 視圖層和邏輯層通過(guò)系統(tǒng)層的JSBridage進(jìn)行通信,邏輯層把數(shù)據(jù)變化通知到視圖層,觸發(fā)視圖層頁(yè)面更新,視圖層把觸發(fā)的事件通知到邏輯層進(jìn)行業(yè)務(wù)處理。 小程序啟動(dòng)時(shí)會(huì)從CDN下載小程序的完整包,一般是數(shù)字命名的,如:_-2082693788_4.wxapkg 三、技術(shù)實(shí)現(xiàn) 小程序的UI視圖和邏輯處理是用多個(gè)webview實(shí)現(xiàn)的,邏輯處理的JS代碼全部加載到一個(gè)Webview里面,稱之為AppService,整個(gè)小程序只有一個(gè),并且整個(gè)生命周期常駐內(nèi)存,而所有的視圖(wxml和wxss)都是單獨(dú)的Webview來(lái)承載,稱之為AppView。所以一個(gè)小程序打開至少就會(huì)有2個(gè)webview進(jìn)程,正式因?yàn)槊總€(gè)視圖都是一個(gè)獨(dú)立的webview進(jìn)程,考慮到性能消耗,小程序不允許打開超過(guò)5個(gè)層級(jí)的頁(yè)面,當(dāng)然同是也是為了體驗(yàn)更好。 AppService 可以理解為也是一個(gè)頁(yè)面,只是主要功能是負(fù)責(zé)邏輯處理部分的執(zhí)行,底層提供一個(gè)WAService.js的文件來(lái)提供各種api接口,主要是以下幾個(gè)部分: 消息通信封裝為WeixinJSBridge(開發(fā)環(huán)境為window.postMessage, IOS下為WKWebview的window.webkit.messageHandlers.invokeHandler.postMessage,android下用WeixinJSCore.invokeHandler) 1、日志組件Reporter封裝 2、wx對(duì)象下面的api方法 3、全局的App,Page,getApp,getCurrentPages等全局方法 4、還有就是對(duì)AMD模塊規(guī)范的實(shí)現(xiàn) 然后整個(gè)頁(yè)面就是加載一堆JS文件,包括小程序配置config,上面的WAService.js(調(diào)試模式下有asdebug.js),剩下就是我們自己寫的全部的js文件,一次性都加載(這里跟單頁(yè)應(yīng)用一樣,一次加載,保證視圖切換的流程性)只是實(shí)現(xiàn)方式在開發(fā)模式和微信環(huán)境下不太一樣。 在開發(fā)環(huán)境下 1、頁(yè)面模板:app.nw/app/dist/weapp/tpl/appserviceTpl.js 2、配置信息,是直接寫入一個(gè)js變量,__wxConfig,如下圖: 3、 其它文件,如下圖: 線上環(huán)境 而在上線后是應(yīng)用部分會(huì)打包為2個(gè)文件,名稱app-config.json和app-service.js,然后微信會(huì)打開webview去加載。線上部分應(yīng)該是微信自身提供了相應(yīng)的模板文件,在壓縮包里沒有找到。 1、WAService.js(底層支持) 2、app-config.json(應(yīng)用配置) 3、app-service.js(應(yīng)用邏輯) 大家可能在想微信官方放出來(lái)的文檔里說(shuō)的是運(yùn)行在JavaScriptCore里面的,不是webview,這個(gè)后面再分析! AppView 這里可以理解為h5的頁(yè)面,提供UI渲染,底層提供一個(gè)WAWebview.js來(lái)提供底層的功能,具體如下: 線上環(huán)境: 模板文件:根目錄page-frame.html WAWebview.js :根目錄 WAWebview.js 每次都是直接加載page-frame.html,然后把指定的page merge到這個(gè)模板文件里實(shí)現(xiàn)頁(yè)面渲染。 實(shí)際上,這里的View不像html一樣,就是純粹的標(biāo)簽渲染UI,這里還是有相應(yīng)的JS處理邏輯的,只是主要是UI部分的邏輯,比如:小程序的組件就是有template和js組合而成的,像我們使用navigator標(biāo)簽,實(shí)際在跳轉(zhuǎn)的時(shí)候,還是使用wx.redirectTo實(shí)現(xiàn)的。 Service和View通信 使用消息publish和subscribe機(jī)制實(shí)現(xiàn)兩個(gè)Webview之間的通信,實(shí)現(xiàn)方式就是統(tǒng)一封裝一個(gè)WeixinJSBridge對(duì)象,而不同的環(huán)境封裝的接口不一樣,具體實(shí)現(xiàn)的技術(shù)如下: windows(開發(fā)環(huán)境) 通過(guò)window.postMessage實(shí)現(xiàn)(使用chrome擴(kuò)展的接口注入一個(gè)contentScript.js,它封裝了postMessage方法,實(shí)現(xiàn)webview之間的通信,并且也它通過(guò)chrome.runtime.connect方式,也提供了直接操作chrome native原生方法的接口) 發(fā)送消息:window.postMessage(data, ‘*’);,// data里指定 webviewID 接收消息:window.addEventListener(‘message’, messageHandler); // 消息處理并分發(fā),同樣支持調(diào)用nwjs的原生能力 在contentScript里面看到一句話,證實(shí)了appservice也是通過(guò)一個(gè)webview實(shí)現(xiàn)的,實(shí)現(xiàn)原理上跟view一樣,只是處理的業(yè)務(wù)邏輯不一樣 'webframe' === b ? postMessageToWebPage(a) : 'appservice' === b && postMessageToWebPage(a) IOS 通過(guò) WKWebview的window.webkit.messageHandlers.NAME.postMessage實(shí)現(xiàn) 微信navite代碼里實(shí)現(xiàn)了兩個(gè)handler消息處理器 invokeHandler: 調(diào)用原生能力 publishHandler:消息分發(fā) android 通過(guò)WeixinJSCore.invokeHanlder實(shí)現(xiàn),這個(gè)WeixinJSCore是微信提供給JS調(diào)用的接口(native實(shí)現(xiàn)) invokeHandler: 調(diào)用原生能力 publishHandler: 消息分發(fā) 四、組件實(shí)現(xiàn) 在WAWebview.js里有個(gè)對(duì)象叫exparser,它完整的實(shí)現(xiàn)小程序里的組件,看具體的實(shí)現(xiàn)方式,思路上跟w3c的web components規(guī)范神似,但是具體實(shí)現(xiàn)上是不一樣的,我們使用的所有組件,都會(huì)被提前注冊(cè)好,在Webview里渲染的時(shí)候進(jìn)行替換組裝。 exparser有個(gè)核心方法: regiisterBehavior: 注冊(cè)組件的一些基礎(chǔ)行為,供組件繼承 registerElement:注冊(cè)組件,跟我們交互接口主要是屬性和事件 組件觸發(fā)事件(帶上webviewID),調(diào)用WeixinJSBridge的接口,publish到native,然后native再分發(fā)到AppService層指定webviewID的Page注冊(cè)事件處理方法 五、總結(jié) 技術(shù)原理:小程序底層還是基于Webview來(lái)實(shí)現(xiàn)的,并沒有發(fā)明創(chuàng)造新技術(shù),但它提供了桌面端集成工具還是比較有創(chuàng)意和誠(chéng)意的,畢竟PC和移動(dòng)端實(shí)現(xiàn)還不太一樣。大大提升了開發(fā)效率 基礎(chǔ)框架:整個(gè)框架體系,比較清晰和簡(jiǎn)單,基于Web規(guī)范,保證現(xiàn)有技能價(jià)值的最大化,只需了解框架規(guī)范即可使用已有Web技術(shù)進(jìn)行開發(fā), MSSM:對(duì)邏輯和UI進(jìn)行了完全隔離,這個(gè)跟當(dāng)前流行的react,agular,vue有本質(zhì)的區(qū)別,小程序邏輯和UI完全運(yùn)行在2個(gè)獨(dú)立的Webview里面,而后面這幾個(gè)框架還是運(yùn)行在一個(gè)webview里面的,如果你想,還是可以直接操作dom對(duì)象,進(jìn)行ui渲染的 組件機(jī)制:引入組件化機(jī)制,但是不完全基于組件開發(fā),跟vue一樣大部分UI還是模板化渲染,這也是vue相對(duì)react更容易上手的原因,前端天生就更適合用模板機(jī)制渲染UI(效率最高),而引入組件機(jī)制能更好的規(guī)范開發(fā)模式,也更方便升級(jí),但是目前還不能自定義組件。 多種節(jié)制:不能同時(shí)打開超過(guò)5個(gè)窗口,打包文件不能大于1M,dom對(duì)象不能大于16000個(gè)等,這些都是為了保證更好的體驗(yàn)。 只要去做, 總有可能。 先轉(zhuǎn)發(fā),再點(diǎn)二維碼, 就是對(duì)我們最好的支持! 業(yè)界最頂尖的技術(shù)大咖 最權(quán)威的實(shí)戰(zhàn)分享 最前沿的行業(yè)咨詢 一切盡在騰訊課堂coding學(xué)院 |
|
來(lái)自: 昵稱SKYw7pk4 > 《技能》