摘要:的主要組件包含了一個(gè)全新的引擎,稱為量子,也稱為。這個(gè)新引擎集成了四種不同瀏覽器的最新創(chuàng)新技術(shù),創(chuàng)造出一個(gè)全新的超級(jí)引擎。這可以發(fā)生在多個(gè)圖層上。最終,擁有最高特異性的規(guī)則會(huì)勝出。
原文:Inside a Super Fast CSS Engine: Quantum CSS(Aka Stylo), Lin Clark
注:原文發(fā)布于 2017 年 8 月,本文翻譯于 2018 年 4 月,因此對(duì)文中跟時(shí)間相關(guān)的部分內(nèi)容做了調(diào)整,但不影響核心內(nèi)容。
全新的 CSS 引擎 - Stylo你可能已經(jīng)對(duì)量子項(xiàng)目(Project Quantum)有所耳聞,該項(xiàng)目對(duì) Firefox 瀏覽器的內(nèi)部實(shí)現(xiàn)進(jìn)行了重大重寫,以求更高性能。
本次重寫中,我們使用了并行瀏覽器引擎 Servo 的新技術(shù)。這里要介紹地是我們對(duì)瀏覽器引擎的重大改進(jìn)。
該項(xiàng)目的開發(fā)過程就像是給正在飛行中的飛機(jī)更換引擎。對(duì)于瀏覽器中的各個(gè)組件,我們是逐個(gè)進(jìn)行更改的。這樣,我們就可以在一個(gè)組件準(zhǔn)備就緒后,立刻從 Firefox 中看到最新效果。
Servo 的主要組件包含了一個(gè)全新的 CSS 引擎,稱為量子 CSS(Quantum CSS,也稱為 Stylo)。
這個(gè)新引擎集成了四種不同瀏覽器的最新創(chuàng)新技術(shù),創(chuàng)造出一個(gè)全新的超級(jí) CSS 引擎。
它充分利用了現(xiàn)代硬件多核心的特性,把所有的工作都變成了并行化操作。這使得它可以提速 2 ~ 4 倍,甚至最大能達(dá)到 18 倍。
在并行化的基礎(chǔ)上,它還結(jié)合了其他瀏覽器現(xiàn)有的最先進(jìn)優(yōu)化技術(shù)。所以,即使拋開并行化運(yùn)行技術(shù),它也仍然是個(gè)快速的 CSS 引擎。
這里,我們不禁要問,這個(gè) CSS 引擎到底做了什么呢?
要回答這個(gè)問題,首先要知道 CSS 引擎是什么,它是如何跟瀏覽器其他組件一起工作的。然后,我們?cè)倏纯?Stylo 是如何變得更快的。
CSS 引擎是瀏覽器渲染引擎(Rendering Engine)的重要組成部分。
渲染引擎的工作就是把網(wǎng)頁的 HTML 和 CSS 文件轉(zhuǎn)化為屏幕上顯示的像素點(diǎn)。
每個(gè)瀏覽器都有一個(gè)渲染引擎。Chrome 的稱為 Blink,Edge 的稱為 EdgeHTML,Safari 的稱為 WebKit,以及 Firefox 的稱為 Gecko 。
基本流程要想把文件變?yōu)橄袼攸c(diǎn),所有的渲染引擎基本上都會(huì)做以下相同的工作:
1、把文件解析成瀏覽器能理解的對(duì)象,包括 DOM 。從這個(gè)角度來說,DOM 掌握了整個(gè)頁面結(jié)構(gòu)。它知道每個(gè)元素之間的父子關(guān)系,但是它不知道這些元素具體長什么樣。
2、弄清楚每個(gè)元素應(yīng)該長什么樣。對(duì)于每個(gè) DOM 節(jié)點(diǎn),CSS 引擎首先會(huì)弄清楚應(yīng)該對(duì)它應(yīng)用什么 CSS 規(guī)則。然后,會(huì)計(jì)算出每個(gè) CSS 屬性的值。
3、計(jì)算出每個(gè)節(jié)點(diǎn)的尺寸和它在屏幕上的位置。為每個(gè)要在屏幕上顯示的內(nèi)容創(chuàng)建盒模型。這些盒模型不僅僅用來表示 DOM 節(jié)點(diǎn),它們也用來表示 DOM 節(jié)點(diǎn)的內(nèi)部內(nèi)容,比如一行一行的文本。
4、繪制不同的盒模型。這可以發(fā)生在多個(gè)圖層上。它就像是以前使用半透明紙的手繪動(dòng)畫,每個(gè)圖層都是獨(dú)立的一張紙。這樣我們就可以只改變當(dāng)前圖層的內(nèi)容,而不會(huì)影響到其他圖層的內(nèi)容。
5、取出已繪制的圖層,應(yīng)用任何僅包含合成器的屬性(比如變換),然后把它們合成為一張圖片。這就好比為這些疊加在一起的圖層拍一張照片,之后這張照片將會(huì)在屏幕上渲染出來。
從以上過程可以看出,當(dāng) CSS 引擎開始計(jì)算樣式時(shí),它已經(jīng)得到了兩樣?xùn)|西:
DOM 樹
樣式規(guī)則列表
引擎會(huì)逐個(gè)遍歷所有 DOM 節(jié)點(diǎn),并計(jì)算出它們的樣式。這個(gè)過程中,它會(huì)給 DOM 節(jié)點(diǎn)的每個(gè) CSS 屬性都進(jìn)行求值,包括樣式表中沒有聲明的屬性。
這個(gè)過程就像一個(gè)人從頭到尾填一張表格一樣。CSS 引擎需要給每個(gè) DOM 節(jié)點(diǎn)都填寫一張表格。并且,表格的每一處空白都需要填上值。
為了填這張表格,CSS 引擎需要做兩件事:
計(jì)算出每個(gè)節(jié)點(diǎn)應(yīng)該應(yīng)用什么樣式規(guī)則,即選擇器匹配(Selector Matching)
根據(jù)父節(jié)點(diǎn)或默認(rèn)值計(jì)算出缺失的屬性值,即樣式級(jí)聯(lián)(Cascading)
選擇器匹配在這個(gè)步驟中,CSS 引擎會(huì)把所有與 DOM 節(jié)點(diǎn)相匹配的樣式規(guī)則添加到一個(gè)列表中。
因?yàn)榭赡苡卸鄠€(gè)樣式規(guī)則都匹配中,所以可能有多個(gè)相同的 CSS 屬性聲明。
此外,瀏覽器本身也提供了一些默認(rèn)的樣式規(guī)則,即用戶代理樣式表。
那 CSS 引擎是如何確定應(yīng)該使用哪個(gè)值的呢?
這個(gè)時(shí)候就需要特異性規(guī)則(Specificity Rules)來幫忙了。
CSS 引擎會(huì)創(chuàng)建一個(gè)電子表格,然后根據(jù)不同的列對(duì)其進(jìn)行排序。
最終,擁有最高特異性的規(guī)則會(huì)勝出。所以,基于這個(gè)表格,CSS 引擎就能夠填充那些它能夠填充的屬性值了。
對(duì)于不能使用這個(gè)方式計(jì)算出的值,它就會(huì)使用樣式級(jí)聯(lián)。
樣式級(jí)聯(lián)樣式級(jí)聯(lián)讓 CSS 的編寫和維護(hù)都變得更加簡單。因?yàn)闃邮郊?jí)聯(lián),你可以在設(shè)置了 的 color 屬性后,直接就知道
、 和 也將會(huì)使用你設(shè)置的顏色值(除非被重寫了)。
為了找出級(jí)聯(lián)的樣式屬性,CSS 引擎會(huì)查看表格中的空白部分。
如果屬性默認(rèn)為繼承值,那么 CSS 引擎會(huì)沿著 DOM 樹往上查找,看看其祖先元素是否已經(jīng)設(shè)置了該值。
如果所有的祖先元素都沒有設(shè)置該值,或者該屬性并不是繼承,那么就會(huì)使用默認(rèn)值。
至此,一個(gè) DOM 節(jié)點(diǎn)的所有樣式屬性就都已經(jīng)得到計(jì)算值了。
樣式結(jié)構(gòu)共享其實(shí),上面提到的樣式表格與實(shí)際情形并不完全一致。
CSS 擁有的樣式屬性非常多,達(dá)到上百個(gè)。如果 CSS 引擎針對(duì)每個(gè) DOM 節(jié)點(diǎn)的屬性都保存一份樣式值,那么內(nèi)存將會(huì)被迅速耗盡。
相反,CSS 引擎通常會(huì)使用樣式結(jié)構(gòu)共享(Style Struct Sharing)。
它會(huì)把通常在一起使用的樣式值存儲(chǔ)于一個(gè)多帶帶的對(duì)象中,該對(duì)象稱為樣式結(jié)構(gòu)。
然后,與其重新存儲(chǔ)相同對(duì)象上的所有樣式值,計(jì)算的樣式對(duì)象實(shí)際只保存了指向那個(gè)對(duì)象的指針。
對(duì)于每個(gè)類別的樣式,實(shí)際上存儲(chǔ)的都是一個(gè)指向樣式結(jié)構(gòu)的指針。
這種共享方式既省內(nèi)存又省時(shí)間。這樣的話,具有相似樣式的節(jié)點(diǎn)(比如兄弟節(jié)點(diǎn))就只需要保存指向共享樣式結(jié)構(gòu)對(duì)象的指針即可。而且,由于很多屬性都是繼承來的,所以祖先節(jié)點(diǎn)可以跟所有的子孫節(jié)點(diǎn)共享相同的樣式結(jié)構(gòu)對(duì)象。
優(yōu)化改進(jìn)上面所說的就是優(yōu)化之前的樣式計(jì)算過程。
這個(gè)過程中進(jìn)行了很多計(jì)算工作。而且它不只是在第一個(gè)頁面加載的時(shí)候發(fā)生。它會(huì)在用戶與頁面進(jìn)行交互的過程中反復(fù)的發(fā)生,懸停在元素上或者改變 DOM 結(jié)構(gòu),都會(huì)觸發(fā)樣式重算(Restyle)。
也就是說,CSS 樣式計(jì)算是一個(gè)舉足輕重的待優(yōu)化點(diǎn)。而且在過去的 20 年里,各個(gè)瀏覽器一直都在測試使用不同的策略來優(yōu)化它。
Stylo 充分吸收了來自不同引擎的優(yōu)化策略,然后把它們結(jié)合在一起,從而創(chuàng)造出一個(gè)全新的超級(jí)引擎。
下面讓我們來看看 Stylo 的實(shí)現(xiàn)細(xì)節(jié)。
并行運(yùn)行Servo 是一個(gè)實(shí)驗(yàn)版的瀏覽器,Stylo 就是該項(xiàng)目的一部分。Servo 想把渲染頁面所需的所有工作都進(jìn)行并行化。
并行化具體指什么呢?
一臺(tái)計(jì)算機(jī)就像一個(gè)大腦。其中有一個(gè)部分是專門進(jìn)行邏輯思考的,叫做算術(shù)邏輯單元(Arithmetic Logic Unit, ALU)。在 ALU 附近,排列著一些短期記憶存儲(chǔ)單元,稱為寄存器(Register)。ALU 和寄存器都是一起放在在 CPU 內(nèi)部的。當(dāng)然也有用于長期記憶的存儲(chǔ)單元,稱為內(nèi)存(RAM)。
使用這種 CPU 的早期計(jì)算機(jī)在同一時(shí)間只能進(jìn)行一種事情。
不過在過去的十幾年里,CPU 已經(jīng)進(jìn)化到同時(shí)擁有多個(gè) ALU 和寄存器組,具備了多個(gè)核心。
這就意味著 CPU 可以一次進(jìn)行多種事情,而且是同時(shí)進(jìn)行的。
Stylo 通過利用計(jì)算機(jī)的這種特性,把不同 DOM 節(jié)點(diǎn)的樣式計(jì)算過程分配到不同的計(jì)算核心當(dāng)中。
這看起來是一件很簡單的事情,只需要把 DOM 樹的不同分支分開來,然后交給不同的核心即可。但實(shí)際上做起來卻比想象的更加困難,其中一個(gè)原因就是 DOM 樹通常是不均勻的。這會(huì)導(dǎo)致有些核心做的工作會(huì)比其它的做得多很多。
為了讓工作分配得更加均勻,Stylo 采用了一種稱為工作偷竊(Work Stealing)的技術(shù)。當(dāng)處理一個(gè) DOM 節(jié)點(diǎn)時(shí),運(yùn)行的代碼會(huì)把它的子節(jié)點(diǎn)分成一個(gè)或多個(gè)工作單元(Work Units)。這些工作單元會(huì)被添加到一個(gè)隊(duì)列中去。
當(dāng)某個(gè)核心把它的工作隊(duì)列都完成后,它會(huì)查看其它隊(duì)列中的工作單元,然后拿過來做。
這樣的話,我們就可以把工作分配得更加均勻,而又不需要花費(fèi)時(shí)間來遍歷 DOM 樹,也不需要事先就花費(fèi)時(shí)間來計(jì)算該如何均勻地分配工作。
在大多數(shù)的瀏覽器中,這種并行化工作做起來非常困難。總所周知,并行化是一塊難啃的硬骨頭,而且 CSS 引擎非常復(fù)雜。同時(shí), CSS 引擎還處于其他兩個(gè)最復(fù)雜部分: DOM 和布局的中間地帶。
因此,這會(huì)非常容易引入 BUG ,而且并行化也會(huì)導(dǎo)致非常難以追查的 BUG,叫做數(shù)據(jù)競爭(Data Races)。我在另一篇文章中詳細(xì)介紹了這種類型錯(cuò)誤,感興趣的可以參考下。
如果你接受來自成百上千名工程師的代碼貢獻(xiàn),你是如何做到平行編程而又無懼 BUG 的呢?這正是 Rust 的用武之地。
在 Rust 中,你可以通過靜態(tài)檢查的方式來避免數(shù)據(jù)競爭。也就是說,你可以直接在代碼中就避免這種難以調(diào)試的錯(cuò)誤。編譯器是不會(huì)讓你的代碼存在這樣的問題的。
使用 Rust ,CSS 樣式計(jì)算就變成了所謂的完美并行問題,因?yàn)槟慊旧喜挥米鍪裁淳蛯?shí)現(xiàn)了并行化。這意味著我們的這個(gè)優(yōu)化可以達(dá)到線性增長。如果你的機(jī)器有 4 個(gè)核心,那么你就擁有接近 4 被的性能增長。
規(guī)則樹對(duì)于每個(gè) DOM 節(jié)點(diǎn),CSS 引擎需要遍歷所有的樣式規(guī)則來完成選擇器匹配。
但是對(duì)于大多數(shù)節(jié)點(diǎn)來說,這種匹配規(guī)則并不是改變的太頻繁。
比如,如果用戶把光標(biāo)懸停在某個(gè)父元素上,那么匹配中該元素的樣式規(guī)則就可能改變了。我們也需要重新計(jì)算它的后代元素的樣式,以重新處理那些繼承屬性。當(dāng)然,匹配中這些后代元素的規(guī)則也可能是不變的。
如果我們能夠記錄哪些規(guī)則能夠匹配中這些后代元素,那是極好的,這樣我們就不需要對(duì)它們重新進(jìn)行選擇器匹配。這就是我們從 Firefox 上一代 CSS 引擎中借鑒過來的規(guī)則樹(Rule Tree)的原理。
CSS 引擎會(huì)經(jīng)歷選擇器進(jìn)行匹配的整個(gè)過程,然后按照特異性來排列它們,由此創(chuàng)建一個(gè)規(guī)則鏈表。
該鏈表會(huì)被添加到規(guī)則樹中。
CSS引擎會(huì)盡量使得規(guī)則樹的分支數(shù)量保持在最小值。為此,它會(huì)盡量復(fù)用已存在的規(guī)則分支。
如果鏈表中的選擇器與已存在的分支相同,那么它將順著相同的路徑往下走。不過它可能最終會(huì)走到一個(gè)下一個(gè)規(guī)則不同的節(jié)點(diǎn)處,只有這個(gè)時(shí)候引擎才會(huì)新增一個(gè)分支。
DOM 節(jié)點(diǎn)會(huì)取得指向這個(gè)規(guī)則最尾端節(jié)點(diǎn)的指針(這個(gè)例子中是 div#warning 規(guī)則)。而且,它的特異性最高的規(guī)則。
在樣式重算時(shí),CSS 引擎會(huì)進(jìn)行一項(xiàng)快速檢查,以判斷對(duì)父元素的變更是否會(huì)影響匹配中子元素的規(guī)則。如果不影響,那么對(duì)于任何后代節(jié)點(diǎn),引擎只需要順著后代節(jié)點(diǎn)保存的規(guī)則指針就可以找到對(duì)應(yīng)規(guī)則分支。在規(guī)則樹中,只要順著樹向上遍歷到根節(jié)點(diǎn)就可以獲取所有匹配的樣式規(guī)則。也就是說,CSS 引擎完全跳過了選擇器匹配和特異性排列過程。
這樣我們就減少了樣式重算過程的計(jì)算量。
雖然如此,但是在樣式初始化時(shí)還是會(huì)耗費(fèi)大量計(jì)算。假如有 10,000 個(gè)節(jié)點(diǎn),仍然需要進(jìn)行 10,000 次選擇器匹配。
不過,不用擔(dān)心,我們還有另一種方式來優(yōu)化它。
對(duì)于一個(gè)擁有成千上萬個(gè)節(jié)點(diǎn)的頁面,其中有許多節(jié)點(diǎn)都會(huì)匹配中相同的樣式規(guī)則。
舉例來說,對(duì)于一個(gè)很長的維基頁面,主要內(nèi)容區(qū)的段落應(yīng)該都是應(yīng)用相同的樣式規(guī)則,因此也就有相同的計(jì)算樣式。
如果這里不做優(yōu)化的話,那么 CSS 引擎必須對(duì)每個(gè)段落都進(jìn)行一次選擇器匹配和樣式計(jì)算。
但是如果有一種方式能證明這些不同段落使用的是相同樣式的話,那么引擎就只需要做一次計(jì)算即可,然后其他段落節(jié)點(diǎn)都指向相同的計(jì)算樣式。
這就是我們所說的樣式共享緩存(Style Sharing Cache),這種做法的靈感來自 Safari 和 Chrome 。
當(dāng)處理完一個(gè)節(jié)點(diǎn)之后,引擎會(huì)把計(jì)算樣式放進(jìn)緩存。然后,在開始計(jì)算下一個(gè)節(jié)點(diǎn)的樣式之前,引擎會(huì)做一些檢查來判斷是否可以使用已緩存的樣式。
這些檢查包括:
兩個(gè)節(jié)點(diǎn)是否有相同的 id、class 等?如果是,那么它們可以匹配中相同的樣式規(guī)則。
對(duì)于任何不是基于選擇器的樣式,比如內(nèi)聯(lián)樣式,節(jié)點(diǎn)具有相同的樣式值么?如果是,那么繼承自父節(jié)點(diǎn)的屬性不會(huì)被覆蓋,或者以相同的方式被覆蓋。
節(jié)點(diǎn)的父節(jié)點(diǎn)是否指向相同的計(jì)算樣式對(duì)象?如果是,那么繼承的樣式值則是一樣的。
從樣式共享緩存被提出的一開始,這些檢查就已經(jīng)應(yīng)用了。
不過,隨著 CSS 的發(fā)展,有許多其它小場景會(huì)導(dǎo)致樣式共享緩存的檢查方式失效。
比如,如果一個(gè) CSS 規(guī)則使用了 :first-child 選擇器,那么兩個(gè)段落元素時(shí)就可能會(huì)導(dǎo)致樣式不一致,即使上面的那些檢查都認(rèn)為它們是相同的。
在 WebKit 和 Blink 中,樣式共享緩存會(huì)忽略這些場景,并且不使用緩存。
隨著越來越多的網(wǎng)站使用現(xiàn)代選擇器,樣式共享緩存的優(yōu)化變得越來越雞肋,因此 Blink 團(tuán)隊(duì)最終還是把它移除了。
但是,事實(shí)證明樣式共享緩存還是有辦法跟上這些進(jìn)化節(jié)奏的。
在 Stylo 中,我們把記錄著所有這些現(xiàn)代選擇器并檢查他們是否能夠適用于 DOM 節(jié)點(diǎn)。然后,我們把檢查結(jié)果以 0 和 1 的方式存儲(chǔ)起來。如果兩個(gè)元素有相同的 0 和 1 ,那么我們就可以確定它們是匹配的。
如果一個(gè) DOM 節(jié)點(diǎn)可以使用已經(jīng)計(jì)算的樣式緩存,那么引擎就可以直接跳過大量的計(jì)算過程。由于頁面中經(jīng)常有大量的 DOM 節(jié)點(diǎn)擁有相同的樣式規(guī)則,所以樣式共享緩存不僅可以節(jié)省內(nèi)存,同時(shí)也能加快計(jì)算過程。
結(jié)論Stylo 是第一個(gè)從 Servo 遷移到 Firefox 的大型技術(shù)。
在這個(gè)過程中,我們已經(jīng)學(xué)到了很多關(guān)于如何把使用 Rust 編寫現(xiàn)代高性能代碼集成到 Firefox 核心。
事不宜遲,趕緊下載 Firefox ,體驗(yàn)極速吧!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/113102.html
摘要:現(xiàn)狀年月日,主流的四大瀏覽器達(dá)成了共識(shí)并宣布的最小可行產(chǎn)品已經(jīng)完成。更快的函數(shù)調(diào)用當(dāng)前,在中調(diào)用函數(shù)比想象的要慢。直接操作目前,沒有任何方式能夠操作。這就導(dǎo)致了部分應(yīng)用可能會(huì)因此而推遲發(fā)布時(shí)間。結(jié)束現(xiàn)如今已經(jīng)相當(dāng)快速。 本文是圖說 WebAssembly 系列文章的最后一篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 現(xiàn)狀 2017 年 2 月 28 日,主流的四大瀏覽器達(dá)成了共識(shí)...
摘要:編譯器優(yōu)缺點(diǎn)與解釋器相比,編譯器有著相反的優(yōu)缺點(diǎn)。它們?yōu)橐嫘略隽艘粋€(gè)組件,稱為監(jiān)視器,或者。優(yōu)化編譯器會(huì)基于監(jiān)視器記錄的代碼運(yùn)行信息來作出一些判斷。通常來說,優(yōu)化編譯器會(huì)使得代碼跑的更快。而這正是優(yōu)化編譯器所做的優(yōu)化之一。 本文是圖說 WebAssembly 系列文章的第二篇,如果你還沒閱讀其它的,建議您從第一篇開始。 JavaScript 的運(yùn)行,一開始是很慢的,但是后面會(huì)變得越來...
摘要:性能簡史在年,被創(chuàng)造出來時(shí)并不是沖著性能去的。而且在之后的十年發(fā)展中,它的性能一直是很低的。的引入成就了性能提升的一個(gè)轉(zhuǎn)折點(diǎn),其執(zhí)行速度比以往快了之多。性能提升也使得在全新的問題上使用成為可能。現(xiàn)在,極可能是下一個(gè)性能轉(zhuǎn)折點(diǎn)。 你可能已經(jīng)聽說 WebAssembly 代碼跑起來非常快。但是你知道這是為什么嗎?在本系列文章中,我們將探究其原因。 何為 WebAssembly WebAss...
摘要:瀏覽器內(nèi)核又叫渲染引擎,主要負(fù)責(zé)的解析,頁面布局渲染與復(fù)合層合成。頁面呈現(xiàn)原理規(guī)范定義了的詞法及語法文法。解析器使用和解析生成器從語法文件中自動(dòng)生成解析器。回憶一下解析器的介紹,創(chuàng)建一個(gè)自底向上的解析器,使用自頂向下解析器。 瀏覽器內(nèi)核又叫渲染引擎,主要負(fù)責(zé) HTML、CSS 的解析,頁面布局、渲染與復(fù)合層合成。瀏覽器內(nèi)核的不同帶來的主要問題是對(duì) CSS 的支持度與屬性表現(xiàn)差異。 we...
摘要:瀏覽器內(nèi)核又叫渲染引擎,主要負(fù)責(zé)的解析,頁面布局渲染與復(fù)合層合成。頁面呈現(xiàn)原理規(guī)范定義了的詞法及語法文法。解析器使用和解析生成器從語法文件中自動(dòng)生成解析器。回憶一下解析器的介紹,創(chuàng)建一個(gè)自底向上的解析器,使用自頂向下解析器。 瀏覽器內(nèi)核又叫渲染引擎,主要負(fù)責(zé) HTML、CSS 的解析,頁面布局、渲染與復(fù)合層合成。瀏覽器內(nèi)核的不同帶來的主要問題是對(duì) CSS 的支持度與屬性表現(xiàn)差異。 we...
閱讀 2921·2023-04-26 01:01
閱讀 3691·2021-11-23 09:51
閱讀 2523·2021-11-22 14:44
閱讀 3604·2021-09-23 11:57
閱讀 2841·2021-09-22 14:58
閱讀 5880·2021-09-10 11:25
閱讀 2110·2019-08-30 13:11
閱讀 1600·2019-08-30 12:59