一.微信小程式是啥
本質其實就是(混合)的app 介於web app與native 原生app之間,具備豐富的呼叫手機各種功能的介面,同時又具備靈活性,跨平臺
掃碼檢視小程式:
1. 執行環境差異
微信小程式執行在三端:iOS、Android 和 用於除錯的開發者工具。
三端的指令碼執行環境以及用於渲染非原生元件的環境是各不相同的:
- 在 iOS 上,小程式的 javascript 程式碼是執行在 JavaScriptCore 中,是由 WKWebView 來渲染的,環境有 iOS8、iOS9、iOS10
- 在 Android 上,小程式的 javascript 程式碼是通過 X5 JSCore來解析,是由 X5 基於 Mobile Chrome 53/57 核心來渲染的
- 在 開發工具上, 小程式的 javascript 程式碼是執行在 nwjs 中,是由 Chrome Webview 來渲染的 來自官方文件說明
2.小程式目錄結
project
├── pages
| ├── index
| | ├── index.json index 頁面配置
| | ├── index.js index 頁面邏輯
| | ├── index.wxml index 頁面結構
| | └── index.wxss index 頁面樣式表
| └── log
| ├── log.json log 頁面配置
| ├── log.wxml log 頁面邏輯
| ├── log.js log 頁面結構
| └── log.wxss log 頁面樣式表
├── app.js 小程式邏輯
├── app.json 小程式公共設定
└── app.wxss 小程式公共樣式表複製程式碼
3.為什麼小程式比較快
二、小程式架構
微信小程式的框架包含兩部分View檢視層(可能存在多個)、App Service邏輯層(一個),View層用來渲染頁面結構,AppService層用來邏輯處理、資料請求、介面呼叫,它們在兩個執行緒裡執行。
檢視層使用WebView渲染,邏輯層使用JSCore執行。
檢視層和邏輯層通過系統層的WeixinJsBridage進行通訊,邏輯層把資料變化通知到檢視層,觸發檢視層頁面更新,檢視層把觸發的事件通知到邏輯層進行業務處理。
重點講一下wxs :
由於view 與 App Service是不同執行緒,之前是傳遞資料,當遇到一些資料需要在view中處理時,就可以用wxs來處理,如下所示定義<wxs module="tools">,使用說明
index.js
//獲取應用例項
const app = getApp()
Page({
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false
},
//事件處理函式
bindViewTap: function() {
},
onLoad: function() {
}
})
複製程式碼
<!--index.wxml-->
<view class="container">
<view class="usermotto">
<text class="user-motto">{{tools.bar(motto)}}</text>
<text class="user-motto">{{tools.foo}}</text>
</view>
<wxs module="tools">
var foo = "'hello world' from comm.wxs";
var bar = function(d) {
return '啥子玩意'+d;
}
module.exports = {
foo: foo,
bar: bar
};
</wxs>
</view>
複製程式碼
三、小程式啟動載入
執行機制
小程式啟動會有兩種情況,一種是「冷啟動」,一種是「熱啟動」。 假如使用者已經開啟過某小程式,然後在一定時間內再次開啟該小程式,此時無需重新啟動,只需將後臺態的小程式切換到前臺,這個過程就是熱啟動;冷啟動指的是使用者首次開啟或小程式被微信主動銷燬後再次開啟的情況,此時小程式需要重新載入啟動。
更新機制
小程式冷啟動時如果發現有新版本,將會非同步下載新版本的程式碼包,並同時用客戶端本地的包進行啟動,即新版本的小程式需要等下一次冷啟動才會應用上。 如果需要馬上應用最新版本,可以使用 wx.getUpdateManager API 進行處理。
執行機制
- 小程式沒有重啟的概念
- 當小程式進入後臺,客戶端會維持一段時間的執行狀態,超過一定時間後(目前是5分鐘)會被微信主動銷燬
- 當短時間內(5s)連續收到兩次以上收到系統記憶體告警,會進行小程式的銷燬
四、View (頁面檢視)
檢視層由 WXML 與 WXSS 編寫,由元件來進行展示。
將邏輯層的資料反應成檢視,同時將檢視層的事件傳送給邏輯層。
1、View - WXML
wxml編譯器:wcc 把wxml檔案 轉為 js 執行方式:wcc index.wxml
2、View - WXSS
- WXSS(WeiXin Style Sheets)
- wxss編譯器:wcsc 把wxss檔案轉化為 js 執行方式: wcsc index.wxss
3、View - Component
小程式的元件基於Web Component標準
使用Polymer框架實現Web Component
4、View - Native Component
- 目前Native實現的元件有 <canvas/> <video/> <map/> <textarea/>
- Native元件層在WebView層之上
五、WebView預載入
每次小程式進入除了當前頁面,Native預先額外載入一個WebView
當開啟指定頁面時,用預設資料直接渲染,請求資料回來時區域性更新
返回顯示歷史View
退出小程式,View狀態不銷燬
六、App Service(邏輯層)
邏輯層將資料進行處理後傳送給檢視層,同時接受檢視層的事件反饋
1、App( ) 小程式的入口;Page( ) 頁面的入口
3、提供豐富的 API,如微信使用者資料,掃一掃,支付等微信特有能力。
4、每個頁面有獨立的作用域,並提供模組化能力。
5、資料繫結、事件分發、生命週期管理、路由管理
執行環境
IOS - JSCore
Android - X5 JS解析器
DevTool - nwjs Chrome 核心
1、App Service - Binding
- 資料繫結使用 Mustache 語法(雙大括號)將變數包起來,動態資料均來自對應 Page 的 data,可以通過setData方法修改資料。
- 事件繫結的寫法同元件的屬性,以 key、value 的形式,key 以bind或catch開頭,然後跟上事件的型別,如bindtap, catchtouchstart,value 是一個字串,需要在對應的 Page 中定義同名的函式。
2、App Service - Life Cylce
3、App Service - API
API通過WeixinJSBridge和Native 進行通訊
4、App Service - Router
- navigateTo(OBJECT)
保留當前頁面,跳轉到應用內的某個頁面,使用navigateBack可以返回到原頁面。頁面路徑只能是五層
- redirectTo(OBJECT)
關閉當前頁面,跳轉到應用內的某個頁面。
- navigateBack(OBJECT)
關閉當前頁面,返回上一頁面或多級頁面。可通過 getCurrentPages()) 獲取當前的頁面棧,決定需要返回幾層。
五、小程式開發經驗
1、小程式存在的問題
小程式仍然使用WebView渲染,並非原生渲染
需要獨立開發,不能在非微信環境執行 。
開發者不可以擴充套件新元件。
依賴瀏覽器環境的js庫不能使用,因為是JSCore執行的,沒有window、document物件。
WXSS中無法使用本地(圖片、字型等)。
WXSS轉化成js 而不是css。
WXSS不支援級聯選擇器。
小程式無法開啟頁面,無法拉起APP。
2、小程式的優點
提前新建WebView,準備新頁面渲染。
View層和邏輯層分離,通過資料驅動,不直接操作DOM。
使用Virtual DOM,進行區域性更新。
全部使用https,確保傳輸中安全。
加入rpx單位,隔離裝置尺寸,方便開發。
rpx(responsive pixel):
可以根據螢幕寬度進行自適應。規定螢幕寬為750rpx。
如在 iPhone6 上,螢幕寬度為375px,共有750個物理畫素,則750rpx = 375px = 750物理畫素,
1rpx = 0.5px = 1物理畫素。
裝置 rpx換算px (螢幕寬度/750) px換算rpx (750/螢幕寬度)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6Plus 1rpx = 0.552px 1px = 1.81rpx複製程式碼
七、程式碼執行
執行時,外面包裹define,程式碼作為回到,當呼叫回撥時,只傳入前面三個值,由於後面的變數都是區域性定義的變數,就遮蔽了(window,document等這些變數.
其中O就是上面define('app.js',callback),的回撥,回撥值傳入了三個引數,遮蔽了其他屬性
八、優化建議(官方建議)
setData 工作原理
小程式的檢視層目前使用 WebView 作為渲染載體,而邏輯層是由獨立的 JavascriptCore 作為執行環境。在架構上,WebView 和 JavascriptCore 都是獨立的模組,並不具備資料直接共享的通道。當前,檢視層和邏輯層的資料傳輸,實際上通過兩邊提供的 evaluateJavascript
所實現。即使用者傳輸的資料,需要將其轉換為字串形式傳遞,同時把轉換後的資料內容拼接成一份 JS 指令碼,再通過執行 JS 指令碼的形式傳遞到兩邊獨立環境。
而 evaluateJavascript
的執行會受很多方面的影響,資料到達檢視層並不是實時的。
常見的 setData 操作錯誤
1. 頻繁的去 setData
在我們分析過的一些案例裡,部分小程式會非常頻繁(毫秒級)的去setData
,其導致了兩個後果:
- Android 下使用者在滑動時會感覺到卡頓,操作反饋延遲嚴重,因為 JS 執行緒一直在編譯執行渲染,未能及時將使用者操作事件傳遞到邏輯層,邏輯層亦無法及時將操作處理結果及時傳遞到檢視層;
- 渲染有出現延時,由於 WebView 的 JS 執行緒一直處於忙碌狀態,邏輯層到頁面層的通訊耗時上升,檢視層收到的資料訊息時距離發出時間已經過去了幾百毫秒,渲染的結果並不實時;
2. 每次 setData 都傳遞大量新資料
由setData
的底層實現可知,我們的資料傳輸實際是一次 evaluateJavascript
指令碼過程,當資料量過大時會增加指令碼的編譯執行時間,佔用 WebView JS 執行緒,
3. 後臺態頁面進行 setData
當頁面進入後臺態(使用者不可見),不應該繼續去進行setData
,後臺態頁面的渲染使用者是無法感受的,另外後臺態頁面去setData
也會搶佔前臺頁面的執行。
圖片資源
- 目前圖片資源的主要效能問題在於大圖片和長列表圖片上,這兩種情況都有可能導致 iOS 客戶端記憶體佔用上升,從而觸發系統回收小程式頁面。
- 在 iOS 上,小程式的頁面是由多個 WKWebView 組成的,在系統記憶體緊張時,會回收掉一部分 WKWebView。從過去我們分析的案例來看,大圖片和長列表圖片的使用會引起 WKWebView 的回收。
程式碼包大小的優化
開發者在實現業務邏輯同時也有必要儘量減少程式碼包的大小,因為程式碼包大小直接影響到下載速度,從而影響使用者的首次開啟體驗。除了程式碼自身的重構優化外,還可以從這兩方面著手優化程式碼大小:
- 分包載入
對小程式進行分包,可以優化小程式首次啟動的下載時間 - 清理沒有使用到的程式碼和資源
目前小程式打包是會將工程下所有檔案都打入程式碼包內,也就是說,這些沒有被實際使用到的庫檔案和資源也會被打入到程式碼包裡,從而影響到整體程式碼包的大小。
預先載入資料
原理
小程式在啟動時,會直接載入所有頁面邏輯程式碼進記憶體,即便 page2 可能都不會被使用。在 page1 跳轉至 page2 時,page1 的邏輯程式碼 Javascript 資料也不會從記憶體中消失。page2 甚至可以直接訪問 page1 中的資料。
小程式的這種機制差異正好可以更好的實現預載入。通常情況下,我們習慣將資料拉取寫在 onLoad 事件中。但是小程式的 page1 跳轉到 page2,到 page2 的 onLoad 是存在一個 300ms ~ 400ms 的延時的。如下圖:
因為小程式的特性,完全可以在 page1 中預先拿取資料,然後在 page2 中直接使用資料,這樣就可以避開 redirecting 的 300ms ~ 400ms了。如下圖:
渲染view執行緒和AppServcie是相互獨立的,對於AppServcie中js執行不會阻塞view的渲染
官方的示例也是採用這種方式: 先App中請求資料,在index.js使用資料
具體可以參考這篇文件(https://mp.weixin.qq.com/s/EvzQoSwWYUmShtI_MkrFuQ)
有錯誤的地方歡迎指出,共同進步
最後遇到相關問題開發者社群搜尋問題
參考內容:
- 微信小程式架構解析
- 微信小程式weapp的底層實現原理
- Web Components 是個什麼樣的東西
- 微信小程式開發深入解讀
- 雲+社群技術沙龍--WeGeek微信小程式敏捷開發實戰
- 理解微信小程式技術架構
- 微信小程式架構分析 (上)