嘿,各位做爲小程序的開發者,有認真看太小程序的page-frame.html嗎!想要告訴你們,頗有必要的喔!經過這段html,管中窺豹,能夠領悟到小程序代碼的加載流程,還蠻有收穫的,故在此記錄一番~html
在此以前,先強烈安利有贊團隊的從源碼看微信小程序啓動過程 ,超良心超有料,我看了三遍都麼有徹底領悟得道(¯∀¯٥)…有想深刻了解的同窗能夠研讀一下這篇文章~node
其實我以爲從模塊引用和頁面展現來講,小程序開發頗像開發單頁應用。雖然開發小程序時會分不一樣的頁面來編寫,但實際呈現出來的是一張大頁面,也就是page-frame.html。webpack
在開發者工具的控制檯輸入document,便可以清晰獲得整段html。以下圖:es6
整份html的各個部分在這裏就再也不贅言啦,基本是初始化全局變量>>加載框架WAService.js>>加載業務代碼的過程。而我最感興趣的是業務代碼的加載。web
顯而易見,每一份js代碼實際經過script的方式加載。那浮如今我腦中的第一個問題天然是,小程序是如何實現js模塊的引用的呢?json
以一個簡單的demo爲例,pages/home/index.js與pages/home/common.js,前者引入後者。小程序
pages/home/index.js:微信小程序
import boom from './common';
Page({});
複製代碼
pages/home/common.js:緩存
export default 'boom';
複製代碼
經小程序處理後,結果以下>>>>bash
pages/home/index.js:
define("pages/home/index.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,URL,DOMParser,upload,preview,build,showDecryptedInfo,syncMessage,checkProxy,showSystemInfo,openVendor,openToolsLog,showRequestInfo,help,showDebugInfoTable,closeDebug,showDebugInfo,__global,WeixinJSBridge){ 'use strict';
var _common = require('./common');
var _common2 = _interopRequireDefault(_common);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
Page({});
});
複製代碼
pages/home/common.js:
define("pages/home/common.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,URL,DOMParser,upload,preview,build,showDecryptedInfo,syncMessage,checkProxy,showSystemInfo,openVendor,openToolsLog,showRequestInfo,help,showDebugInfoTable,closeDebug,showDebugInfo,__global,WeixinJSBridge){ 'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = 'boom';
});
複製代碼
可見小程序對這份代碼作了es6>es5的轉換(固然前提是要在項目設置中打開容許),並將其處理成類amd的模式。那麼小程序是如何處理define及require的呢?
由於代碼在執行時全局變量define和局域變量require已存在,因此咱們能夠在代碼中打印它們出來,並順勢找到這兩段代碼定義的位置。它們都位於WAService.js。
define:
代碼代表,全部js都以路徑-模塊的鍵值對的方式存在了某全局變量c裏。模塊有兩個屬性,status和factory。其中status暫爲1,表明模塊還沒有加載;factory即被閉包在函數中的模塊內容。
require:
能夠看到require可分爲三步:
從全局變量c中讀出define裏定義的模塊對象(即當前的t)。
如status爲1,則初始化模塊;不然直接返回t.exports。
加載模塊
①將status的值置爲2,表示該模塊已被初始化。
②調用t.factory(即r(...)這一步)加載模塊,並將結果賦給t.exports。
仔細看看r()這一步,有傳入三個入參,分別對應define中的require, module及exports。第一個入參,i(e),它到底執行了什麼呢?答案就在require的上面一個代碼塊中(line 37005)。它對模塊的存在性作了校驗,若讀不到模塊,會拋出cant find module xxx的異常。它的返回值就是require函數。
③當一段js模塊加載完成後,其模塊對象會變成
{
status: 2,
factory: t,
exports: t裏導出的值
}
複製代碼
因此,這其實就是典型的模塊加載思路,我以爲也與webpack打包模塊的處理方式十分類似,保證每份js模塊只在被第一次引用時被初始化一次,並將結果緩存全局以供其餘模塊往後使用。
瞭解完小程序的js模塊加載機制,再將目光移回page-frame.html~
不難發現小程序經過script標籤引入全部js,並按照其餘js > 頁面js >app.js的順序。以後會當即執行require('app.js')和require('各頁面.js')來註冊app和頁面。
不過,你們有好奇過這幾塊script標籤是如何生成的嗎?它們是一開始就存在嗎?只要咱們接着往下看,注意以後的script標籤,能夠留意到這樣一段代碼:
重要的部分我大概圈了出來~從中發現,其實上段代碼中小程序引入的全部js script,都是經過這段終極script製造出來的喔。它的行動過程大概分爲兩步:
<script src="pages/home/index"></script>
`(小小留意一下順序問題,app.js最後才被add script)require('app.js')和require('各頁面.js')
的代碼塊來執行對應板塊的代碼!~因此這就是爲何app.js和各page.js在引入完成後,就會自動執行的緣由~
組件是小程序晚些時候提供的能力,咱們也能夠將其視爲一種特殊的頁面。以下圖,可見組件加載方式也是與頁面同樣同樣的~(固然實現不同)。
好的,目前爲止,普通頁面的加載過程應該已一目瞭然~那天然引入下一個問題,小程序是如何處理分包代碼加載的呢,大機率也是在前往分包頁面後,將分包代碼append script的把?
那麼頁面結構保持不變,不過在app.json中,將pages/pageA劃入subPackages的分包範疇。再次刷新代碼:
在home頁時,能夠看到document的結構中確實只有app.js和home.js,沒有引入pageA.js:
而後navigateTo pageA頁,再次檢閱document結構,發現符合猜測:
原代碼的基礎上多了pageA的script,以及又一大坨代碼段,粗略看起來是處理wxml的nodes外加與處理加載相關的其餘代碼…不過以前我覺得也會顯性經過appendrequire('pages/pagesA/index.js')
代碼塊的方式來執行pageA.js頁面,如今看來並無。
①page-frame.html體現了小程序處理頁面的顯性流程。
②各js模塊經過script標籤引入,經過類AMD方式進行加載執行。
③app.js和page.js以及component.js會在引入後當即執行。
emmm…雖然知道了這些我很開心 可是貌似對開發也麼有什麼太大的幫助>.< 不過總比兩眼一抹黑的狀態要好啦,而且想作小程序打包時,知道這些會有些許裨益~!Happy wxa coding!