摘要:引擎可以是一個(gè)標(biāo)準(zhǔn)的解釋器,也可以是一個(gè)將編譯成某種形式的字節(jié)碼的即時(shí)編譯器。和其他引擎最主要的差別在于,不會(huì)生成任何字節(jié)碼或是中間代碼。不使用中間字節(jié)碼的表示方式,就沒(méi)有必要用解釋器了。
原文地址:https://blog.sessionstack.com...
數(shù)周之前,我們開(kāi)始寫(xiě)作一檔專欄,旨在深入挖掘JavaScript,希望能真正弄清楚它是怎么工作的。我們認(rèn)為,如果了解了JavaScript的構(gòu)建模塊,以及它們之間是如何協(xié)同工作的,就能寫(xiě)出更好的代碼和app。
該專欄的第一篇文章,主要講了引擎、runtime和調(diào)用棧的概要知識(shí)。今天這第二篇,我們會(huì)深入地研究Google的V8 JS引擎的內(nèi)部結(jié)構(gòu)。此外,我們還會(huì)提供一些快捷的技巧,幫助大家寫(xiě)出更優(yōu)質(zhì)的JavaScript代碼——這些技巧是我們?cè)赟essionStack的開(kāi)發(fā)團(tuán)隊(duì)開(kāi)發(fā)產(chǎn)品時(shí)所發(fā)現(xiàn)的最佳方案。
概述
所謂的JavaScript引擎是一個(gè)能運(yùn)行JavaScript代碼的程序(program)或解釋器(interpreter)。JavaScript引擎可以是一個(gè)標(biāo)準(zhǔn)的解釋器,也可以是一個(gè)將JavaScript編譯成某種形式的字節(jié)碼的即時(shí)編譯器。
下面是一些正在開(kāi)發(fā)JavaScript引擎的比較流行的工程:
1、V8——開(kāi)源,Google用C++開(kāi)發(fā)的
2、Rhino——開(kāi)源,火狐(Mozilla Foundation)完全用Java開(kāi)發(fā)
3、SpiderMonkey——最早的JavaScript引擎,過(guò)去在網(wǎng)景瀏覽器(Netscape Navigator)中使用,今天則在火狐瀏覽器(Firefox)中使用
4、JavaScriptCore——開(kāi)源,市場(chǎng)上稱作Nitro,由Apple為Safari開(kāi)發(fā)
5、KJS——KDE的引擎,最初由Harri Porten為KDE項(xiàng)目的Konqueror網(wǎng)頁(yè)瀏覽器所開(kāi)發(fā)
6、Chakra(JScript9)——IE瀏覽器
7、Chakra(JavaScript)——Microsoft Edge
8、Nashorn——OpenJDK開(kāi)源項(xiàng)目的一部分,用的是Oracle Java語(yǔ)言和工具組
9、JerryScript——用于物聯(lián)網(wǎng)的輕量級(jí)引擎
為什么要開(kāi)發(fā)V8引擎?
V8引擎是由Google開(kāi)發(fā)的開(kāi)源產(chǎn)品,使用C++開(kāi)發(fā)。該引擎在Google Chrome瀏覽器中使用。和其他的引擎不同,V8還被流行的Node.js runtime使用。
最初,V8被設(shè)計(jì)用于提升web瀏覽器內(nèi)部的JavaScript運(yùn)行的性能。為了提升速度,V8把JavaScript代碼翻譯成執(zhí)行效率更高的機(jī)器碼(不使用解釋器來(lái)做這件事)。在執(zhí)行JavaScript代碼時(shí),V8像很多的現(xiàn)代JavaScript引擎——如SpiderMonkey或Rhino(Mozilla)——一樣,實(shí)現(xiàn)了一個(gè)JIT編譯器(即時(shí)編譯器),從而把JavaScript代碼編譯成機(jī)器語(yǔ)言。和其他引擎最主要的差別在于,V8不會(huì)生成任何字節(jié)碼或是中間代碼。
V8曾有兩個(gè)編譯器
在5.9版本(今年早些時(shí)候發(fā)布)的V8出來(lái)之前,V8使用兩個(gè)編譯器:
1、full-codegen——一個(gè)簡(jiǎn)單且快的編譯器,它能生成簡(jiǎn)單和運(yùn)行起來(lái)相對(duì)慢的機(jī)器碼
2、Grankshaft——一個(gè)相對(duì)來(lái)說(shuō)更復(fù)雜的(實(shí)時(shí))、優(yōu)化的編譯器,生成高度優(yōu)化的代碼
V8引擎在內(nèi)部還使用相當(dāng)多的線程:
1、主線程(main線程)做的是我們通常能想到的事情:拿到我們的代碼,編譯代碼,然后執(zhí)行之
2、同時(shí),還有一個(gè)獨(dú)立的用于編譯的線程,這樣主線程就能在該獨(dú)立用于編譯的線程優(yōu)化代碼的時(shí)候不間斷地執(zhí)行代碼
3、一個(gè)Pfofiler線程(分析器線程),它能告訴運(yùn)行環(huán)境(runtime)我們?cè)谀男┓椒ㄉ匣舜罅康臅r(shí)間,以便Grankshaft可以優(yōu)化這些方法
4、一些處理垃圾回收清理的線程
第一次執(zhí)行JavaScript代碼時(shí),V8充分使用full-codegen來(lái)將解析過(guò)的JavaScript直接翻譯成機(jī)器碼,這個(gè)過(guò)程不會(huì)做任何的中間轉(zhuǎn)化。這種做法使得V8能夠非常快速地開(kāi)始執(zhí)行機(jī)器碼。V8不使用中間字節(jié)碼的表示方式,就沒(méi)有必要用解釋器了。
當(dāng)我們的代碼運(yùn)行了一段時(shí)間后,Profiler線程就會(huì)收集到足夠的數(shù)據(jù),可以判斷出哪些方法需要被優(yōu)化。
接下來(lái),在另一個(gè)進(jìn)程里,Grankshaft優(yōu)化就開(kāi)始了。它將JavaScript的抽象語(yǔ)法樹(shù)翻譯成高度靜態(tài)單賦值的(SSA)表現(xiàn)形式——該表現(xiàn)形式被稱為Hydrogen,然后設(shè)法優(yōu)化Hydrogen圖。大部分的優(yōu)化都是在這一層面完成的。
代碼嵌入(Inlining)
第一個(gè)優(yōu)化是提前嵌入盡可能多的代碼。
代碼嵌入(Inlining)是將一個(gè)調(diào)用點(diǎn)(調(diào)用某函數(shù)的那行代碼)替換成被調(diào)用函數(shù)的函數(shù)體。這個(gè)簡(jiǎn)單的步驟使得接下來(lái)的優(yōu)化更有意義。
隱藏類(Hidden class)
JavaScript是一門(mén)基于原型的語(yǔ)言:沒(méi)有什么類或?qū)ο笫峭ㄟ^(guò)克隆的方式生成的。JavaScript還是一門(mén)動(dòng)態(tài)的編程語(yǔ)言,意味著在一個(gè)對(duì)象實(shí)例化之后,可以輕松地為其增加或移除屬性。
大部分的JavaScript解釋器使用類似于字典的結(jié)構(gòu)(基于hash函數(shù))存儲(chǔ)對(duì)象屬性值在內(nèi)存中的位置。這種結(jié)構(gòu)使得相對(duì)于非動(dòng)態(tài)編程語(yǔ)言(如Java或C#)而言,在JavaScript中檢索一個(gè)屬性值麻煩很多。Java中,在編譯之前,所有對(duì)象的屬性都由一個(gè)固定的對(duì)象布局
所確定,在運(yùn)行時(shí)不會(huì)動(dòng)態(tài)的增加或移除(當(dāng)然,C#具有動(dòng)態(tài)類型,那是另外一個(gè)話題了)。所以,在非動(dòng)態(tài)編程語(yǔ)言中,屬性值(或指向?qū)傩缘闹羔槪┰趦?nèi)存中可以被儲(chǔ)存在一個(gè)連續(xù)的buffer里,且兩兩之間的偏移量是固定的。
由于使用字典在內(nèi)存中查找對(duì)象屬性位置非常低效,V8使用了一種不同的方法:隱藏類(hidden classes)。隱藏類和與Java類似的語(yǔ)言中使用的固定對(duì)象布局(類)的工作方式非常接近,只是隱藏類是在運(yùn)行時(shí)被創(chuàng)建的。現(xiàn)在,我們就來(lái)看看它們到底長(zhǎng)什么樣:
一旦“new Point(1,2)”被調(diào)用,V8就會(huì)創(chuàng)建一個(gè)隱藏類,稱為 “ C0 ” 。
到目前為止,Point還沒(méi)有被定義屬性,所以“ C0 ” 目前還是空的。
一旦第一個(gè)語(yǔ)句“this.x = x”被執(zhí)行(在“Point” 方法中),V8就會(huì)創(chuàng)建基于“ C0 ”的第二個(gè)隱藏類,稱為“ C1 ”。“ C1 ”描述了在內(nèi)存中屬性x的位置(相對(duì)于對(duì)象指針的)。在這個(gè)例子中,“x”被存儲(chǔ)在offset 0,表示在內(nèi)存中把Point對(duì)象視為連續(xù)的buffer時(shí),它的第一個(gè)offset對(duì)應(yīng)的就是屬性 “x”。V8還會(huì)用一個(gè) “類轉(zhuǎn)換”對(duì)“ C0 ”做個(gè)更新,該 “類轉(zhuǎn)換”描述的是如果一個(gè)屬性 “x”被添加到一個(gè)Point對(duì)象上,隱藏類需要從“ C0 ”變?yōu)椤?C1 ”。下面這個(gè)Point對(duì)象的隱藏類現(xiàn)在就是“ C1 ”了。
每一次當(dāng)一個(gè)新的屬性被添加到某個(gè)對(duì)象上時(shí),舊的隱藏類就會(huì)通過(guò)一個(gè)轉(zhuǎn)換路徑被更新為一個(gè)新的隱藏類。“隱藏類轉(zhuǎn)換”非常重要,因?yàn)樗屜嗤绞缴傻膶?duì)象們能共享隱藏類。如果兩個(gè)對(duì)象共享一個(gè)隱藏類,并且二者都被增加了一個(gè)相同的屬性,“隱藏類轉(zhuǎn)換”能保證二者能獲得相同的新的隱藏類和所有與之關(guān)聯(lián)的優(yōu)化代碼。
當(dāng)執(zhí)行 “this.y = y”語(yǔ)句(仍然是Point方法里的;位于“this.x = x”語(yǔ)句之后的那條語(yǔ)句)時(shí),上述過(guò)程會(huì)被重復(fù)一遍。
一個(gè)新的名為“ C2 ”隱藏類被創(chuàng)建,同時(shí)一個(gè)類轉(zhuǎn)換被添加到“ C1 ”上——用來(lái)描述如果一個(gè)屬性 “y”被添加到Point對(duì)象(其已經(jīng)包含了屬性 “x”)上,那么隱藏類就要變成“ C2 ”,并且Point對(duì)象的隱藏類被更新為“ C2 ”。
隱藏類轉(zhuǎn)換根據(jù)屬性被添加到對(duì)象上的順序而發(fā)生變化。我們看看下面這一小段代碼:
你可能會(huì)說(shuō)對(duì)p1和p2而言,它們會(huì)使用相同的隱藏類和類轉(zhuǎn)換。其實(shí)不然~ 對(duì) “p1”來(lái)說(shuō),先是屬性 “a”被添加,然后是屬性 “b”。而對(duì) “p2”來(lái)說(shuō),先是屬性 “b”被添加,然后才是屬性 “a”。這樣, “p1”和 “p2”就在不同的轉(zhuǎn)換路徑作用下,有了不同的隱藏類。在這兩種情形下,其實(shí)最好是用相同的順序初始化動(dòng)態(tài)屬性,這樣隱藏類就可以被復(fù)用了。
內(nèi)聯(lián)緩存(Inline caching)
V8還使用另一種優(yōu)化動(dòng)態(tài)類型語(yǔ)言的技巧,即所謂的內(nèi)聯(lián)緩存。內(nèi)聯(lián)緩存的使用,基于我們發(fā)現(xiàn):通常,同一個(gè)方法的重復(fù)調(diào)用是發(fā)生在相同類型的對(duì)象上的。內(nèi)聯(lián)緩存的深度解讀可查看這里。
這篇文章我們來(lái)說(shuō)說(shuō)內(nèi)聯(lián)緩存的大致概念。(以防您沒(méi)有時(shí)間閱讀上面提到的深度解讀文章)
所以內(nèi)聯(lián)緩存是怎么工作的呢?V8維護(hù)一個(gè)對(duì)象類型的緩存;這些對(duì)象在最近的方法調(diào)用中被當(dāng)做傳參,然后V8根據(jù)這個(gè)緩存信息來(lái)推斷將來(lái)什么樣類型的對(duì)象會(huì)再次被當(dāng)成傳參。如果V8能夠準(zhǔn)確推斷出接下來(lái)被傳入的對(duì)象類型,那么它就能繞開(kāi)獲取對(duì)象屬性的計(jì)算步驟,而只是使用先前查找該對(duì)象的隱藏類時(shí)所存儲(chǔ)的信息。
那么隱藏類和內(nèi)聯(lián)緩存的概念是如何關(guān)聯(lián)的呢?當(dāng)一個(gè)特定對(duì)象調(diào)用一個(gè)方法時(shí),V8引擎需要查找這個(gè)對(duì)象的隱藏類,以便確定獲取某個(gè)特定屬性時(shí)的offset。在對(duì)于同一個(gè)隱藏類兩次成功地調(diào)用相同的方法后,V8就略去隱藏類的查找,而將這個(gè)屬性的offset添加到對(duì)象自身的指針上。對(duì)于未來(lái)所有對(duì)該方法的調(diào)用,V8引擎都假設(shè)隱藏類沒(méi)有發(fā)生變化,并使用之前查詢中存儲(chǔ)的offset值直接跳到特定屬性的內(nèi)存地址里。這個(gè)過(guò)程極大地提升了執(zhí)行速度。
內(nèi)聯(lián)緩存的使用也是為什么同類型對(duì)象共享隱藏類是如此重要的原因。如果我們創(chuàng)建同一個(gè)類型的兩個(gè)對(duì)象,而它們隱藏類不同(就如同我們?cè)谇懊娴睦又凶龅哪菢樱琕8就不能使用內(nèi)聯(lián)緩存了,因?yàn)榧词箖蓚€(gè)對(duì)象類型相同,它們對(duì)應(yīng)的隱藏類會(huì)給它們的屬性分配不同的offset。
這兩個(gè)對(duì)象基本相同,但是“a” 和 “b”屬性創(chuàng)建的順序不同。
編譯成機(jī)器語(yǔ)言
一旦Hydrogen圖被優(yōu)化,Crankshaft就將這個(gè)圖降級(jí)到一個(gè)較低水平的表現(xiàn)形式——稱為L(zhǎng)ithium。大多數(shù)的Lithium實(shí)現(xiàn)都是面向特定系統(tǒng)結(jié)構(gòu)的。寄存器分配(Register allocation)發(fā)生在這一層面。
最后,Lithium被編譯成機(jī)器碼。然后會(huì)發(fā)生一些其他的事情,即所謂的OSR:on-stack replacement(堆棧上替換)。當(dāng)我們開(kāi)始編譯和優(yōu)化一個(gè)明顯耗時(shí)的方法時(shí),我們很可能之前一直在運(yùn)行它。V8不會(huì)將它之前執(zhí)行的很慢的代碼拋在一邊,再重新執(zhí)行優(yōu)化后的代碼。相反,他會(huì)對(duì)這些慢代碼所擁有的全部上下文(堆棧,寄存器)做一個(gè)轉(zhuǎn)換,以便能
夠在執(zhí)行這些慢代碼的過(guò)程中直接切換到優(yōu)化后的版本。這是一個(gè)非常復(fù)雜的任務(wù),要知道,V8已經(jīng)在其他的優(yōu)化中將代碼嵌入了(inlined the code initially)。當(dāng)然,V8不是唯一一個(gè)能做到這一點(diǎn)的引擎。
我們還有被稱為 “去優(yōu)化”的保障措施,能夠做相反的轉(zhuǎn)換,將代碼逆轉(zhuǎn)成未優(yōu)化的代碼,防止引擎做的假定不再為真時(shí)負(fù)面效應(yīng)的出現(xiàn)。
垃圾回收
說(shuō)到垃圾回收,V8使用一種傳統(tǒng)的分代式標(biāo)記清除方法(a traditional generational approach of mark-and-sweep),來(lái)清除老一代。標(biāo)記階段會(huì)阻止JavaScript執(zhí)行過(guò)程。為了控制垃圾回收的成本,并使代碼執(zhí)行更穩(wěn)定,V8使用增量標(biāo)記:和遍歷整個(gè)堆(heap)、試圖標(biāo)記所有可能的對(duì)象不同,它僅遍歷部分堆,然后恢復(fù)正常的執(zhí)行。下一次垃圾回收將從上一次堆遍歷停止的地方開(kāi)始。這就使得每一次正常執(zhí)行之間的停頓非常短暫。如前文所述,清除操作是由獨(dú)立的進(jìn)程來(lái)處理的。
點(diǎn)火和渦輪風(fēng)扇(Ignition and TurboFan)
隨著2017年早些時(shí)候V8 5.9版本的發(fā)布,一個(gè)新的執(zhí)行管線(execution pipeline)被引入了。該新型管線在真實(shí)世界的JavaScript應(yīng)用中甚至取得了更大的性能提升和巨大的內(nèi)存節(jié)約。
該新型管線構(gòu)建于V8解釋器Ignition和最新的優(yōu)化編譯器TurboFan之上。
你可以在此查看V8團(tuán)隊(duì)有關(guān)該主題的博文。
V8 5.9版本問(wèn)世后,由于V8團(tuán)隊(duì)力爭(zhēng)和新的JavaScript語(yǔ)言特性以及針對(duì)這些新特性所需要的優(yōu)化保持一致,full-codegen和Crankshaft(這兩項(xiàng)技術(shù)從2010年開(kāi)始為V8服務(wù))不再被V8用來(lái)運(yùn)行JavaScript。
Web和Node.js基準(zhǔn)上的改進(jìn)
這意味著整個(gè)V8將擁有更簡(jiǎn)單和更易維護(hù)的架構(gòu)。
這些改進(jìn)只是一個(gè)開(kāi)始。新的Ignition和TurboFan管線為未來(lái)的優(yōu)化鋪平了道路,未來(lái)JavaScript的性能會(huì)有更加巨大的提升,并能讓V8在Chrome和Node.js中節(jié)約資源。
最后,這里提供一些小技巧,幫助大家寫(xiě)出更優(yōu)化的、更優(yōu)質(zhì)的JavaScript。從上文中您一定可以輕松地總結(jié)出一些技巧,不過(guò)為了方便,仍然為您提供一份總結(jié)。
如何寫(xiě)出優(yōu)化的JavaScript
1、對(duì)象屬性的順序:永遠(yuǎn)用相同的順序?yàn)槟膶?duì)象屬性實(shí)例化,這樣隱藏類和隨后的優(yōu)化代碼才能共享。
2、動(dòng)態(tài)屬性:在對(duì)象實(shí)例化后為其新增屬性會(huì)導(dǎo)致隱藏類變化,從而會(huì)減慢為舊隱藏類所優(yōu)化的方法的執(zhí)行。所以,盡量在構(gòu)造函數(shù)中分配對(duì)象的所有屬性。
3、方法:重復(fù)執(zhí)行相同方法的代碼會(huì)比不同的方法只執(zhí)行一次的代碼運(yùn)行得更快(由于內(nèi)聯(lián)緩存)。
4、數(shù)組:避免使用keys不是遞增數(shù)字的稀疏數(shù)組(sparse arrays)。并不為每個(gè)元素分配內(nèi)存的稀疏數(shù)組實(shí)質(zhì)上是一個(gè)hash表。這種數(shù)組中的元素比通常數(shù)組的元素會(huì)花銷更大才能獲取到。此外,避免使用預(yù)申請(qǐng)的大型數(shù)組。最好隨著需要慢慢增加數(shù)組的大小。最后,不要?jiǎng)h除數(shù)組中的元素,因這會(huì)使得keys變得稀疏。
5、標(biāo)記值(Tagged values): V8用32個(gè)比特來(lái)表示對(duì)象和數(shù)字。它使用1個(gè)比特來(lái)區(qū)分是一個(gè)對(duì)象(flag = 1)還是一個(gè)整型(flag = 0)(被稱為SMI或SMall Integer,小整型,因其只有31比特來(lái)表示值)。然后,如果一個(gè)數(shù)值大于31比特,V8就會(huì)給這個(gè)數(shù)字進(jìn)行裝箱操作(boxing),將其變成double型,并創(chuàng)建一個(gè)新的對(duì)象將這個(gè)double型數(shù)字放入其中。所以,為了避免代價(jià)很高的boxing操作,盡量使用31比特的有符號(hào)數(shù)。
參考資源:
https://docs.google.com/docum...
https://github.com/thlorenz/v...
http://code.google.com/p/v8/w...
http://mrale.ph/v8/resources....
https://www.youtube.com/watch...
https://www.youtube.com/watch...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/91971.html
摘要:本文將會(huì)深入分析的引擎的內(nèi)部實(shí)現(xiàn)。該引擎使用在谷歌瀏覽器內(nèi)部。同其他現(xiàn)代引擎如或所做的一樣,通過(guò)實(shí)現(xiàn)即時(shí)編譯器在執(zhí)行時(shí)將代碼編譯成機(jī)器代碼。這可使正常執(zhí)行期間只發(fā)生相當(dāng)短的暫停。 原文 How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code 幾周前我們開(kāi)始了一個(gè)系列博文旨在深入...
摘要:前端日?qǐng)?bào)精選的作用鳥(niǎo)瞰前端再論性能優(yōu)化翻譯給創(chuàng)始人和們的許可協(xié)議解惑如何工作引擎深入探究?jī)?yōu)化代碼的個(gè)技巧譯文第期還是,讓我來(lái)解決你的困惑中文基礎(chǔ)為什么比快二分查找法你真的寫(xiě)對(duì)了嗎個(gè)人文章推薦機(jī)不可失直播技術(shù)盛宴,深圳騰訊開(kāi)發(fā)者大 2017-09-21 前端日?qǐng)?bào) 精選 setTimeout(fn, 0) 的作用鳥(niǎo)瞰前端 , 再論性能優(yōu)化翻譯:給創(chuàng)始人和 CTO 們的 React 許可協(xié)議...
摘要:歡迎來(lái)我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開(kāi)啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫(xiě)法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開(kāi)緩存網(wǎng)頁(yè)性能管理詳解寫(xiě)給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來(lái)我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開(kāi)啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫(xiě)法...
摘要:歡迎來(lái)我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開(kāi)啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫(xiě)法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開(kāi)緩存網(wǎng)頁(yè)性能管理詳解寫(xiě)給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來(lái)我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開(kāi)啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫(xiě)法...
摘要:歡迎來(lái)我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開(kāi)啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫(xiě)法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開(kāi)緩存網(wǎng)頁(yè)性能管理詳解寫(xiě)給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來(lái)我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開(kāi)啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫(xiě)法...
閱讀 3091·2023-04-25 15:44
閱讀 1876·2019-08-30 13:11
閱讀 2830·2019-08-30 11:11
閱讀 3004·2019-08-29 17:21
閱讀 1306·2019-08-29 15:38
閱讀 898·2019-08-29 12:49
閱讀 1793·2019-08-28 18:19
閱讀 3222·2019-08-26 14:01