從源碼看微信小程序啓動過程

1、寫做背景

接觸小程序一年多,真實體驗就是小程序開發門檻相對而言確實比較低。不太小程序的開發方式,一直是開發者吐槽的,如習慣了 Vue,React 開發的開發者常常會吐槽小程序一個 Page 必須由多個文件組成,組件化支持不完善或者說不能很是愉快的開發組件。在之前小項目中沒太大感受,從加入有贊,參與有贊微商城小程序的開發,是真切的體會到對於大型小程序項目開發的複雜性。css

有贊從微信小程序內測就開始開發小程序,在不支持自定義組件的時代,只能經過 import 的形式拆分模塊或實現組件。在業務複雜的頁面,可能會 import 很是多的模塊,而相應的 wxss 也須要 import 樣式,除了操做繁瑣,有時候也不免遺漏。web

做爲開發者,咱們固然但願可讓工做更簡單,更愉快,也但願改善咱們的開發方式。因此但願可以更瞭解微信小程序框架,減小沒必要要的試錯,因而有了一次對小程序框架的 debug 之旅。(基礎庫 1.9.93)json

經過三週空餘時間的 debug,也算對小程序框架有了一些淺顯的認識,達到了最初的目的;對小程序啓動,實例,運行等有了真切的體會。這篇文章記錄了小程序框架的基本代碼結構,啓動流程,以及程序實例化過程。小程序

本文的目的是但願把我看到的分享給對小程序感興趣或者正在開發小程序的讀者,主要解答「框架對傳入的對象等到底作了什麼」。微信小程序

2、從啓動流程一窺小程序框架細節

在開發者工具中使用 help() 方法,能夠查看一些指令和方法。使用其中的 openVendor 方法能夠打開微信開發者工具在小程序框架所在目錄。其中以包括以基礎庫命名的目錄和其餘幫助文件,如其中有兩個工具 wcc,wcsc。wcc 可把 wxml 轉換爲對應的 JS 函數 —— $gwx(path, global),wcsc 可將 wxss 轉換爲 css。而基礎庫目錄包括 WAService.js 和 WAWebview.js 文件。小程序框架在開發者工具中以 WAService.js 命名(WAWebview.js 不知其做用,據說在真機環境使用該文件)。瀏覽器

在開發中工具命令行使用 document.head 能夠查看到小程序的啓動流程大體以下: bash

以小節的方式分別介紹這些流程,小程序是如何處理的(小節編號與圖中編號相同)。

一、初始化全局變量

下圖是小程序啓動是初始化的一些全局的變量: 微信

那些使用「__」開頭,未在文檔中說起可以使用變量是不建議使用的,__wxAppCode__ 在開發者工具中分爲兩類值,json 類型和 wxml 類型。以 .json 結尾的,其 key 值爲開發者代碼中對應的 json 文件的內容,.wxml 結尾的,其 key 值爲經過調用 $gwx('./pages/example/index.wxml') 將獲得一個可執行函數,經過調用這個函數可獲得一個標識節點關係的 JSON 樹。微信開發

二、加載框架(WAService.js)

使用工具對 WAService.js 進行格式化後進行 debug。能夠發現小程序框架大體由: WeixinJSBridgeNativeBufferwxConsoleWeixinWorkerJavaScript 兼容(這部分爲猜想)、 Reporterwxexparser__virtualDOM____appServiceEngine__ 幾部分組成。app

其中除了 wxWeixinJSBridge 這兩個基礎 API 集合,exparser, __virtualDOM__, __appServiceEngine__ 這三部分做爲框架的核心,__appServiceEngine__ 提供了框架最基本的接口如 App,Page,Component;exparser 提供了框架底層的能力,如實例化組件,數據變化監聽,view 層與邏輯層的交互等;而 __virtualDOM__ 則起着連接 __appServiceEngine__exparser 的做用,如對開發者傳入 Page 方法的對象進行格式化再傳入 exparser 的對應方法處理。

框架對外暴露了如下API:Behavior,App,Page,Component,getApp,getCurrentPages,definePlugin,requirePlugin,wx。

三、業務代碼的加載

在小程序中,開發者的 JavaScript 代碼會被打包爲

define('xxx.js', function(require, module, exports, window, document, frames, self, location, navigator, localStorage, history, Caches, screen, alert, confirm, prompt, fetch, XMLHttpRequest, WebSocket, webkit, WeixinJSCore, Reporter, print, WeixinJSBridge) {
  'use strict';

  // your code
})
複製代碼

這裏的 define 是在框架中定義的方法,在框架中提供了兩個方法:require 和 define 用來定義和使用業務代碼。其方式有些像 AMD 規範接口,經過 define 定義一個模塊,使用 require 來應用一個模塊。可是也有很大區別,首先 define 限制了模塊可以使用的其餘模塊,如 window,document;其次 require 在使用模塊時只會傳入 require 和 module,也就是說參數中的其餘模塊在定義的模塊中都是 undefined,這也是不能在開發者工具中獲取一些瀏覽器環境對象的緣由。

在小程序中,JavaScript 代碼的加載方式和在瀏覽器中也有些不一樣,其加載順序是首先加載項目中其餘 js 文件(非註冊程序和註冊頁面的 js 文件),其次是註冊程序的 app.js,而後是自定義組件 js 文件,最後纔是註冊頁面的 js 代碼。並且小程序對於在 app.js 以及註冊頁面的 js 代碼都會加載完成後當即使用 require 方法執行模塊中的程序。其餘的代碼則須要在程序中使用 require 方法纔會被執行。

下面詳細介紹了 app.js,自定義組件,頁面 js 代碼的處理流程。

四、加載 app.js 與註冊程序

在 app.js 加載完成後,小程序會使用 require('app.js') 註冊程序,即對 App 方法進行調用,App 方法是對 __appServiceEngine__.App 方法的引用。

下圖是框架對於 App 方法調用時的處理流程:

App 方法根據傳入的對象實例化一個 app 實例,其生命週期函數 onLaunch 和 onShow 由於使用不一樣的方式獲取 options的參數。在有些須要根據場景值來實現需求的,或許使用 onShow 中的場景值更合適。

在實際開發過程當中發現,在微信頂部喚起小程序和在小程序列表喚起的 options 也是不同的。在該案例中經過點擊分享的小程序進入後,關閉小程序,再經過不一樣方式進入小程序,經過頂部喚起的仍是 options 的 path 屬性仍是分享出來的 path,可是經過列表中打開直接回到了首頁,這裏 App 中的 onShow 就會獲取到不一樣的 options。

五、加載自定義組件代碼以及註冊自定義組件

自定義組件在 app.js 以後被加載,小程序會在這個過程當中加載完全部的自定義組件(分包中自定義組件沒有有測試過),而且是加載完成後自動註冊,只有註冊完成後纔會加載下一個自定義組件的代碼。

下圖是框架對於 Component 方法處理流程:

圖中介紹了框架如何對傳入 Component 方法的對象的處理,其後面還有不少深刻的對於組件實例化的步驟沒有在圖中表示出來,具體能夠在文章最後的附件中查看。

自定義組件在小程序中愈來愈完善,其擁有的能力也比 Page 更強大,然後面會提到在使用自定義組件的 Page 中,Page 實例也會使用和自定義組件同樣的實例化方式,也就是說,他擁有和自定義組件同樣的能力。

六、加載頁面代碼和註冊頁面

加載頁面代碼的處理流程和加載自定義組件同樣,都是加載完成後先註冊頁面,而後纔會加載下一個頁面。

下圖是註冊一個頁面時框架對於 Page 方法的處理流程:

Page 運行流程

Page 方法會根據是否使用自定義組件作不一樣的處理。使用自定義組件的 page 對象會被處理爲和自定義組件的結構,並在頁面實例化時使用不一樣的處理流程進行實例化。固然對於開發而言沒任何不一樣。

從圖中能夠發現 Page 傳入的(生命週期)代碼並不會在這裏被執行,能夠經過下面小節瞭解 Page 實例化的詳細過程。

七、等待頁面 Ready 和 Page 實例化

還記得上面介紹的啓動流程中最後一步等待頁面 Ready?嚴格來說是等待瀏覽器 Ready,小程序雖然有部分原生的組件,不過本質上仍是一個 web 程序。

在小程序中切換頁面或打開頁面時會觸發 onAppRoute 事件,小程序框架經過 wx.onAppRoute 註冊頁面切換的處理程序,在全部程序就緒後,以 entryPagePath 做爲入口使用 appLaunch 的方式進入頁面。

下圖是處理導航的程序流程:

從圖中能夠看出頁面的實例化是在進入頁面時進行,下圖是具體的實例化過程:

下圖是最終可獲得 Page 實例:

能夠發現其中多了 onRouteEnd API,實際該接口不會被調用。其中以 component 標記的表示只有在使用了自定義組件時纔會有的方法和屬性。在前面第 5 小節提到了對於使用自定義組件的頁面會按照自定義組件方式解析,這些屬性和方法與自定義組件表現一致。

八、關於 setData

小程序框架是一個以數據驅動的框架,固然不能少了對他如何實現數據綁定的探索,下圖是 Page 實例的 setData 執行流程:

其中 component:setData 表示使用自定義組件的 Page 實例的 setData 方法。

3、寫在最後

這是一次不徹底的小程序框架探索,是在微信開發工具中 debug 的結果。雖然對於實際開發沒有什麼太大的幫助,可是對框架如何對開發的 js 代碼進行處理有了一個很明確的認識,在使用一些 js 特性時能夠有明確的感知。若是你還疑惑「小程序框架對傳入的對象等到底作了什麼」那必定是我表達能力太差,說聲對不起。

經過這一次 debug ,也給我引入了新的問題,還但願可以有更多的討論:

  • 自定義組件太多啓動時會耗時處理自定義組件
  • 文件太多會耗時讀文件
  • 合理的設計分包很重要

一份在調試過程當中的筆記 小程序框架不徹底分析.xmind,若是看官有興趣能夠下載看看。固然最後對於框架中已有的能力,仍是很是但願微信能夠開放更多穩定的接口,並在文檔中告知開發者,讓開發變得簡單一些。

本文首發於有贊技術博客

相關文章
相關標籤/搜索