整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          瀏覽器渲染原理

          我們正式開始學(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]

          1. 構(gòu)建DOM樹

          服務(wù)器響應(yīng)瀏覽器的HTML請求后,瀏覽器進(jìn)程開始準(zhǔn)備渲染流程,瀏覽器進(jìn)程解析HTML文件,構(gòu)建DOM樹

          2. 樣式計算

          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)

          3. 布局階段

          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)位置的布局樹

          4. 分層(layer)

          在生成布局樹之后不能直接繪制,渲染進(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ú)一層。

          5. 圖層繪制:

          渲染引擎會把一個圖層的繪制拆分成很多小繪制指令,然后把這些指令按照順序組成待繪制列表

          6.柵格化(光柵化):

          柵格化的過程就是將圖塊轉(zhuǎn)化成位圖的過程。圖塊是柵格化的最小單位(通常為256×256或者512×512)。合成線程會按照視口附近的圖塊優(yōu)先生成位圖的原則將圖塊生成位圖,而實(shí)際生成位圖的操作是由柵格化來執(zhí)行的。渲染過程中借用GPU來加速生成,最后將生成的位圖保存到GPU內(nèi)存中。

          7. 合成:

          圖塊被柵格化后完成后,合成線程會生成繪制圖塊的指令-DrawQuad并提交給瀏覽器進(jìn)程。瀏覽器進(jìn)程viz組件,用來接收DrawQuad指令,然后將頁面內(nèi)容會知道內(nèi)存中,最后將內(nèi)存數(shù)據(jù)顯示在屏幕上。

          8.總結(jié)

          完整的渲染流程大致總結(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]

          CSS解析和JS解析

          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í)行腳本

          DOM的重繪和回流(Repaint&Reflow)

          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了~是不是很簡單


          主站蜘蛛池模板: 精品国产AⅤ一区二区三区4区| 无码中文字幕人妻在线一区二区三区| 日韩精品一区二区三区不卡| 亚洲AV无码一区二区三区久久精品| 亚洲av色香蕉一区二区三区蜜桃| 国产三级一区二区三区| 高清一区二区三区视频| 成人午夜视频精品一区| 日韩一本之道一区中文字幕| 无码喷水一区二区浪潮AV| 无码中文字幕人妻在线一区二区三区| 成人免费一区二区无码视频| 人妻在线无码一区二区三区| 欧美日本精品一区二区三区 | 国产午夜精品一区二区三区漫画 | 无码国产精品一区二区免费模式| 久久青草国产精品一区| 亚洲无删减国产精品一区| 国产福利一区二区三区在线观看| 国产成人欧美一区二区三区| 日韩精品一区二区三区国语自制| 国产在线观看91精品一区| 内射白浆一区二区在线观看| 精品一区二区久久久久久久网精| 国产无吗一区二区三区在线欢 | 精品无码一区二区三区爱欲| 久久久久成人精品一区二区| 日韩精品区一区二区三VR| 精品国产一区二区三区无码| 亚洲av无码一区二区三区四区| 日本一区二区高清不卡| 国产成人精品一区二区三区免费 | 在线日产精品一区| 国产三级一区二区三区| 精品国产一区二区三区免费| 人体内射精一区二区三区| 中文字幕日韩欧美一区二区三区| 国产激情无码一区二区三区| 亚洲狠狠狠一区二区三区| 无码人妻精品一区二区蜜桃百度| 日本一区二区三区精品中文字幕|