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