讓別人的小程序運行在自己的app中

概要

本文包括的內(nèi)容:

  • 小程序在微信開發(fā)者工具中婿脸,通過構(gòu)建生成真正的執(zhí)行代碼和安裝包魂奥,****.wxapkg终抽。wxml和wxss在構(gòu)建這一步就被轉(zhuǎn)換成了html和css(virtual-DOM)势木。微信開發(fā)者工具中可以得到構(gòu)建腳本和各個版本的js運行SDK文件蛛倦。
  • 小程序是以wxapkg文件的形式下發(fā)的,可以拿到wxapkg后解壓拿到執(zhí)行代碼啦桌。同時解壓微信.ipa拿到當(dāng)前的js運行SDK文件,主要是WAWebView.js和WAService.js溯壶。
  • 小程序在App中執(zhí)行時的時候分為三個不同的模塊,View/Service/Natvie,各司其職。
  • 實現(xiàn)過程遇到大量的細節(jié)和坑江场。

介紹小程序原理的文章比較多缤言,這篇講的比較細:微信小程序架構(gòu)分析。這篇文章的作者也成功的實現(xiàn)了wept,讓小程序運行在自己的webapp里傲武。

參考最多的是微店的Hera,完成度非常高的小程序框架,能夠?qū)⑿〕绦虻膁emo代碼在web/iOS/android運行起來,而且實現(xiàn)了很多工具慨蓝。Hera的問題是開發(fā)于比較早期的版本,不兼容最新的版本了端幼。Hera還有一個問題是他修改了小程序構(gòu)建之后的目錄結(jié)構(gòu)礼烈,采用了service.html作為service部分的入口,跟小程序本身的實現(xiàn)尚有有一些區(qū)別婆跑。所以Hera只能夠構(gòu)建執(zhí)行自己編寫的小程序此熬,不能執(zhí)行別人編寫的小程序。

我的目標(biāo)是能夠運行其它人開發(fā)的app滑进,意味著我只能通過逆向的方式拿到wxapkg犀忱。但是因為拿不到源碼,所以要盡可能在構(gòu)建環(huán)節(jié)跟小程序保持一致扶关。

經(jīng)過數(shù)周的掙扎阴汇,目前已經(jīng)實現(xiàn)了運行官方demo。已經(jīng)達到"可行"的階段驮审,但是還遠遠談不上“可用”鲫寄,因為需要實現(xiàn)小程序大量的API吉执,這是個體力活,依賴個人的力量難以完成地来。

構(gòu)建

官方demo小程序原先的目錄分為幾類文件:

  • 配置: 在這個目錄中戳玫,app.json里保存了頁面信息、tabbar信息未斑、網(wǎng)絡(luò)超時配置等等咕宿。 config.js保存了騰訊云后臺服務(wù)解決方案的配置。
  • js文件: js定義了各種函數(shù)接口邏輯
  • wxml: 類似于html,定義了頁面結(jié)構(gòu)
  • wxss: 類似css
demo
├── app.js
├── app.json
├── app.wxss
├── config.js
├── image
│   ├── green_tri.png
│   ├── ...
├── page
│   ├── API
│   │   ├── index.js
│   │   ├── index.json
│   │   ├── index.wxml
│   │   ├── index.wxss
│   │   ├── pages
│   │   │   ├── action-sheet
│   │   │   │   ├── action-sheet.js
│   │   │   │   ├── action-sheet.json
│   │   │   │   ├── action-sheet.wxml
│   │   │   │   └── action-sheet.wxss
│   │   │   ├── ...
│   │   └── resources
│   │       └── ...
│   ├── ...
├── project.config.json
├── util
│   └── util.js
└── vendor
    └── qcloud-weapp-client-sdk
        ├── ...

經(jīng)過小程序的開發(fā)環(huán)境構(gòu)建后蜡秽,生成了一個*.wxapkg文件府阀。
這個文件可以通過從越獄的iPhone或者root的安卓手機上拿到。有部分人用charles通過https抓包拿到了下載鏈接芽突,也拿到了包试浙。
拿到后要進行解包。有大神已經(jīng)通過反編譯安卓apk的方式拿到了解包部分的代碼寞蚌,然后用python重寫了一遍田巴。源碼見wechat-app-unpack

解包后得到的目錄如下:

1.wxapkg_dir
├── app-config.json
├── app-service.js
├── app-service.js.map
├── image
│   ├── green_tri.png
│   ├── ...
├── page
│   ├── API
│   │   ├── index.html
│   │   ├── pages
│   │   │   ├── action-sheet
│   │   │   │   └── action-sheet.html
│   │   │   ├── ...
│   │   └── resources
│   │       └── kind
│   │           ├── api.png
│   │           ├── ...
│   ├── ...
└── page-frame.html

轉(zhuǎn)換過程可以分為三部分:

  • 從wxml/wxss到html挟秤。 page-frame.html里定義了所有的virtural-dom壹哺,來自所有的wxss和wxml的轉(zhuǎn)換,文件非常大艘刚。 page.html里很簡單管宵,就是從page-frame.html里提取對應(yīng)的virtual-dom。包括wxss和wxml對應(yīng)的邏輯攀甚。
  • app-service.js是從之前所有的js文件轉(zhuǎn)換而來箩朴。
  • app-config.json是從app.json轉(zhuǎn)換而來。

openVendor命令可以在小程序中獲取到構(gòu)建腳本wcc和wcsc, 以及各個版本小程序的執(zhí)行SDK ****.wxvpkg,這個SDK也可以用wechat-app-unpack解開秋度,解開后里面就是WAService.js和WAWebView.js等代碼隧饼。

wxml/wxss的構(gòu)建原理

wxss 轉(zhuǎn)換成了css,wxml轉(zhuǎn)換成了inject_js,實際上就是virtual_dom静陈。
是用什么工具轉(zhuǎn)換的?小程序里是叫wcc和wcsc诞丽。在開源工具hera自己實現(xiàn)了一套wxss-transpiler和wxml-transpiler鲸拥。而hera的前身wept是直接使用wcc和wcsc。
我們?yōu)榱藴p少維護成本僧免,直接采用wcc和wcsc刑赶。
因為我們沒有wcc和wcsc的源碼,所以只能借助wxss-transpiler和wxml-transpiler來幫助我們理解wxml/wxss的構(gòu)建原理懂衩。

wxss-transpiler調(diào)用了一個PostCSS的插件撞叨,用來處理wxss金踪。
PostCSS 提供了一種方式用 JavaScript 代碼來處理 CSS。它負責(zé)把 CSS 代碼解析成抽象語法樹結(jié)構(gòu)(Abstract Syntax Tree牵敷,AST)胡岔,再交由插件來進行處理。插件基于 CSS 代碼的 AST 所能進行的操作是多種多樣的枷餐,比如可以支持變量和混入(mixin)靶瘸,增加瀏覽器相關(guān)的聲明前綴,或是把使用將來的 CSS 規(guī)范的樣式規(guī)則轉(zhuǎn)譯(transpile)成當(dāng)前的 CSS 規(guī)范支持的格式毛肋。

wxml-transpiler:實現(xiàn)了一個轉(zhuǎn)譯器的工作怨咪,比如postcss也是轉(zhuǎn)譯器,包括解釋器(parser),代碼轉(zhuǎn)換器(Transformer),代碼生成器(Generator)润匙。這個是閉源的诗眨。

  • 根據(jù)輸入的列表,讀取所有文件
  • 調(diào)用VUE的HTML Parser孕讳,解析輸入的標(biāo)簽及屬性匠楚,生成一顆DOM樹。vue解決不了的js語言卫病,用babylon庫來處理油啤。(Parse)
  • 在解析組件的標(biāo)簽時,對其上包含的屬性值進行解析(邊Parse邊Transform)
  • 根據(jù)已有的AST生成JS文件(Generate)

更多實現(xiàn)原理見這篇文章

模塊之前的通信

image

小程序在App中執(zhí)行時的時候分為三個不同的模塊蟀苛,View/Service/Native益咬,各司其職。

View和Service都在WKWebView中執(zhí)行帜平,互相無法調(diào)用幽告。他們之間通過Native層通信。

Native和WebView之間通過webkit.messagehandler和evaluateJavascript互相調(diào)用裆甩。

  • WeixinJSBridge.publish: view和service之間的透傳冗锁,在WKWebView之間傳遞消息。

  • WeixinJSBridge.subscribe: 注冊監(jiān)聽嗤栓,監(jiān)聽view和service之間的消息調(diào)用冻河。

  • WeixinJSBridge.invoke: View或者Service傳遞消息到Native,然后Native使用邏輯調(diào)用js callback。

  • WeixinJSBridge.on:監(jiān)聽Native的事件茉帅。

如何執(zhí)行

這里以iOS為例介紹Native執(zhí)行過程叨叙。安卓類似。

通過解壓微信的ipa可以拿到WAService.js和WAWebView.js兩個基礎(chǔ)庫文件堪澎,文件內(nèi)容與hera的service.js/view.js已經(jīng)有了較大的區(qū)別擂错。
我們采用小程序的架構(gòu)和hera的兩個webView的方案,盡可能模仿小程序的執(zhí)行過程樱蛤。

View部分

View部分是比較直觀的钮呀,就是WKWebView加載web頁剑鞍。這里需要在app-config.json里讀取到首頁的路徑,然后加載該頁面爽醋。這個路徑下的xxx/index.html是無法直接加載的蚁署,需要做一些處理。要引入本地執(zhí)行SDK里的index.css和view.js, 然后把page-frame.html里的virtual-dom全部塞進該頁面子房。 然后loadHTML即可形用。

View所有的WKWebView也是要注冊WKUserContentController的,用于通信证杭。
通過反匯編可以得知這個類在微信中叫YYWAWebView田度,調(diào)用js是直接調(diào)用-evaluateJavaScript:completionHandler:方法的。

view.js中包含的邏輯:

  • WeixinJSBridge 對象處理消息通信: invoke invokeCallbackHandler on publish subscribe subscribe subscribeHandler解愤。
  • Reporter 對象
  • wxparser 對象镇饺,提供 dom 到 wx element 對象之間的映射操作,提供元素操作管理和事件管理功能送讲。
  • virtual dom 渲染算法實現(xiàn)奸笤,提供 diff apply render 等方法,該模塊接口基本與 virtual-dom 一致哼鬓,這里特別的地方在于它所 diff 和生成的并不是原生 DOM监右,而是各種模擬了 DOM 接口的 wx element 對象

Service部分

Service部分的實現(xiàn),Hera和微信小程序采取的了不同的架構(gòu)异希。
Hera的實現(xiàn)較為簡潔健盒,跟View部分保持一致,采用了WKWebView称簿,調(diào)用-evaluateJavaScript:completionHandler:方法執(zhí)行js扣癣,js回調(diào)OC時使用WKScriptMessageHandler。
通過反匯編可以得知這個類在微信中叫WAJSCoreService憨降,js和OC之間的調(diào)用是采用JavascriptCore互相調(diào)用父虑。

JavascriptCore它首先要加載app-config.json并把這個配置賦給一個全局對象__wxConfig。然后他要加載service.js是SDK基礎(chǔ),再然后他要加載app-service.js授药,這里面包含了用戶編寫的js邏輯士嚎。最后它發(fā)出全局消息
WeixinJSBridge.publish('serviceReady',,);</script>喚起小程序app的初始化。

Service.js中包含的邏輯:

  • 跟 view.js 一樣的 WeixinJSBridge 兼容模塊
  • view.js 一樣的 Reporter 模塊
  • appServiceEngine 模塊悔叽,提供 Page航邢,App,GetApp 接口

Native部分

Native執(zhí)行的問題比較復(fù)雜骄蝇,因為基本是黑盒,里面發(fā)生了什么并不知道操骡。
hera的方案在構(gòu)建過程就已經(jīng)跟小程序?qū)嶋H的方案有所區(qū)別九火,會提高維護成本赚窃。所以我們只能靠猜測來實現(xiàn)Native的執(zhí)行過程。

Native部分就是作為入口岔激,運行環(huán)境勒极,跳轉(zhuǎn),轉(zhuǎn)發(fā)消息虑鼎,實現(xiàn)擴展辱匿。包括網(wǎng)絡(luò)模塊/攝像頭/tabbar實現(xiàn)的都是擴展。
我們可以得知的是消息傳遞的協(xié)議炫彩。然后只能通過safari來調(diào)試webView匾七,根據(jù)協(xié)議的名稱和出入?yún)聿聹y協(xié)議的內(nèi)容。

主要的困難點

  • 由于壓縮后的view.js和service.js基本不具備可讀性江兢,而virtual-dom又徹底不具備可讀性...所以就算猜中了協(xié)議昨忆,最后也往往是魔改∩荚剩可維護性極差邑贴。
  • 工作量不小,因為調(diào)試困難叔磷,無法閱讀拢驾。之后隨著小程序SDK升級,能用多久也不可知改基。加上也有各種微逆向的操作繁疤,小程序封上任何一個接口,都會導(dǎo)致?lián)浣至攘选嵌洼?沙掷m(xù)性極差》馇。總之非常佩服微店的同學(xué)能把Hera搞出來:)

小程序的性能啟發(fā)

小程序是顛覆我對Web的固有印象,最初還以為是類似weex或者rn的調(diào)用原生的方式麻养,沒想到幾乎完全是運行在WKWebView之上的。

  • 采用virtual DOM诺舔,操作JS比html性能高很多鳖昌,因為是diff后再操作dom,不需要全部重新渲染低飒,快很多许昨。
  • WKWebView,滑動60fps褥赊,在獨立于App之外的進程執(zhí)行糕档。
  • 部分邏輯Native化,比如收發(fā)網(wǎng)絡(luò)請求拌喉,比如數(shù)據(jù)持久化速那。 逐步用native組件來替換h5組件俐银。比如tabbar。
  • 重用webView以及提前初始化webView等等技巧端仰。

存在的問題:

  • 看消息傳遞的原理就能發(fā)現(xiàn)捶惜,傳遞的過程太長了,尤其是setData:這種傳遞整個model對象荔烧,是兩次對象的深拷貝吱七,可能會增加兩次json的序列化和反序列化,如果model對象很復(fù)雜對性能影響比較大鹤竭。
  • 頁面初始化/響應(yīng)速度/UI細節(jié)還是跟原生有差距踊餐。

當(dāng)然已經(jīng)比純web頁強很多了。目前來看還是只適合輕量化的應(yīng)用诺擅。受制于架構(gòu)以及微信的平臺市袖,個人認為是對Web的替代和改善。但是就算在可見的未來烁涌,還是很難跟native抗衡苍碟。

現(xiàn)代瀏覽器和操作系統(tǒng)之間的界限越來越模糊。App的"下載/安裝"過程本身就是一種妥協(xié)撮执。只要小程序的體驗足夠好,應(yīng)該沒有人會拒絕微峰。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抒钱,隨后出現(xiàn)的幾起案子蜓肆,更是在濱河造成了極大的恐慌,老刑警劉巖谋币,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仗扬,死亡現(xiàn)場離奇詭異,居然都是意外死亡蕾额,警方通過查閱死者的電腦和手機早芭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诅蝶,“玉大人退个,你說我怎么就攤上這事〉骶妫” “怎么了语盈?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缰泡。 經(jīng)常有香客問我刀荒,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任缠借,我火速辦了婚禮资溃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烈炭。我一直安慰自己,他們只是感情好宝恶,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布符隙。 她就那樣靜靜地躺著,像睡著了一般垫毙。 火紅的嫁衣襯著肌膚如雪霹疫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天综芥,我揣著相機與錄音丽蝎,去河邊找鬼。 笑死膀藐,一個胖子當(dāng)著我的面吹牛屠阻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播额各,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼国觉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虾啦?” 一聲冷哼從身側(cè)響起麻诀,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎傲醉,沒想到半個月后蝇闭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡硬毕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年呻引,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昭殉。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡苞七,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挪丢,到底是詐尸還是另有隱情蹂风,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布乾蓬,位于F島的核電站惠啄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撵渡,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一融柬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趋距,春花似錦粒氧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至翼雀,卻和暖如春饱苟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背狼渊。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工箱熬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狈邑。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓城须,卻偏偏與公主長得像,于是被迫代替她去往敵國和親官地。 傳聞我的和親對象是個殘疾皇子酿傍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 每個人都有拖延癥赤炒,有自制力、堅持做一件事情亏较,其實很消耗能量莺褒,如果總是失敗,其實很傷害自信和意志力雪情。 但如果通過循序...
    曹門霞客行閱讀 529評論 7 20
  • 東野圭吾先生的小說以前沒怎么看過遵岩,偶然機會在朋友的桌頭看到一本他的推理小說《嫌疑人x的獻身》,一時興起用了大概一個...
    吃嗎帶走閱讀 273評論 0 0
  • 感賞今天晚上有很多顧客試衣服巡通,這樣就提高了顧客買單的幾率尘执。 感賞自己因為前天的事情受宇宙爸爸點化,讓我認識到了努力...
    離不若閱讀 170評論 0 0