導航:首頁 > 源碼編譯 > 什麼是編譯器優化

什麼是編譯器優化

發布時間:2025-02-23 04:22:02

Ⅰ 淺談vue3的編譯優化

編譯優化:編譯器將模版編譯為渲染函數的過程中,盡可能地提取關鍵信息,並以此指導生成最優代碼的過程。

優化的方向:盡可能地區分動態內容和靜態內容,並針對不同的內容採用不同的優化策略

1.動態節點收集與補丁標志1.1傳統diff演算法的問題

比對新舊兩棵虛擬DOM樹的時候,總是要按照虛擬DOM的層級結構「一層一層」地遍歷

<divid="foo"><pclass="bar">{{text}}</p></div>

上面這段代碼中,當響應式數據text值發生變化的時候,最高效的更新方式是直接設置p標簽的文本內容

傳統Diff演算法做不到如此高效,當text值發生變化的時候,會產生一顆新的虛擬DOM樹,對比新舊虛擬DOM過程如下:

對比div節點,以及該節點的屬性和子節點

對比p節點,以及該節點的屬性和子節點

對比p節點的文本子節點,如果文本子節點的內容變了,則更新,否則什麼都不做

可以發現,有很多無意義的對比操作。

總結:

傳統diff演算法的問題:無法利用編譯時提取到的任何關鍵信息,導致渲染器在運行時不會去做相關的優化。

vue3的編譯器會將編譯得到的關鍵信息「附著」在它生成的虛擬DOM上,傳遞給渲染器,執行「快捷路徑」。

1.2Block與PatchFlags

傳統Diff演算法無法避免新舊虛擬DOM樹間無用的比較操作,是因為運行時得不到足夠的關鍵信息,從而無法區分動態內容和靜態內容。換句話說,只要運行時能夠區分動態內容和靜態內容,就可以實現極簡的優化策略

舉個例子:

<div><div>foo</div><p>{{bar}}</p></div>

只有{{bar}}是動態的內容。理想情況下,當數據bar的值變化時,只需要更新p標簽的文本節點即可。為了實現這個目標,需要提供信息給運行時

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}

可以發現,虛擬節點多了一個額外的屬性,即patchFlag(補丁標志),存在該屬性,就認為是動態節點

patchFlag(補丁標志)可以理解為一系列的數字標記,含義如下

constPatchFlags={TEXT:1,//代表節點有動態的textContentCLASS:2,//代表元素有動態的class綁定STYLE:3//其他。。。}

可以在虛擬節點的創建階段,把它的動態子節點提取出來,並存儲到該虛擬節點的dynamicChildren數組中

constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點],//將children中的動態節點提取到dynamicChildren數組中dynamicChildren:[{tag:'p',children:ctx.bar,patchFlag:PatchFlags.TEXT}]}

Block定義:帶有dynamicChildren屬性的虛擬節點稱為「塊」,即(Block)

一個Block本質上也是一個虛擬DOM,比普通的虛擬節點多處一個用來存儲動態節點的dynamicChildren屬性。(能夠收集所有的動態子代節點)

渲染器的更新操作會以Block為維度。當渲染器在更新一個Block時,會忽略虛擬節點的children數組,直接找到dynamicChildren數組,並只更新該數組中的動態節點。跳過了靜態內容,只更新動態內容。同時,由於存在對應的補丁標志,也能夠做到靶向更新。

Block節點有哪些:模版根節點、帶有v-for、v-if/v-else-if/v-else等指令的節點

1.3收集動態節點

編譯器生成的渲染函數代碼中,不會直接包含用來描述虛擬節點的數據結構,而是包含著用來創建虛擬DOM節點的輔助函數,如下

render(){returncreateVNode('div',{id:'foo'},[createVNode('p',null,'text')])}functioncreateVNode(tag,props,children){constkey=props&&props.keyprops&&deleteprops.key//省略部分代碼return{tag,props,children,key}}

createVNode的返回值是一個虛擬DOM節點

舉個例子:

<divid="foo"><pclass="bar">{{bar}}</p></div>

上面模版生成帶有補丁標志的渲染函數如下:

render(){returncreateVNode('div',{id:'foo'},[createVNode('p',{class:'bar'},text,PatchFlags.TEXT)])}

怎麼將根節點變成一個Block,如何將動態子代節點收集到該Block的dynamicChildren數組中?

可以發現,在渲染函數內,對createVNode函數的調用是層層嵌套結構,執行順序是內層先執行,外層再執行,當外層createVNode函數執行時,內層的createVNode函數已經執行完畢了。因此,為了讓外層Block節點能夠收集到內層動態節點,需要一個棧結構的數據來臨時存儲內層的動態節點。代碼實現如下:

//動態節點constdynamicChildrenStack=[]//當前動態節點集合letcurrentDynamicChildren=null//openBlock用來創建一個新的動態節點集合,並將該集合壓入棧中functionopenBlock(){dynamicChildrenStack.push((currentDynamicChildren=[]))}//closeBlock用來通過openBlock創建的動態節點集合從棧中彈出functioncloseBlock(){currentDynamicChildren=dynamicChildrenStack.pop()}

然後調整createVNode函數

<div><div>foo</div><p>{{bar}}</p></div>0

接著調整

<div><div>foo</div><p>{{bar}}</p></div>11.4.渲染器的運行時支持

傳統的節點更新方式如下:

<div><div>foo</div><p>{{bar}}</p></div>2

優化後的更新方式,直接對比動態節點

<div><div>foo</div><p>{{bar}}</p></div>3

存在對應的補丁標志,可以針對性地完成靶向更新

<div><div>foo</div><p>{{bar}}</p></div>42.Block樹

除了模版的根節點是Block外,帶有結構化指令的節點,如:v-if、v-for,也都應該是Block

2.1帶有v-if指令的節點<div><div>foo</div><p>{{bar}}</p></div>5

假設只有最外層的div標簽會作為Block,那麼變數foo的值為true還是false,block收集到的動態節點都是一樣的,如下:

<div><div>foo</div><p>{{bar}}</p></div>6

這意味著,在Diff階段不會更新。顯然,foo不同值下,一個是section,一個是div,是不同標簽,是需要更新的。

再舉個例子:

<div><div>foo</div><p>{{bar}}</p></div>7

一樣會導致更新失敗

問題在於:dynamicChildren收集的動態節點是忽略虛擬DOM樹層級的,結構化指令會導致更新前後模版的結構發生變化,即模版結構不穩定

解決方法:讓帶有v-if/v-else-if/v-else等結構化指令的節點也作為Block即可,如下所示

<div><div>foo</div><p>{{bar}}</p></div>8<div><div>foo</div><p>{{bar}}</p></div>9

在Diff過程中,渲染器根據key值區分,使用新的Block替換舊的Block

2.2帶有v-for指令的節點

帶有v-for指令的節點也會讓虛擬DOM樹變得不穩定

例子:

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}0

list的值由[1,2]變成[1]

更新前後對應的Block樹如下:

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}1

更新前後,動態節點數量不一致,無法進行diff操作(diff操作的前提是:操作的節點必須是同層級節點,dynamicChildren不一定是同層級的)

解決方法:讓v-for指令的標簽也作為Block角色,保證虛擬DOM樹具有穩定的結構,無論v-for在運行時怎樣變化。如下:

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}2

由於v-for指令渲染的是一個片段,所以類型用Fragment

2.3Fragment的穩定性//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}3

發現Fragment本身收集的動態節點存在結構是不穩定的情況

結構不穩定:指更新前後一個block的dynamicChildren數組中收集的動態節點的數量或順序不一致

這種情況無法直接進行靶向更新

解決方法:回退到傳統虛擬DOM的Diff手段,即直接使用Fragment的children而非dynamicChildren來進行Diff操作

Fragment的子節點仍然可以是由Block組成的數組

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}4

當Fragment的子節點更新時,就可以恢復優化模式

有穩定的Fragment嗎?如下:

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}5

穩定的Fragment,可以使用優化模式

vue3模版中的多個根節點,也是穩定的Fragment

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}63.靜態提升

減少更新時創建虛擬DOM帶來的性能開銷和內存佔用

如:

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}7

沒有靜態提升時,渲染函數是:

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}8

響應式數據title變化後,整個渲染函數會重新執行

把純靜態的節點提升到渲染函數之外

//傳統虛擬DOM描述constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar},]}9

響應式數據title變化後,不會重新創建靜態的虛擬節點

註:靜態提升是以樹為單位的

包含動態綁定的節點本身不會被提升,但是該節點上的靜態屬性是可以被提升的

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}0

可以減少創建虛擬DOM產生的開銷以及內存佔用

4.預字元串化

基於靜態提升,進一步採用預字元串化優化。

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}1

採用靜態提升優化策略後

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}2

採用預字元串化將這些靜態節點序列化為字元串,並生成一個Static類型的VNode

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}3

優勢:

大塊的靜態內容可以通過innerHTML設置,在性能上有一定優勢

減少創建虛擬節點產生的性能開銷

減少內存佔用

5.緩存內聯事件處理函數//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}4//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}5

每次重新渲染時,都會為Com組件創建一個全新的props對象。同時,props對象中onChange屬性的值也會是全新的函數。造成額外的性能開銷

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}66.v-once

v-once可以對虛擬DOM進行緩存

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}7

由於節點被緩存,意味著更新前後的虛擬節點不會發生變化,因此也就不需要這些被緩存的虛擬節點參與Diff操作了。編譯後的結果如下:

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}8

v-once包裹的動態節點不會被父級Block收集,因此不會參與Diff操作

v-once指令通常用於不會發生改變的動態綁定中,例如綁定一個常量

//編譯優化後constvnode={tag:'div',children:[{tag:'div',children:'foo'},{tag:'p',children:ctx.bar,patchFlag:1},//這是動態節點]}9

v-once帶來的性能提升

避免組件更新時重新創建虛擬DOM帶來的性能開銷。因為虛擬DOM被緩存了,所以更新時無需重新創建

避免無用的Diff開銷。因為被v-once標記的虛擬DOM樹不會被父級Block節點收集

7.總結

1.vue3提出了Block的概念,利用Block樹及補丁標志

2.靜態提升:可以減少更新時創建虛擬DOM產生的性能開銷和內存佔用

3.預字元串化:在靜態提升的基礎上,對靜態節點進行字元串化。這樣做能夠減少創建虛擬節點產生的性能開銷以及內存佔用

4.緩存內聯事件處理函數:避免造成不必要的組件更新

5.v-once指令:緩存全部或部分虛擬節點,能夠避免組件更新時重新創建虛擬DOM帶來的性能開銷,也可以避免無用的Diff操作

原文:https://juejin.cn/post/7101859824203202568

Ⅱ 簡述代碼優化的常用措施和優化的三個層次

代碼優化的常用措施主要包括演算法優化、減少嵌套循環、避免全局變數、使用合適的數據結構、利用編譯器優化等。而代碼優化可以在三個層次上進行:演算法級別優化、代碼級別優化和編譯器優化。

演算法級別的優化是最高層次的優化,它關注的是演算法本身的效率和復雜度。例如,當我們處理排序或搜索問題時,選擇高效的演算法如快速排序或二分搜索,會比使用冒泡排序或線性搜索更加高效。這種優化可以顯著減少程序運行所需的時間和資源。

代碼級別的優化關注的是代碼的具體實現。這包括減少不必要的計算,避免重復的代碼,以及優化循環和條件判斷。例如,如果在循環內部有不會改變的計算,我們可以將這些計算移到循環外部,從而減少每次迭代都需要進行的計算量。此外,我們還可以通過減少嵌套循環的深度,或者使用更高效的數據結構來提高代碼的運行效率。

編譯器優化則是在編譯階段進行的優化。現代編譯器有許多內置的優化策略,可以在編譯時將源代碼轉換為更高效的機器代碼。例如,編譯器可能會自動進行常量折疊,或者進行死代碼刪除。此外,編譯器還可以進行指令流水線的優化,以及利用硬體的並行性來提高運行效率。我們可以通過選擇合適的編譯器選項,或者調整源代碼以更好地利用編譯器的優化策略,來提高程序的運行效率。

總的來說,代碼優化是一個多層次、多角度的過程,需要我們從演算法、代碼實現和編譯器等多個方面進行考慮。通過合理的優化策略,我們可以顯著提高程序的運行效率,從而提升用戶體驗和系統性能。

Ⅲ 應用編譯優化三種模式

應用編譯優化三種模式分別是:編譯時間優化模式、執行時間優化模式和代碼大小優化模式。
1、編譯時間優化模式:關注編譯速度的提升,以縮短應用程序高脊的編譯時間為目標。在這種模式下,編譯器會減少編譯時間,會降低應用程序的執行效率。
2、執行時間優化模式:關注應用程序的執行效率,以提高應用程序的性能為目標。在這種模式下,編譯器會優化應用程序的代碼,以提高執行效率,會增加編譯時間。
3、代碼大小優化模式:關注應用程序的大小,以減小應兆培用程序的體積為目標。族念唯在這種模式下,編譯器會減小應用程序的代碼大小,以減小應用程序的體積,會降低應用程序的執行效率。

閱讀全文

與什麼是編譯器優化相關的資料

熱點內容
壓縮的照片損畫質嗎 瀏覽:562
在電腦中如何查找伺服器地址 瀏覽:12
點陣圖轉pdf 瀏覽:1001
linuxshell腳本命令 瀏覽:295
阿里雲新購伺服器 瀏覽:759
java函數調用數組 瀏覽:991
自駕游租車app哪個最好 瀏覽:523
程序員刪除鏈表 瀏覽:96
40命令行編譯器 瀏覽:738
連接命令方塊 瀏覽:953
ai演算法的藝術品製作 瀏覽:240
郵箱加密文件怎麼列印 瀏覽:886
pythoninstall命令 瀏覽:545
ppt怎麼加密保險 瀏覽:17
如何限制dhcp伺服器 瀏覽:962
gprs的地址是伺服器的 瀏覽:170
javajtable單元格 瀏覽:308
pythoncookbook英文版 瀏覽:999
xp命令被禁止 瀏覽:332
linuxjava開發環境搭建 瀏覽:711