我們正式開始學(xué)習(xí)之前,首先讓我們了解一些瀏覽器的一些知識點(diǎn),然后再對瀏覽器渲染原理深刻的理解一下,這次學(xué)習(xí)是針對Chrome瀏覽器的渲染機(jī)制。
最新的Chrome瀏覽器包括:1個瀏覽器主進(jìn)程,一個GPU進(jìn)程,一個網(wǎng)絡(luò)進(jìn)程,多個渲染進(jìn)程和多個插件進(jìn)程。
瀏覽器進(jìn)程:主要負(fù)責(zé)界面顯示、用戶交互、子進(jìn)程管理、同時提供存儲等功能
渲染進(jìn)程:核心任務(wù)將HTML、CSS和JavaScript轉(zhuǎn)化為用戶可以交互的網(wǎng)頁,排版引擎Blink和JavaScript引擎V8都是運(yùn)行在該線程。默認(rèn)情況下,Chrome瀏覽器為每個標(biāo)簽頁創(chuàng)建一個渲染進(jìn)程,但從一個頁面打開另一個頁面,而新頁面和當(dāng)前頁面屬于同一站點(diǎn)的話,那么新頁面會復(fù)用父頁面的渲染流程
GPU進(jìn)程:GPU的初衷是為了使用3D CSS效果,隨后網(wǎng)頁、Chrome的UI界面都采用了GPU來繪制,使得GPU成為瀏覽器普遍的需求,Chrome在其多進(jìn)程架構(gòu)上也引入了GPU進(jìn)程
網(wǎng)絡(luò)進(jìn)程:主要負(fù)責(zé)頁面的網(wǎng)絡(luò)資源加載
插件進(jìn)程:主要負(fù)責(zé)插件的運(yùn)行,因插件容易崩潰,所以需要通過插件進(jìn)程來隔離,保證插件進(jìn)程崩潰不會對瀏覽器和頁面造成影響。[1] [張2]
服務(wù)器響應(yīng)瀏覽器的HTML請求后,瀏覽器進(jìn)程開始準(zhǔn)備渲染流程,瀏覽器進(jìn)程解析HTML文件,構(gòu)建DOM樹
CSS解析
CSS樣式來源主要有三種:
1. 通過link或@import引入外部CSS
2. 標(biāo)記內(nèi)的CSS
3. 元素style的屬性內(nèi)嵌的CSS
解析CSS的順序是瀏覽器樣式>用戶自定義樣式>頁面link標(biāo)簽等引進(jìn)來的樣式>style標(biāo)簽的內(nèi)聯(lián)樣式
瀏覽器的渲染引擎接收到CSS文本時,將CSS文本轉(zhuǎn)換為瀏覽器可以理解的結(jié)構(gòu)styleSheets(CSSOM)
1. 創(chuàng)建布局樹
DOM樹中的所有可見節(jié)點(diǎn),并把這些節(jié)點(diǎn)加到布局中
忽略不可見節(jié)點(diǎn),比如:head標(biāo)簽,具有display:node樣式的元素
2. 布局計算
根據(jù)布局樹對各節(jié)點(diǎn)的幾何坐標(biāo)位置進(jìn)行計算,輸出帶有坐標(biāo)位置的布局樹
在生成布局樹之后不能直接繪制,渲染進(jìn)程會將一些復(fù)雜的3D動畫,滾動條,z-index層級高的生成圖層,并生成一顆圖層樹交給GPU加速渲染。
可以通過chrome開發(fā)工具,選擇more tool下的layers標(biāo)簽,來查看網(wǎng)頁的圖層狀態(tài)。
擁有層疊上下文對象的元素會被提升為單獨(dú)的一層。
文檔中層疊上下文滿足以下任意一個條件的元素形成:
1. 根元素(html)
2. z-index值不為auto的絕對/相對定位元素
3. fixed/sticky定位
4. z-index值不為auto的flex子項
5. opacity屬性值小于1的元素
6. transform、filter、perspective、clip-path、mask、mask-image、mask-border屬性值不為none的元素
7. isolation屬性被設(shè)置為isolate的元素
8. -webkit-overflow-scrolling屬性值為touch的元素
9. 在will-change中指定任意CSS屬性
10. contain屬性為layout、paint或者綜合值(如:strict、content)
在層疊上下文中,子元素同樣按照這個規(guī)則進(jìn)行層疊。重要的是,子級層疊上下文的z-idnex值只在父級中有意義,子級層疊上下文被自動視為父級層疊上下文的一個獨(dú)立單元。
需要剪裁(clip)的地方會被創(chuàng)建圖層
當(dāng)子元素需要展示的區(qū)域大于父元素的大小時,就會需要剪裁。出現(xiàn)剪裁情況的時候,渲染引擎回味文字部分單獨(dú)擦護(hù)功能鍵一個層,如果出現(xiàn)滾動條,滾動條也會被提升為單獨(dú)的層。
下面看這么一個案例:
總結(jié):元素有了層疊上下文屬性或者需要被剪裁,滿足任意一點(diǎn),就會提升為單獨(dú)一層。
渲染引擎會把一個圖層的繪制拆分成很多小繪制指令,然后把這些指令按照順序組成待繪制列表
柵格化的過程就是將圖塊轉(zhuǎn)化成位圖的過程。圖塊是柵格化的最小單位(通常為256×256或者512×512)。合成線程會按照視口附近的圖塊優(yōu)先生成位圖的原則將圖塊生成位圖,而實(shí)際生成位圖的操作是由柵格化來執(zhí)行的。渲染過程中借用GPU來加速生成,最后將生成的位圖保存到GPU內(nèi)存中。
圖塊被柵格化后完成后,合成線程會生成繪制圖塊的指令-DrawQuad并提交給瀏覽器進(jìn)程。瀏覽器進(jìn)程viz組件,用來接收DrawQuad指令,然后將頁面內(nèi)容會知道內(nèi)存中,最后將內(nèi)存數(shù)據(jù)顯示在屏幕上。
完整的渲染流程大致總結(jié)如下:
1.渲染引擎將HTML內(nèi)容轉(zhuǎn)換為DOM樹結(jié)構(gòu)
2.渲染引擎將CSS樣式表轉(zhuǎn)化為瀏覽器理解的styleSheets,計算DOM節(jié)點(diǎn)樣式
3.創(chuàng)建布局樹,計算元素的布局信息
4.對布局樹進(jìn)行分層,生成分層樹
5.對每個圖層生成繪制列表,并將其提交到合成線程
6.合成線程將圖層分成圖塊,并在柵格化線程池中將圖塊轉(zhuǎn)換成位圖
7.合成線程發(fā)送繪制圖塊命令DrawQuad給瀏覽器進(jìn)程
8.瀏覽器進(jìn)程根據(jù)DrawQuad生成頁面,顯示在顯示器上
[3] [張4]
1. CSS不會阻塞DOM解析,但會阻塞頁面渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
div {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
}
</style>
<link rel="stylesheet" href="./index.css" />
<script src="./index.js" defer></script>
</head>
<body>
</body>
</html>
//index.js
const div = document.querySelector("div");
console.log(div);
//index.css
div {
width: 200px;
height: 200px;
background: gray;
}
//server.js
const express = require("express");
const fs = require("fs");
const app = express();
app.get("/", (req, res) => {
fs.readFile("./index.html", (err, data) => {
res.send(data.toString());
});
});
app.get("/index.css", (req, res) => {
res.set("Content-Type", "text/css");
fs.readFile("./index.css", (err, data) => {
console.log(data);
setTimeout(() => {
res.send(data.toString());
}, 3000);
});
});
app.get("/index.js", (req, res) => {
fs.readFile("./index.js", (err, data) => {
res.send(data.toString());
});
});
app.listen(8080, (err) => {
if (err) {
console.log(err);
} else {
console.log("服務(wù)器啟動成功");
}
});
script標(biāo)簽上的defer屬性是通知瀏覽器該腳本在文檔完成解析后執(zhí)行,這樣在DOM解析后馬上能打印出來div。
通過結(jié)果看的出來,css文件內(nèi)容會延遲3秒后響應(yīng),通過效果看出來的是控制臺先打印的div標(biāo)簽,3秒后div藍(lán)色背景樣式出來,這就證明了css是不會阻塞DOM解析的。
CSS解析不會阻塞DOM解析,那我們認(rèn)為的應(yīng)該是div背景色的一個改變,從紅色變?yōu)榛疑切Ч莾H僅呈現(xiàn)了最終結(jié)果灰色,沒有變色這個過程,因此CSS是阻塞頁面渲染的。
2. JS阻塞DOM解析,瀏覽器遇到<script>標(biāo)簽時,會觸發(fā)頁面渲染
當(dāng)把scrpit標(biāo)簽的defer屬性刪除掉,我們延遲加載js文件,發(fā)現(xiàn)我們打印的結(jié)果是null,隨后頁面才會出現(xiàn)效果,這個效果說明JS阻塞了DOM解析。
現(xiàn)代瀏覽器會先提前下載script,link,src等標(biāo)簽的資源,不會等解析到標(biāo)簽的時候才下載。
當(dāng)我們把script標(biāo)簽和link標(biāo)簽放在div標(biāo)簽下面加載的時候,發(fā)現(xiàn)頁面會先展示紅色然后變成灰色的一個過程,瀏覽器在遇到script標(biāo)簽的時候渲染了一次頁面,加載完css文件后,由重新渲染了一次。這是因為瀏覽器不知道腳本內(nèi)容,因此碰到腳本內(nèi)容時,先渲染一遍頁面,確保腳本獲取到最新的DOM信息。
總結(jié):
1. CSS不會阻塞DOM解析,但會阻塞DOM渲染
2. JS阻塞DOM解析,但瀏覽器會預(yù)先下載相關(guān)資源
3. 瀏覽器遇到scirpt且沒有defer或async屬性的時,會觸發(fā)頁面渲染,如果前面CSS資源未加載完畢,瀏覽器會等待加載完成后執(zhí)行腳本
1. 重繪:元素樣式的改變(但寬高、大小、位置等不變)如:outline,visibility,color等
2. 回流(重排):元素的大小或者位置發(fā)生了變化(當(dāng)頁面布局和幾何信息發(fā)生變化的時候),觸發(fā)了重新布局,導(dǎo)致渲染樹重新計算布局和渲染。如:添加或刪除可見的DOM元素;元素的位置、尺寸發(fā)生變化,內(nèi)容發(fā)生變化(文本變化或圖片被另一個尺寸圖片替代)
注意:回流一定會觸發(fā)重繪,重繪不一定發(fā)生回流,回流比重繪會更消耗性能,他們會導(dǎo)致web應(yīng)用的UI反映遲鈍。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
#box {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
let box = document.getElementById("box");
/*
分離讀寫:
box.style.width = "200px";
console.log(box.clientHeight) 這一步會修改渲染機(jī)制,會造成兩次回流
box.style.height = "200px";
box.style.margin = "200px";
現(xiàn)代瀏覽器有渲染等待機(jī)制,會觸發(fā)一次回流(重排),老版瀏覽器會觸發(fā)三次回流(重排)
*/
/*
批量處理(樣式集中改變)
box.style.cssText="width:20px,height:20px"
box.className="box"
*/
/*
緩存布局信息
因為有讀操作box.clientWidth和box.clientHeight 會觸發(fā)兩次回流
box.style.width = box.clientWidth+10+'px'
box.style.heght = box.clientHeight+10+'px'
實(shí)現(xiàn)(本質(zhì):分離讀寫):
a=box.clientWidth b=box.clientHeight
box.style.width = a+10+'px'
box.style.heght = b+10+'px'
*/
/*
元素批量修改
1.文檔碎片
let frg = document.createDocumentFragment()
for (let i = 0; i < 5; i++) {
let newLi = document.createElement("li");
newLi.innerHTML = i;
//box.appendChild(frg); 觸發(fā)5次回流
frg.appendChild(newLi);
}
// 一次性把內(nèi)容放到容器中,觸發(fā)一次回流
box.appendChild(frg);
frg = null;
2. 字符串拼接
let str=''
for (let i = 0; i < 5; i++) {
str+= '<li>i</li>'
}
box.innerHTML(str); //觸發(fā)一次回流
str = ''
*/
/*
動畫效果應(yīng)用到postion屬性為absolute或fixed的元素(脫離文檔流,防止影響其他元素)
*/
/*
CSS3硬件加速(硬件加速規(guī)避回流)
使用CSS樣式:transform、opacity等這些屬性會觸發(fā)硬件加速,不會引入回流和重繪
box.style.left = '100px'//觸發(fā)一次回流
box.style.transform='translateX('200px')'
*/
/*
犧牲平滑度換取速度
每次1像素移動動畫,如果動畫使用100%CPU,動畫就會看上去跳動的,瀏覽器不斷的在更新回流
每次移動3像素效果的話平滑度降低了,但不會導(dǎo)致在CPU較慢的機(jī)器中抖動
*/
/*
避免table布局和使用CSS的javascript表達(dá)式(盡可能不使用table)
*/
</script>
</body>
</html>
[5] [張6] 我們可以通過以下幾種方式來避免DOM的回流
1. 放棄傳統(tǒng)操作DOM,基于vue、react數(shù)據(jù)影響視圖模數(shù)
2. 分離讀寫操作(現(xiàn)代瀏覽器都有渲染隊列的機(jī)制)
3. 樣式集中改變
4. 緩存布局信息
5. 元素批量修改
6. 動畫效果應(yīng)用到position屬性為absolute或fixed的元素(脫離文檔流)
7. CSS3硬件加速
8. 犧牲平滑度換取速度
9. 避免table布局
web前端之二叉搜索樹
天我們將利用vue的條件指令來完成一個簡易的動態(tài)變色功能按鈕
首先我們還是要對vue進(jìn)行配置.
然后我們要在html頁面上搭建三個簡易的按鈕,顏色分別為紫,綠和藍(lán)(顏色隨意)其代碼如下:
<body>
<div id="app">
<p>
<button @click="btn_click('pg1')" :style="{background:'rebeccapurple'}">紫</button>
<button @click="btn_click('pg2')" :style="{background:'yellowgreen'}">綠</button>
<button @click="btn_click('pg3')" :style="{background:'cornflowerblue'}">藍(lán)</button>
</p>
</div>
</body>
這里的@是v-on事件指令,在這里要在三個按鈕上設(shè)置點(diǎn)擊事件
接著我們要進(jìn)行條件指令的判斷,其代碼如下:
<div class="box pg1" v-if="pg == 'pg1'" key="pg1"></div>
<div class="box pg2" v-else-if="pg == 'pg2'" key="pg2"></div>
<div class="box pg3" v-else key="pg3"></div>
通過條件來判斷點(diǎn)擊不同的按鈕觸發(fā)不同的效果
接下來我們將進(jìn)行掛載點(diǎn),事件的渲染以及為事件提供實(shí)現(xiàn)體操作
<script>
new Vue({
el: '#app',
data: {
pg: 'pg1' },
methods: {
btn_click: function (pg_num) {
this.pg = pg_num
}
}
})
</script>
這里我們設(shè)置進(jìn)入頁面后默認(rèn)顯示第一個按鈕顯示的圖片,通過點(diǎn)擊來完成事件的轉(zhuǎn)換.
最后就是把圖片給設(shè)置出來啦
<style>
.box {
width: 200px;
height: 100px;
background-color: darkgray;
}
.pg1 { background-color: rebeccapurple; }
.pg2 { background-color: yellowgreen; }
.pg3 { background-color: cornflowerblue; }
</style>
當(dāng)然也可以選擇你喜歡的圖片進(jìn)行放置,這里我們只是放置顏色填充的框
具體的實(shí)現(xiàn)效果如下:
通過不同的點(diǎn)擊來獲得不同的圖片!
整體代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.box {
width: 200px;
height: 100px;
background-color: darkgray;
}
.pg1 { background-color: rebeccapurple; }
.pg2 { background-color: yellowgreen; }
.pg3 { background-color: cornflowerblue; }
</style>
</head>
<body>
<div id="app">
<p v-if="is_if" key="my_if">v-if的顯示與隱藏</p>
<p v-show="is_show" key="my_show">v-show的顯示與隱藏</p>
<p>
<button @click="btn_click('pg1')" :style="{background:'rebeccapurple'}">紫</button>
<button @click="btn_click('pg2')" :style="{background:'yellowgreen'}">綠</button>
<button @click="btn_click('pg3')" :style="{background:'cornflowerblue'}">藍(lán)</button>
<div class="box pg1" v-if="pg == 'pg1'" key="pg1"></div>
<div class="box pg2" v-else-if="pg == 'pg2'" key="pg2"></div>
<div class="box pg3" v-else key="pg3"></div>
</p>
</div>
</body>
<script src="js/vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
is_if: false,
is_show: true,
pg: 'pg1'
},
methods: {
btn_click: function (pg_num) {
this.pg = pg_num
}
}
})
</script>
</html>
“我們相信人人都可以成為一個WEB前端開發(fā)大神,現(xiàn)在開始,找個師兄,帶你入門,學(xué)習(xí)的路上不再迷茫。這里是WEB前端開發(fā)修真院,初學(xué)者轉(zhuǎn)行到互聯(lián)網(wǎng)行業(yè)的聚集地。"
大師兄送你套WEB前端入門心法
限時免費(fèi)獲取方式
▼
領(lǐng)取方法:
關(guān)注“速學(xué)編程” 然后私信回復(fù)“前端”即可
私信不要多字,不要少字,不要錯字,私信方法:點(diǎn)擊我頭像,進(jìn)入主頁面,右上角有私信功能,在關(guān)注的上方位置。
如果對您有幫助請記得給速學(xué)編程先來個“評論+轉(zhuǎn)發(fā)”
到題目是不是嚇了一跳?css竟然還有這個操作?還真是第一次聽說~
原理嘛,其實(shí)很簡單的,用到的就是 CSS3 濾鏡filter中的drop-shadow,該濾鏡可以給圖片非透明區(qū)域添加投影。你可以理解為下圖
它實(shí)現(xiàn)的效果看上去就像使原來的對象離開頁面,然后在頁面上顯示出該對象的投影。是有一點(diǎn)類似box-shadow,但是二者還是有顯著差別的,我后面會寫一篇專門的文章來比較二者的差別。
先來看一下語法:
filter:drop-shadow(水平陰影偏移距離 垂直陰影偏移距離 投射的陰影顏色 );
我們準(zhǔn)備一張背景色是透明的圖片(圖片尺寸40px X 40px),
用一個div將該圖片包裹住,給圖片添加
filter: drop-shadow(40px 40px yellow)
這段代碼,代表投射出一個和該圖片一樣的形狀,
三個參數(shù)分別代表:
水平向右移動40px,
垂直向下移動40px,
投射出的形狀顏色為黃色。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
.box{
width: 40px;
height: 40px;
border: 1px solid red;
margin: 200px auto;
}
.pic{
filter: drop-shadow(40px 40px yellow);
}
</style>
</head>
<body>
<div class="box">
<img src="img/delete.png" class="pic"/>
</div>
</body>
</html>
效果為
接下來我們稍微更改一下原代碼,將原圖設(shè)置在div外部并隱藏,變色后的投影放置在div中
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
.box{
width: 40px;
height: 40px;
border: 1px solid red;
margin: 200px auto;
text-indent: -40px;
overflow: hidden;
}
.pic{
filter: drop-shadow(40px 0px yellow);
}
</style>
</head>
<body>
<div class="box">
<img src="img/delete.png" class="pic"/>
</div>
</body>
</html>
如果想換成其他顏色,直接更改第三個參數(shù)就Ok了~是不是很簡單
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。