成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

探索小程序底層架構原理

開發 架構
在這之前,我們先來思考一個問題,小程序在架構上為什么會選擇雙線程?

雙線程架構

在這之前,我們先來思考一個問題,小程序在架構上為什么會選擇雙線程?

為什么是雙線程?

加載及渲染性能

小程序的設計之初就是要求快速,這里的快指的是加載以及渲染。

目前主流的渲染方式有以下3種:

  • Web技術渲染
  • Native技術渲染
  • Hybrid技術渲染(同時使用了webview和原生來渲染)

從小程序的定位來講,它就不可能用純原生技術來進行開發,因為那樣它的編譯以及發版都得跟隨微信,所以需要像Web技術那樣,有一份隨時可更新的資源包放在遠程,通過下載到本地,動態執行后即可渲染出界面。

但如果用純web技術來開發的話,會有一個很致命的缺點那就是在 Web 技術中,UI渲染跟 JavaScript 的腳本執行都在一個單線程中執行,這就容易導致一些邏輯任務搶占UI渲染的資源,這也就跟設計之初要求的快相違背了。

因此微信小程序選擇了Hybrid 技術,界面主要由成熟的 Web 技術渲染,輔之以大量的接口提供豐富的客戶端原生能力。同時,每個小程序頁面都是用不同的WebView去渲染,這樣可以提供更好的交互體驗,更貼近原生體驗,也避免了單個WebView的任務過于繁重。

微信小程序是以webview渲染為主,原生渲染為輔的混合渲染方式。

管控安全

由于web技術的靈活開放特點,如果基于純web技術來渲染小程序的話,勢必會存在一些不可控因素和安全風險。

為了解決安全管控的問題,小程序從設計上就阻止了開發者去使用一些瀏覽器提供的開放性api,比如說跳轉頁面、操作DOM等等。如果把這些東西一個一個地去加入到黑名單,那么勢必會陷入一個非常糟糕的循環,因為瀏覽器的接口也非常豐富,那么就很容易遺漏一些危險的接口,而且就算是禁用掉了所有的接口,也防不住瀏覽器內核的下次更新。

所以要徹底解決這個問題,必須提供一個沙箱環境來運行開發者的JavaScript 代碼。這個沙箱環境只提供純 JavaScript 的解釋執行環境,沒有任何瀏覽器相關接口。那么像HTML5中的ServiceWorker、WebWorker特性就符合這樣的條件,這兩者都是啟用另一線程來執行 javaScript。

這就是小程序雙線程模型的由來:

  • 渲染層:界面渲染相關的任務全都在 WebView 線程里執行,通過邏輯層代碼去控制渲染哪些界面。一個小程序存在多個界面,所以渲染層存在多個 WebView。
  • 邏輯層:創建一個單獨的線程去執行 JavaScript,在這個環境下執行的都是有關小程序業務邏輯的代碼。

雙線程模型

小程序的架構模型有別與傳統web單線程架構,小程序為雙線程架構。

微信小程序的渲染層與邏輯層分別由兩個線程管理,渲染層的界面使用 webview 進行渲染;邏輯層采用 JSCore運行JavaScript代碼。

webview渲染線程

如何找到渲染層?

1.我們可以通過調試微信開發者工具:微信開發者工具 ->調試 ->調試微信開發者工具

圖片

2.然后我們會再看到一個調試界面,看起來跟我們平時用的瀏覽器調試界面幾乎一摸一樣

圖片

但這并不是小程序的渲染層,而是開發者工具的結構。但我們在里面可以發現有一些webview標簽,在第一個webview上的src屬性看著是不是有點眼熟,沒猜錯的話它就是我們當前小程序打開頁面的路徑。所以這個webview才是小程序真正的渲染層。這里你會發現它里面并不只有一個webview,其實里面包含著視圖層的webview,業務邏輯層webview,開發者工具的webview。

開發者工具的邏輯層跑在webview中主要是為了模擬真機上的雙線程

3.打開渲染層一探究竟

通過showdevTools方法來打開調試此webview界面的調試器

document.querySelectorAll('webview')[0].showDevTools(true)

這里我們看到的才真正是小程序的渲染層,也就是小程序代碼編譯后的樣子,我們會發現這里的標簽都與我們開發時寫的不一樣,都統一加了wx-前綴。了解過webComponent的同學相信一眼就能看出他們非常相似,但小程序并沒有直接使用webComponent,而是自行搭建了一套組件系統Exparser。

Exparser的組件模型與WebComponents標準中的Shadow DOM高度相似。Exparser會維護整個頁面的節點樹相關信息,包括節點的屬性、事件綁定等,相當于一個簡化版的Shadow DOM實現。

為什么不直接使用webComponent,而是選擇自行搭建一套組件系統?

點擊查看:

  • 管控與安全:web技術可以通過腳本獲取修改頁面敏感內容或者隨意跳轉其它頁面
  • 能力有限:會限制小程序的表現形式
  • 標簽眾多:增加理解成本

JSCore邏輯線程

邏輯層我們直接在小程序開發者工具的調試器中輸入document就能看到。

小程序將所有業務代碼置于同一個線程中運行,在小程序開發者工具中邏輯線程同樣是跑在一個webview中;webview中的appservice.html除了引入業務代碼js之外,還有后臺服務內嵌的一些基礎功能代碼。

編譯原理

了解完小程序的雙線程架構,我們再來看一下小程序的代碼是如何編譯運行的,微信開者工具模擬器運行的代碼是經過本地預處理、本地編譯,而微信客戶端運行的代碼是額外經過服務器編譯的。這里我們還是以微信開發者工具為例來探索一番。

在開發者工具輸入openVendor(),會幫我們打開微信開發者工具的WeappVendor文件夾

圖片

在這里我們我們會看到一些wxvpkg文件,這是小程序的各個版本的基礎庫文件,還有兩個值得我們注意的文件:wcc、wcsc,這兩個文件是小程序的編譯器,分別用來編譯wxml和wxss文件。

編譯wxml

這里我們可以將開發者工具中的wcc編譯器拷貝一份出來,嘗試去用它編譯一下wxml文件,看看最后的產物是什么?

圖片

我們在終端執行一下以下命令

./wcc -b index.wxml >> wxml_output.js

然后它會在當前目錄下生成一個wxml_output.js文件,文件中有一個非常重要的方法$gwx,該方法會返回一個函數。該函數的具體作用我們可以嘗試執行一下看看結果。

我們打開渲染層webview搜索一下該方法(為了方便查看,這里會用個小項目來演示)

圖片

從這里我們可以看到該方法會傳入一個小程序頁面的路徑,返回的依然是一個函數

var decodeName = decodeURI("./index/index.wxml")
var generateFunc = $gwx(decodeName)

我們嘗試按這里流程執行一下$gwx返回的函數,看看返回的內容是什么?

<!--compiler-test/index.wxml-->
<view class="qd_container" >
<text name="title">wxml編譯</text>
<view >{{ name }}</view>
</view>
const func = $gwx(decodeURI('index.wxml'))
console.log(func())

圖片

沒錯,這個函數正是用來生成Virtual DOM。

編譯wxss

我們同樣可以用微信開發者工具中的wcsc來編譯一下wxss文件。

(大家認為這里應該是會生成css文件還是js文件呢?)

我們在終端執行一下以下命令來編譯wxss文件

./wcsc -js index.wxss >> wxss_output.js

圖片

相比之前的wcc編譯wxml文件來說,這次的編譯相對來說比較簡單,它主要完成了以下內容:

  • rpx單位的換算,轉換成px
  • 提供setCssToHead方法將轉換好的css添加到head中

rpx動態適配

小程序提供rpx單位來適配各種尺寸的設備。

圖片

比如:

/*index.wxss */
.qd_container {
width: 100rpx;
background: skyblue;
border: 1rpx solid salmon;
}
.qd_reader {
font-size: 20rpx;
color: #191919;
font-weight: 400;
}

經過編譯之后會生成setCssToHead方法并執行

setCssToHead([".",[1],"qd_container { width: ",[0,100],"; background: skyblue; border: ",[0,1]," solid salmon; }\n.",[1],"qd_reader { font-size: ",[0,20],"; color: #191919; font-weight: 400; }\n",])( typeof __wxAppSuffixCode__ == "undefined"? undefined : __wxAppSuffixCode__ );

里面會調用transformRPX方法將rpx轉成px

var transformRPX = window.__transformRpx__ || function(number, newDeviceWidth) {
if ( number === 0 ) return 0;
number = number / BASE_DEVICE_WIDTH * ( newDeviceWidth || deviceWidth );
number = Math.floor(number + eps);
if (number === 0) {
if (deviceDPR === 1 || !isIOS) {
return 1;
} else {
return 0.5;
}
}
return number;
}
// 主要公式
number = number / BASE_DEVICE_WIDTH * (newDeviceWidth || deviceWidth);
number = Math.floor(number + eps); //為了精確
// rpx值 / 基礎設備寬750 * 真實設備寬

渲染流程

上面了解完wxml與wxss的編譯過程,我們再來整體了解一下頁面的渲染流程。

先來了解渲染層模版

從上面的渲染層webview我們可以找到這兩個webview

圖片

第一個index/indexwebview我們上面說了它就是對應我們的小程序的渲染層,也就是真正的小程序頁面。

那么下面這個instanceframe.html是什么呢?

這個webview其實是小程序渲染模版,打開查看一番

圖片

它其實就是提前注入了一些頁面所需要的公共文件,以及紅框內的一些頁面獨立的文件占位符,這些占位符會等小程序對應頁面文件編譯完成后注入進來。

如何保證代碼的注入是在渲染層webview的初始化之后執行?

在剛剛渲染模版webview的下方有這樣一段腳本:

if (document.readyState === 'complete') {
alert("DOCUMENT_READY")
} else {
const fn = () => {
alert("DOCUMENT_READY")
window.removeEventListener('load', fn)
}
window.addEventListener('load', fn)
}

很明顯,這里在頁面初始化完成后,通過alert來進行通知。此時的native/nw.js會攔截這個alert,從而知道此時的webview已經初始化完成。

整體渲染流程

了解了上面這個重要過程,我們就可以將整個流程串聯起來了。

1.打開小程序,創建視圖層頁的webview時,此時會初始化渲染層webview,并且會將該web view地址設置為instanceframe.html,也就是我們的渲染層模版。

2.然后進入頁面/index/index,等instanceframewebview初始化完成,會將頁面index/index編譯好的代碼注入進來并執行。

// 將webview src路徑修改為頁面路徑
history.pushState('', '', 'http://127.0.0.1:26444/__pageframe__/index/index')

/*
...
這里還有一些 wx config及wxss編譯后的代碼
*/

// 這里是
var decodeName = decodeURI("./index/index.wxml")
var generateFunc = $gwx(decodeName)
if (decodeName === './__wx__/functional-page.wxml') {
generateFunc = function () {
return {
tag: 'wx-page',
children: [],
}
}
}
if (generateFunc) {
var CE = (typeof __global === 'object') ? (window.CustomEvent || __global.CustomEvent) : window.CustomEvent;
document.dispatchEvent(new CE("generateFuncReady", {
detail: {
generateFunc: generateFunc
}
}))
__global.timing.addPoint('PAGEFRAME_GENERATE_FUNC_READY', Date.now())
} else {
document.body.innerText = decodeName + " not found"
console.error(decodeName + " not found")
}

3.此時通過history.pushState方法修改webview的src但是webview并不會發送頁面請求,并且將調用$gwx為生成一個generateFun方法,前面我們了解到該方法是用來生成虛擬dom的。

4.然后會判斷該方法存在時,通過document.dispatchEvent 派發發自定義事件generateFuncReady 將generateFunc當作參數傳遞給底層渲染庫。

5.然后在底層渲染庫WAWebview.js中會監聽自定義事件generateFuncReady ,然后通過 WeixinJSBridge 通知 JS 邏輯層視圖已經準備好()。

圖片

6.最后 JS 邏輯層將數據給 Webview 渲染層,WAWebview.js在通過virtual dom生成真實dom過程中,它會掛載到頁面的document.body上,至此一個頁面的渲染流程就結束了。

數據更新

小程序的視圖層目前使用 WebView 作為渲染載體,而邏輯層是由獨立的 JavascriptCore 作為運行環境。

在架構上,WebView 和 JS Core 都是獨立的模塊,并不具備數據直接共享的通道。所以在更新數據時必須調用setData來通知渲染層做更新。

setData

  • 邏輯層虛擬 DOM 樹的遍歷和更新,觸發組件生命周期和 observer 等;
  • 將 data 從邏輯層傳輸到視圖層;
  • 視圖層虛擬 DOM 樹的更新、真實 DOM 元素的更新并觸發頁面渲染更新。

這里第二步由于WebView 和 JS Core 都是獨立的模塊,數據傳輸是通過 evaluateJavascript 實現的,還會有額外 JS 腳本解析和執行的耗時因此數據到達渲染層是異步的。

因此切記:

  • 不要頻繁的去setData
  • 不要每次 setData 都傳遞大量新數據(單次stringify后不超過256kb)
  • 不要對后臺態頁面進行setData,會搶占正在執行的前臺頁面的資源

與Vue對比(再來看看Vue)

整體來講,小程序身上或多或少都有著vue的影子...(模版文件,data,指令,虛擬dom,生命周期等)

但在數據更新這里,小程序卻與Vue表現的截然不同。

1.頁面更新DOM是同步的還是異步的?

2.既然更新DOM是個同步的過程,為什么Vue中還會有nextTick鉤子?

mounted() {
this.name = '前端南玖'
console.log('sync',this.$refs.title.innerText) // 舊文案
// 新文案
Promise.resolve().then(() => {
console.log('微任務',this.$refs.title.innerText)
})
setTimeout(() => {
console.log('宏任務',this.$refs.title.innerText)
}, 0)
this.$nextTick(() => {
console.log('nextTick',this.$refs.title.innerText)
})
}

圖片

責任編輯:華軒 來源: 前端南玖
相關推薦

2023-06-09 14:01:00

架構程序APP

2021-10-25 09:41:04

架構運維技術

2019-10-16 16:33:41

Docker架構語言

2021-09-14 09:52:56

ToB小程序生態評估

2022-10-28 10:23:27

Java多線程底層

2020-11-05 11:14:29

Docker底層原理

2019-12-16 10:01:54

Java開發Web

2023-01-04 07:54:03

HashMap底層JDK

2024-01-05 09:00:00

SpringMVC軟件

2017-06-09 10:06:54

微信小程序架構分析

2017-06-09 10:40:00

微信小程序架構分析

2017-06-09 12:58:20

微信小程序架構分析

2021-06-10 10:51:27

程序基礎架構

2023-10-18 10:55:55

HashMap

2022-12-19 08:00:00

SpringBootWeb開發

2021-07-05 07:51:43

JVM底層Python

2021-07-23 13:34:50

MySQL存儲InnoDB

2010-02-04 10:43:05

Android DDM

2021-01-08 08:34:09

Synchronize線程開發技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91成人免费看 | 亚洲一区二区在线播放 | 色男人的天堂 | 免费一级黄色电影 | 午夜精品一区二区三区免费视频 | 在线观看成人免费视频 | 99精品一区二区三区 | 99精品一级欧美片免费播放 | 亚洲综合大片69999 | 亚洲第一天堂 | 国产精品美女久久久久aⅴ国产馆 | 午夜精品一区二区三区在线观看 | 亚洲视频欧美视频 | 亚洲一区中文字幕在线观看 | 在线四虎 | 日韩国产一区二区三区 | 国产婷婷在线视频 | www.久草| 午夜爽爽男女免费观看hd | 最新中文字幕 | 久久久久久久久91 | 久久国产一区二区 | 国产精品久久久久久久久久不蜜臀 | 日韩欧美国产精品 | 一区二区三区国产 | 国产精品免费av | 不卡视频一区 | 亚洲视频中文字幕 | 亚洲国产一区在线 | 久久在线精品 | 日日操夜夜摸 | 一区二区三区四区在线视频 | 欧美成人免费电影 | av国产精品| 久草久草久草 | 超碰97人人人人人蜜桃 | 国产欧美一级二级三级在线视频 | 亚洲国产aⅴ精品 | 日本国产一区二区 | 国产福利视频网站 | 日本精品在线一区 |