自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

小程序底層架構(gòu)剖析

開發(fā) 前端
我們先拋出一個(gè)問(wèn)題,在沒(méi)有小程序的時(shí)候,企業(yè)們都在微信里怎么運(yùn)營(yíng)? 答案就是小程序的“前身”-公眾號(hào),企業(yè)們普遍會(huì)把H5網(wǎng)站放在公眾號(hào)作為流量轉(zhuǎn)換的入口。但是h5確實(shí)讓公眾號(hào)遇到了一些問(wèn)題。

當(dāng)我們前端切圖崽網(wǎng)上沖浪的時(shí)候,會(huì)發(fā)現(xiàn)有很多技術(shù)文章都在分析vue框架,react框架,顯少有分析小程序框架的。那今天就通過(guò)這篇短小精悍的文章帶大家了解一下微信小程序的底層架構(gòu)。(如無(wú)特殊說(shuō)明,下文中提到的小程序都是微信小程序)

小程序的由來(lái)

我們先拋出一個(gè)問(wèn)題,在沒(méi)有小程序的時(shí)候,企業(yè)們都在微信里怎么運(yùn)營(yíng)? 答案就是小程序的“前身”-公眾號(hào),企業(yè)們普遍會(huì)把H5網(wǎng)站放在公眾號(hào)作為流量轉(zhuǎn)換的入口。但是h5確實(shí)讓公眾號(hào)遇到了一些問(wèn)題。

首先就是白屏過(guò)程,對(duì)于一些復(fù)雜頁(yè)面,受限于設(shè)備性能和網(wǎng)絡(luò)速度,白屏?xí)用黠@;再就是缺少操作反饋,比如頁(yè)面切換生硬以及點(diǎn)擊所帶來(lái)的遲滯感等等;

微信團(tuán)隊(duì)內(nèi)部通過(guò)JS-SDK以及后來(lái)的增強(qiáng)JS-SDK已經(jīng)能夠解決一些問(wèn)題,但是對(duì)于上述問(wèn)題是JS-SDK所處理不了的,急需一個(gè)全新的系統(tǒng)來(lái)完成,它需要具備以下能力:

  • 快速加載
  • 更強(qiáng)大的能力
  • 原生的體驗(yàn)
  • 易用且安全的微信數(shù)據(jù)開放
  • 高效,簡(jiǎn)單的開發(fā)

于是,小程序誕生了。

雙線程架構(gòu)

此處點(diǎn)題一下,本文我們討論的是小程序的底層架構(gòu),其實(shí),雙線程架構(gòu)就是小程序的核心。

圖片

那為什么要設(shè)計(jì)成雙線程架構(gòu)呢?首先我們來(lái)回顧一下瀏覽器的線程模型,瀏覽器是一個(gè)單線程架構(gòu),主要原因是js允許訪問(wèn)操作DOM,因此js線程和渲染線程只能互斥運(yùn)行。

那小程序又是如何做到雙線程的呢,根本原因就是微信小程序禁止js操作DOM。

使用雙線程架構(gòu)的優(yōu)勢(shì)一目了然:

  • 提高用戶體驗(yàn)(ui和邏輯分離,避免頁(yè)面長(zhǎng)時(shí)間阻塞和卡頓)
  • 優(yōu)化應(yīng)用性能(運(yùn)行在不同的線程中,可以同時(shí)渲染或者計(jì)算)
  • 開發(fā)效率更高(解耦和松散耦合)

接下來(lái)就帶大家了解一下渲染層以及邏輯層的設(shè)計(jì)思路。

設(shè)計(jì)思路-渲染層

標(biāo)簽實(shí)現(xiàn)

小程序使用的是Exparser組件模型,Exparser組件模型與Web Components中的shadow DOM高度相似,微信為什么使用自定義組件框架,而不使用Web Components呢?主要還是出于安全考慮,并且方便管控。既然Exparser組件框架與shadow DOM高度相似,那么我們首先來(lái)了解一下shadow DOM。

shadow DOM: Web Components的一個(gè)重要屬性是封裝-可以將標(biāo)記結(jié)構(gòu)、樣式和行為隱藏起來(lái),并與頁(yè)面上的其他代碼相隔離,保證不同的部分不會(huì)混在一起,可使代碼更加干凈、整潔。其中,shadow DOM接口是關(guān)鍵所在,它可以將一個(gè)隱藏的,獨(dú)立的DOM附加到一個(gè)元素上。

圖片

shadow DOM允許將隱藏的DOM樹附加到常規(guī)的DOM樹中-它以shadow root節(jié)點(diǎn)為起始根節(jié)點(diǎn),在根節(jié)點(diǎn)的下方,可以是任意元素,和普通的DOM一樣。

以上解釋來(lái)源于MDN,其實(shí)shadow DOM并不神秘,像我們非常熟悉的video標(biāo)簽本質(zhì)上就是用shadow DOM實(shí)現(xiàn)的。我們先打開chrome瀏覽器設(shè)置中的“打開用戶代理shadow DOM”,然后再點(diǎn)擊video標(biāo)簽就能看到。

圖片

創(chuàng)建shadow DOM也非常簡(jiǎn)單,直接使用attachShadow方法就可以創(chuàng)建。

var shadow = Element.attachShadow({ mode: 'closed'})

Exparser組件模型:Exparser組件模型參考了shadow DOM并進(jìn)行了一些修改,像事件系統(tǒng)就是完全復(fù)刻的,slot插槽,屬性傳遞等都基本一致。但同時(shí)它又具有一些特點(diǎn):

  • 基于shadow DOM模型:模型上與Web Components的shadow DOM高度相似,但不依賴瀏覽器的原生支持,也沒(méi)有其他依賴庫(kù);實(shí)現(xiàn)時(shí),還針對(duì)性地增加了其他API以支持小程序組件編程;
  • 可在純JS環(huán)境中運(yùn)行:這意味著邏輯層也具有一定的組件樹組織能力;
  • 高效輕量:性能表現(xiàn)好,在組件實(shí)例極多的環(huán)境下表現(xiàn)尤其優(yōu)異,同時(shí)代碼尺寸也較?。?/li>

WXML編譯

了解了小程序的組件系統(tǒng)之后,接下來(lái)看看WXML的編譯過(guò)程。小程序中的DOM編譯流程與vue類似,也會(huì)先將代碼字符串編譯為虛擬DOM,小程序中的虛擬DOM結(jié)構(gòu)如下:WXML最終會(huì)被編譯為JS文件,然后插入到渲染層的script標(biāo)簽中。

圖片

圖片


WXSS動(dòng)態(tài)適配

WXSS是小程序中使用的樣式語(yǔ)言,WXSS具有CSS的大部分特性,同時(shí)它對(duì)CSS進(jìn)行了擴(kuò)充以及修改。

小程序中使用的尺寸單位為rpx(Responsive px),不同于h5中對(duì)于px的處理,需要使用postcss進(jìn)行統(tǒng)一的轉(zhuǎn)換,小程序底層已經(jīng)為開發(fā)者做好了這層轉(zhuǎn)換,那具體它是怎么做到的呢?

圖片

我們看它的這段源碼,其實(shí)它與阿里的flexible.js方案是類似的,不同的是它做了一個(gè)精度收攏的優(yōu)化,主要是為了解決1px的問(wèn)題。

WXSS同樣會(huì)經(jīng)過(guò)編譯,最終的編譯產(chǎn)物為wxss.js,不同于WXML通過(guò)script標(biāo)簽的形式插入到渲染層,wxss.js則是通過(guò)eval的方式注入到渲染層代碼中。

渲染層webview

全局變量: 渲染線程中存在著以下全局變量。

  • webviewId:webview的唯一標(biāo)識(shí),當(dāng)用戶打開一個(gè)小程序頁(yè)面的時(shí)候,相當(dāng)于打開了一個(gè)webview,不同的webview用webviewid來(lái)區(qū)分;
  • wxAppCode:整個(gè)頁(yè)面的json wxss wxml編譯之后都存儲(chǔ)在這里;
  • Vd_version_info:版本信息;
  • ./dev/wxconfig.js:小程序默認(rèn)總配置項(xiàng),包括用戶自定義與系統(tǒng)默認(rèn)的整合結(jié)果。在控制臺(tái)輸入__wxConfig可以看出打印結(jié)果;
  • ./dev/devtoolsconfig.js:小程序開發(fā)者配置,包括navigationBarHeight,標(biāo)題欄的高度,狀態(tài)欄高度,等等,控制臺(tái)輸入__devtoolsConfig可以看到其對(duì)應(yīng)的信息;
  • ./dev/deviceinfo.js:設(shè)備信息,包含尺寸/像素點(diǎn)pixelRatio;
  • ./dev/jsdebug.js:debug工具;
  • ./dev/WAWebview.js:渲染層底層基礎(chǔ)庫(kù);
  • ./dev/hls.js:優(yōu)秀的視頻流處理工具;
  • ./dev/WARemoteDebug.js:底層基礎(chǔ)庫(kù)調(diào)試工具;

那小程序是如何快速啟動(dòng)一個(gè)webview的呢?

我們?cè)诖蜷_pages/index/index視圖頁(yè)面時(shí),發(fā)現(xiàn)DOM中多加載了一個(gè)__pageframe__/pageframe.html的視圖層。這個(gè)視圖層的作用正是小程序提前為一個(gè)新的頁(yè)面層準(zhǔn)備的。小程序每個(gè)視圖層頁(yè)面內(nèi)容都是通過(guò)pageframe.html模板來(lái)生成的,包括小程序啟動(dòng)的首頁(yè)。

下面來(lái)看看小程序?yàn)榭焖俅蜷_小程序頁(yè)面做的技術(shù)優(yōu)化:

  • 首頁(yè)啟動(dòng)時(shí),即第一次通過(guò)pageframe.html生成內(nèi)容后,后臺(tái)服務(wù)會(huì)緩存pageframe.html模板首次生成的html內(nèi)容;
  • 非首次新打開頁(yè)面時(shí),頁(yè)面請(qǐng)求的pageframe.html內(nèi)容直接走后臺(tái)緩存;
  • 非首次新打開頁(yè)面時(shí),pageframe.html頁(yè)面引入的外鏈js資源走本地緩存; 這樣在后續(xù)新打開頁(yè)面時(shí),都會(huì)走緩存的pageframe的內(nèi)容,避免重復(fù)生成,快速打開一個(gè)新頁(yè)面。

視圖層打開新頁(yè)面的流程

在創(chuàng)建每個(gè)視圖層頁(yè)面的webview時(shí),都會(huì)為其綁定了onLoadCommit事件(它會(huì)在頁(yè)面加載完成后觸發(fā),包含當(dāng)前文檔的導(dǎo)航和副框架的文檔加載)。初始時(shí)webview的src會(huì)被指定為空頁(yè)面地址http://127.0.0.1:${global.proxyPort}/aboutblank?${c},其中c為對(duì)應(yīng)webview的id。webview從空頁(yè)面到具體頁(yè)面視圖的過(guò)程如下:

  1. 空頁(yè)面地址webview加載完畢后執(zhí)行事件中的reload方法,即設(shè)置webview的src為pageframe地址;
  2. 加載完成后,設(shè)置其src為pageframe.html, 新的src內(nèi)容加載完成后再次觸發(fā)onLoadCommit事件但根據(jù)條件不會(huì)執(zhí)行reload方法;
  3. pageframe.html頁(yè)面在dom ready之后觸發(fā)注入并執(zhí)行具體頁(yè)面相關(guān)的代碼,此時(shí)通過(guò)history.pushState方法修改webview的src但是webview并不會(huì)發(fā)送頁(yè)面請(qǐng)求;

設(shè)計(jì)思路-邏輯層

接下來(lái)我們看看小程序在邏輯層都做了哪些事情。

邏輯層與視圖層通信

在小程序中,邏輯層只有一個(gè),但是渲染層有多個(gè),渲染層和邏輯層之間是通過(guò)微信客戶端進(jìn)行橋接通信的。那具體是怎么實(shí)現(xiàn)的呢?其實(shí)它使用的就是WeixinJSBridge通信機(jī)制。

在小程序執(zhí)行的過(guò)程中,微信客戶端分別向渲染層和邏輯層注入WeixinJSBridge,WeixinJSBridge主要提供了以下幾個(gè)方法:

  • invoke:調(diào)用native API;
  • invokeCallbackHandler:Native 傳遞 invoke 方法回調(diào)結(jié)果;
  • publish:渲染層用來(lái)向邏輯業(yè)務(wù)層發(fā)送消息,也就是說(shuō)要調(diào)用邏輯層的事件方法;
  • subscribe:訂閱邏輯層消息;
  • subscribeHandler:視圖層和邏輯層消息訂閱轉(zhuǎn)發(fā);
  • setCustomPublishHandler:自定義消息轉(zhuǎn)發(fā);

渲染層如何向邏輯層通信?

圖片

圖片

渲染層向邏輯層通信的方式就是采用事件系統(tǒng),以上就是完整的事件系統(tǒng)流程。

開發(fā)者在DOM上通過(guò)@click綁定事件,WXML文件被編譯的時(shí)候,會(huì)通過(guò)$gwx函數(shù)生成虛擬DOM,然后小程序執(zhí)行的時(shí)候渲染層底層基礎(chǔ)庫(kù)會(huì)對(duì)虛擬DOM進(jìn)行解析,事件綁定最終會(huì)以attr屬性的形式生成到虛擬DOM中,所以底層基礎(chǔ)庫(kù)通過(guò)applyPropeties解析事件并通過(guò)addEventListener綁定到相應(yīng)DOM并聲明回調(diào)。

用戶點(diǎn)擊相應(yīng)DOM時(shí),Exparser組件系統(tǒng)接收到這個(gè)事件,然后開始執(zhí)行回調(diào)?;卣{(diào)函數(shù)在邏輯層,事件的觸發(fā)在渲染層,此時(shí),小程序會(huì)通過(guò)setData發(fā)送數(shù)據(jù)到邏輯層,這個(gè)時(shí)候WeixinJSBridge就派上用場(chǎng)了,渲染層調(diào)用publish方法發(fā)送數(shù)據(jù),邏輯層通過(guò)registercallback進(jìn)行監(jiān)聽,并執(zhí)行相應(yīng)的回調(diào)。此時(shí),渲染層到邏輯層的通信流程結(jié)束。

那邏輯層又是如何將改變后的數(shù)據(jù)回傳給渲染層的呢?邏輯層改變數(shù)據(jù)之后,同樣是觸發(fā)setData方法,然后渲染層通過(guò)subscribe進(jìn)行監(jiān)聽,從eventname和觸發(fā)事件時(shí)候記錄的回調(diào)函數(shù)來(lái)判斷是哪個(gè)事件被觸發(fā)了,從而獲取動(dòng)態(tài)數(shù)據(jù)。

第三方小程序框架

WXML,WXSS都是小程序的原生開發(fā)語(yǔ)言,使用原生語(yǔ)言開發(fā)還是存在諸多限制,尤其是17年小程序剛推出那會(huì)。因此,第三方小程序框架應(yīng)運(yùn)而生。第三方框架可以分為三大類。

第一類是預(yù)編譯框架,預(yù)編譯框架就是在執(zhí)行前就進(jìn)行編譯。像我司在17年開發(fā)“轉(zhuǎn)轉(zhuǎn)二手交易網(wǎng)”的時(shí)候使用的wepy框架就屬于預(yù)編譯框架。預(yù)編譯框架也有一些顯而易見(jiàn)的缺點(diǎn),這類預(yù)編譯框架要么是類vue,要么是類React,如果后期vue或者React再出一些新特性的話,預(yù)編譯框架就要進(jìn)行擴(kuò)展編寫;還有一些兼容問(wèn)題,對(duì)于小程序本身不支持的一些屬性,預(yù)編譯框架需要進(jìn)行兼容;

第二類是半編譯半運(yùn)行框架,像美團(tuán)的mpvue就是此類框架,半編譯指的是vue的template需要單獨(dú)編譯為wxml,半運(yùn)行講的是vue整體的特性都會(huì)在邏輯層中運(yùn)行。為了符合小程序的渲染框架,修改了vue的框架;

第三類是運(yùn)行時(shí)框架,像Remax就是運(yùn)行時(shí)框架,它可以使開發(fā)者使用完整的React語(yǔ)法來(lái)開發(fā)小程序。因?yàn)樾〕绦蚩蚣鼙旧硎遣恢С謏s直接操作DOM的,那Remax框架是如何解決這個(gè)問(wèn)題的呢?其實(shí)它自己復(fù)刻了一套操作DOM的API,例如appendChild,innterHtml等,但是它真正操作的并不是dom,而是data中的數(shù)據(jù)結(jié)構(gòu)。從而達(dá)到了操作DOM的目的。使得自己真正成了一個(gè)運(yùn)行時(shí)框架;

結(jié)語(yǔ)

介紹到這里,小程序的底層框架原理基本已經(jīng)介紹完了,想跟大家分享的是,小程序確實(shí)和h5非常類似,其實(shí)它相當(dāng)于一個(gè)借助了native強(qiáng)大功能的加強(qiáng)版h5,小程序并不神秘,除了微信小程序之外,現(xiàn)在各大超級(jí)APP都已經(jīng)推出了自己的小程序,原理應(yīng)該都大差不差。

本篇文章其實(shí)相當(dāng)于一個(gè)學(xué)習(xí)筆記,作者本身非常想搞清楚微信小程序的架構(gòu),但是微信小程序并沒(méi)有開源,某次偶然的機(jī)會(huì)逛掘金的時(shí)候看到這篇小冊(cè),就整個(gè)學(xué)習(xí)了一下,在此感謝原作者!

參考

https://juejin.cn/book/6982013809212784676?enter_from=course_center&utm_source=course_center

責(zé)任編輯:武曉燕 來(lái)源: 大轉(zhuǎn)轉(zhuǎn)FE
相關(guān)推薦

2022-12-14 15:34:33

架構(gòu)開發(fā)雙線程

2022-10-28 10:23:27

Java多線程底層

2022-11-11 10:48:55

AQS源碼架構(gòu)

2024-05-16 10:30:54

HiveSQL任務(wù)

2021-07-20 10:26:53

源碼底層ArrayList

2017-06-09 10:40:00

微信小程序架構(gòu)分析

2017-06-09 10:06:54

微信小程序架構(gòu)分析

2017-06-09 12:58:20

微信小程序架構(gòu)分析

2021-06-10 10:51:27

程序基礎(chǔ)架構(gòu)

2012-08-31 09:36:01

CSS

2022-08-03 11:00:20

Linux內(nèi)核

2016-09-22 16:48:38

微信程序邏輯分析

2022-11-29 11:21:20

單體分層應(yīng)用架構(gòu)

2010-01-07 15:07:34

Ubuntu Anju

2011-06-10 11:05:05

Qt Quick QML

2010-03-03 13:56:24

2017-07-05 17:47:17

架構(gòu)DockerContainer

2024-03-12 12:57:07

Redis主從架構(gòu)

2010-02-06 15:32:30

Android架構(gòu)

2009-12-07 18:43:29

WCF框架
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)