打開小程序開發者工具,在調試控制檯輸入openVendor
就會打開小程序的WeappVendor目錄,該目錄包括如下幾個主要內容:css
wcc xxx.wxml
wcsc xxx.wxss
正如上面分析的,經過調用小程序內置的可執行程序執行wcc xxx.wxml
,將指定的wxml轉換爲js腳本內容。其具體用法能夠--help查看,以下圖:
html
小程序開發者工具底層會將小程序項目中全部wxml轉爲js內容,能夠理解爲爲每一個頁面wxml進行了註冊。例如咱們小程序demo有兩個頁面index.wxml和logs.wxml,其中index.wxml內容以下圖:
經過wcc可執行程序生成的相關頁面註冊的代碼以下圖所示:
web
從頁面轉換的js內容來看,主要記錄標籤的屬性及其值等。另外,轉化的js腳本提供最核心的方法是$gwx
方法,能夠在開發者工具開發控制檯訪問到,其方法簽名以下:json
$gwx = function(path, global) { ... return function(env,dd,global){ ... } }
該方法根據傳入具體的頁面wxml路徑,找到對應的頁面,而後返回一個函數,向該函數傳入頁面渲染須要的數據(即Page中data對象)就能獲得該頁面wxml對應的js對象形式表示的dom樹。其實每一個小程序頁面在頁面準備初始化渲染時會調用這個$gwx
方法,調用以下圖所示:
小程序
另外,咱們直接在開發者工具的控制檯直接調用,輸入以下語句,能夠獲得的js對象表示以下圖:瀏覽器
$gwx('./pages/index/index.wxml')({show: true});
wcsc
可執行程序用於處理wxss,小程序底層使用該可執行程序轉換爲js內容來處理頁面css的引用。首先咱們來看下wxss
提供功能,以下圖:
緩存
小程序底層使用wcsc -db -pc
來轉換對應wxss文件的,其生成的js內容以下圖eval函數中的字符串所示:
微信
生成是js主要做用:app
setCssToHead
方法將轉換後的css內容添加到header小程序開發者工具的主入口也是小程序的啓動入口,是整個小程序開發者工具的控制層,例如建立或者銷燬webview等。它主要包括小程序的視圖層的webview,業務邏輯層webview,調試器的webview和編輯區的webview幾大塊;咱們只需關心視圖層和業務邏輯層的webview。啓動入口對應這一個index.html頁面,裏面引入主入口js,以下:框架
<div id=container class=container></div> <script src=../js/core/index.js> </script>
最終初次進入小程序主頁後,主入口index.html的渲染html中有關視圖層和業務邏輯層結果以下圖所示:
由此能夠證實,小程序開發者工具業務邏輯層是在webview中執行的,該webview雖然提供瀏覽器相關接口,可是小程序只是在其中單純的執行js代碼。
在咱們小程序demo中有index首頁navigateTo到logs日誌頁時,能夠看主入口dom的變化,見下圖:
從dom變化能夠看出,調用navigateTo至關於新打開一個webview加載另外一個頁面視圖,隨着打開的頁面愈來愈多,內存就比較吃緊。這也是爲何小程序對打開頁面數量有限制的緣由。從圖中可能也看出了,爲啥多加載了一個pageframe.html
的webview,這個是幹什麼用的?後面會說到它的做用。
咱們在寫小程序頁面視圖時,貌似並不關心webview中的html結構,這些都是小程序底層幫咱們實現, 咱們只須要寫頁面ui和業務邏輯便可。下面咱們來看看view視圖層小程序幫咱們作了什麼。先來看一下視圖層pageframe.html的模板:
其中,模板中的註釋佔位符通過後臺服務處理會注入不一樣js腳本,主要js內容:
<!-- deviceinfo -->
: 暫時無用的佔位符,會被空字符串""替換<!-- jsdebug -->
: 提供視圖層的WeixinJSBridge模擬實現以及一些事件的處理如enablePullDownRefresh,其對應的js內容爲extensions/pageframe/index.js.<!-- plugincode -->
: 小程序插件相關的代碼,若小程序使用插件則會注入<!-- wxmlcode -->
: 調用wcc
可執行命令生成的小程序註冊全部頁面wxml對應的js腳本內容<!-- wxsscode -->
: 調用wcss
可執行命令生成的js腳本內容,提供注入css到頁面的js方法;該內容會提早注入全局的css。<!-- wxappcode -->
: 小程序當前視圖頁面相關的配置json內容以及wxml和wxss轉換爲js的內容,可在控制檯輸入__wxAppCode__看相關信息<!-- vendorlist -->
: 小程序爲視圖層注入的基礎庫功能,包括WAWebview.js、WARemoteDebug.js和hls.js本節來詳細介紹下小程序視圖層實現的一些技術細節
首先看一下小程序官網頁面層級準備小節描述的一段內容:
wx.navigateTo會建立一個新的頁面層級,對於每個新的頁面層級,視圖層都須要進行一些額外的準備工做。在小程序啓動前,微信會提早準備好一個頁面層級用於展現小程序的首頁。除此之外,每當一個頁面層級被用於渲染頁面,微信都會提早開始準備一個新的頁面層級,使得每次調用wx.navigateTo都可以儘快展現一個新的頁面。
正如上文提到的,咱們在打開pages/logs/logs視圖頁面時,發現dom中多加載了一個__pageframe__/pageframe.html的視圖層,其模板內容正如上一節描述的。這個視圖層的做用正是爲了小程序提早爲一個新的頁面層準備的。
小程序每一個視圖層頁面內容都是經過pageframe.html模板來生成的,包括小程序啓動的首頁;下面來看看小程序爲快速打開小程序頁面作的技術優化:
這樣在後續新打開頁面時,都會走緩存的pageframe的內容,避免重複生成,快速打開一個新頁面。
其實在小程序開發者工具實現中,在建立每一個視圖層頁的webview時,都會爲其綁定了onLoadCommit
事件(它會在頁面加載完成後觸發,包含當前文檔的導航和副框架的文檔加載)。初始時webview的src會被指定爲空頁面地址http://127.0.0.1:${global.proxyPort}/aboutblank?${c}
,其中c爲對應webview的id。webview從空頁面到具體頁面視圖的過程以下:
http://127.0.0.1:${global.proxyPort}/__pageframe__/pageframe.html
。加載完成後,設置其src爲pageframe.html:
新的src內容加載完成後再次觸發onLoadCommit事件但根據條件不會執行reload方法。
pageframe.html頁面在dom ready以後觸發注入並執行具體頁面相關的代碼,此時經過history.pushState方法修改webview的src可是webview並不會發送頁面請求。
pageframe.html模板生成的內容除小程序基礎庫視圖層的底層功能以外,還包括小程序全部頁面的模板信息、配置信息以及樣式內容,這些均可以在生成pageframe.html的dom結構中窺探一二。
那麼,既然每一個視圖層頁面由pageframe模板生成,那麼小程序每一個頁面獨有的頁面內容如dom和樣式等如何生成呢,這主要是利用nw.js的executeScript方法來執行一段js腳原本注入只與當前頁面相關的代碼,包括當前頁面的配置,注入當前頁的css以及當前頁面的virtual dom的生成,注入的代碼以下:
最終生成的js代碼(拿pages/index/index爲例)以下圖:
其中:
history.pushState('','', 'http://127.0.0.1:59524/__pageframe__/pages/index/index')
這句代碼的做用修改當前webview的src,由於視圖層的webview的src爲pageframe.html,經過這句代碼將其變動爲具體的頁面地址。
另外,須要注意的是nw.js的executeScript方法注入的代碼是須要時機的,須要等到視圖層的初始化工做準備ready以後才行,那麼這個時機如何知道呢?細心的讀者可能發現,在pageframe模板的最後一個script的內容:
<script>alert("DOCUMENT_READY")</script>
這個從字面意思能夠看出此時應該是頁面dom ready的一個時機,經過alert來進行通知。
alert能通知消息?固然能夠的,在nw.js的webview中alert、prompt對應的彈框是被會阻止的,那麼經過爲webview綁定dialog事件來知道是那種彈框類型,以及提示內容,具體能夠查看這篇文章。例如小程序開發者工具綁定事件部分代碼(便於查看有修改):
this.webview.on('dialog', (a) => { a.preventDefault(); const b = a.messageType || '', c = a.messageText, d = a.dialog; if ('alert' === b) { c === 'DOCUMENT_READY' && (this.documentReady = !0, this.loadPage()) } ... })
這樣方法loadPage
就會觸發nw注入並執行頁面相關的代碼。最終生成的頁面視圖對應dom結構以下圖:
能夠看出,視圖頁面生成的dom結構中,document.body已無pageframe.html模板中對應body中的script內容,這是由於視圖層的WAWebview.js在經過virtual dom生成真實dom過程當中,它會掛載到頁面的document.body上,覆蓋掉pageframe.html模板中對應document.body的內容。
小程序將全部業務代碼置於同一個線程中運行,小程序開發者工具是在一個webview中執行;webview中的appservice.html引入業務代碼js以外,還有後臺服務內嵌的一些基礎功能代碼,appservice.html對應的模板內容以下:
通過後臺服務的處理,模板中的各類佔位符就被對應的js內容注入,下面就來簡單說幾個重要的注入內容:
<!-- wxconfig -->
: 小程序的配置項,包括用戶自定義與系統默認的整合結果。在控制檯輸入__wxConfig
能夠看出打印結果<!--devtoolsconfig-->
:小程序開發者配置,包括開發者工具版本,設置的請求域名、默認開發者工具的設置以及訪問Native方法須要permission的方法。控制檯輸入__devtoolsconfig
能夠看到其對應的信息<!-- wxmlxcjs -->
: 調用wcc
可執行命令生成的小程序註冊全部頁面wxml對應的js腳本內容,js腳本提供$gwx方法。<!-- asdebug -->
: 提供業務邏輯層的WeixinJSBridge模擬以及一些針對開發者工具的接口,如在控制檯輸入help能夠看到提供的接口。其內容爲extensions/appservice/index.js.<!-- vendorlist -->
: 爲業務邏輯層注入WAService.js,爲業務邏輯層提供小程序底層基礎庫的功能此外,開發者工具服務還在appservice.html的body注入一段腳本,腳本的做用是將業務邏輯代碼經過script動態注入到head中執行,這段代碼以下:
最終生成的appservice.html中的head狀況以下圖所示:
經過上圖能夠看出,咱們寫的頁面邏輯都引入到頁面中,而且分別從app.js開始一一執行;小程序代碼調用Page構造器的時候,小程序基礎庫會記錄頁面的基礎信息,如初始數據(data)、方法等。須要注意的是,若是一個頁面被屢次建立,並不會使得這個頁面所在的JS文件被執行屢次,而僅僅是根據初始數據多生成了一個頁面實例(this),在頁面JS文件中直接定義的變量,在全部這個頁面的實例間是共享的。