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

ghuk2926 6年前發布 | 49K 次閱讀 小程序開發 微信小程序 移動開發

一、寫作背景

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

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

作為開發者,我們當然希望可以讓工作更簡單,更愉快,也希望改善我們的開發方式。所以希望能夠更了解微信小程序框架,減少不必要的試錯,于是有了一次對小程序框架的 debug 之旅。(基礎庫 1.9.93)

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

本文的目的是希望把我看到的分享給對小程序感興趣或者正在開發小程序的讀者,主要解答“框架對傳入的對象等到底做了什么”。

二、從啟動流程一窺小程序框架細節

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

在開發中工具命令行使用 document.head 可以查看到小程序的啟動流程大致如下: 以小節的方式分別介紹這些流程,小程序是如何處理的(小節編號與圖中編號相同)。

1、初始化全局變量

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

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

2、加載框架(WAService.js)

使用工具對 WAService.js 進行格式化后進行 debug。可以發現小程序框架大致由: WeixinJSBridge 、 NativeBuffer 、 wxConsole 、 WeixinWorker 、 JavaScript 兼容 (這部分為猜測)、 Reporter 、 wx 、 exparser 、 __virtualDOM__ 、 __appServiceEngine__ 幾部分組成。

其中除了 wx 和 WeixinJSBridge 這兩個基礎 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。

3、業務代碼的加載

在小程序中,開發者的 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 })</pre>

這里的 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 代碼的處理流程。

4、加載 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。

5、加載自定義組件代碼以及注冊自定義組件

自定義組件在 app.js 之后被加載,小程序會在這個過程中加載完所有的自定義組件(分包中自定義組件沒有有測試過),并且是加載完成后自動注冊,只有注冊完成后才會加載下一個自定義組件的代碼。

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

圖中介紹了框架如何對傳入 Component 方法的對象的處理,其后面還有很多深入的對于組件實例化的步驟沒有在圖中表示出來,具體可以在文章最后的附件中查看。

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

6、加載頁面代碼和注冊頁面

加載頁面代碼的處理流程和加載自定義組件一樣,都是加載完成后先注冊頁面,然后才會加載下一個頁面。

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

Page 方法會根據是否使用自定義組件做不同的處理。使用自定義組件的 page 對象會被處理為和自定義組件的結構,并在頁面實例化時使用不同的處理流程進行實例化。當然對于開發而言沒任何不同。

從圖中可以發現 Page 傳入的(生命周期)代碼并不會在這里被執行,可以通過下面小節了解 Page 實例化的詳細過程。

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

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

在小程序中切換頁面或打開頁面時會觸發 onAppRoute 事件,小程序框架通過 wx.onAppRoute 注冊頁面切換的處理程序,在所有程序就緒后,以 entryPagePath 作為入口使用 appLaunch 的方式進入頁面。

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

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

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

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

8、關于 setData

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

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

三、寫在最后

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

通過這一次 debug ,也給我引入了新的問題,還希望能夠有更多的討論:

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

一份在調試過程中的筆記 小程序框架不完全分析.xmind ,如果看官有興趣可以下載看看。當然最后對于框架中已有的能力,還是分層希望微信可以開放更多穩定的接口,并在文檔中告知開發者,讓開發變得簡單一些。

 

來自:https://tech.youzan.com/weapp-booting/

 

 本文由用戶 ghuk2926 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!