瀏覽器模型

1.代碼嵌入網頁的方法
1.1script 元素嵌入代碼
1.2script 元素加載外部腳本
1.3事件屬性
1.4URL 協議
2.script 元素
2.1工做原理
2.2defer 屬性 同步下載生成dom後執行按順序
2.3async 屬性 同步下載直接中斷開始執行不按順序
2.4腳本的動態加載 生成dom後不按順序執行 能夠設async false後按順序執行
2.5加載使用的協議
3.瀏覽器的組成
3.1渲染引擎
3.2重流和重繪
3.3JavaScript 引擎javascript

1.代碼嵌入網頁的方法

網頁中嵌入 JavaScript 代碼,主要有三種方法。css

  • <script>元素直接嵌入代碼。
  • <script>標籤加載外部腳本
  • 事件屬性
  • URL 協議

1.1script 元素嵌入代碼

1.1.1 <script>標籤有一個type屬性,用來指定腳本類型。對 JavaScript 腳原本說,type屬性能夠設爲兩種值。html

text/javascript:這是默認值,也是歷史上一向設定的值。若是你省略type屬性,默認就是這個值。對於老式瀏覽器,設爲這個值比較好。
application/javascript:對於較新的瀏覽器,建議設爲這個值。
'
<script type="application/javascript">
console.log('Hello World');
</script>
'java

若是type屬性的值,瀏覽器不認識,那麼它不會執行其中的代碼。可是,這個<script>節點依然存在於 DOM 之中,可使用<script>節點的text屬性讀出它的內容。
'
<script id="mydata" type="x-custom-data">
console.log('Hello World');
</script>
'jquery

1.2script 元素加載外部腳本

若是腳本文件使用了非英語字符,還應該註明字符的編碼。
爲了防止攻擊者篡改外部腳本,script標籤容許設置一個integrity屬性,寫入該外部腳本的 Hash 簽名,用來驗證腳本的一致性
'
<script src="/assets/application.js"
integrity="sha256-TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs=" charset="utf-8">
</script>
'瀏覽器

1.3事件屬性

網頁元素的事件屬性(好比onclick和onmouseover),能夠寫入 JavaScript 代碼
'
<button id="myBtn" onclick="console.log(this.id)">點擊</button
'緩存

1.4URL 協議

URL 支持javascript:協議,即在 URL 的位置寫入代碼,使用這個 URL 的時候就會執行 JavaScript 代碼。沒有返回值,不進行跳轉
'
點擊
//瀏覽器的地址欄也能夠執行javascript:協議。將javascript:console.log('Hello')放入地址欄,按回車鍵也會執行這段代碼。不進行跳轉
'服務器

若是 JavaScript 代碼返回一個字符串,瀏覽器就會新建一個文檔,展現這個字符串的內容,原有文檔的內容都會消失。app

點擊
上面代碼中,用戶點擊連接之後,會打開一個新文檔,裏面有當前時間。dom

javascript:協議的常見用途是書籤腳本 Bookmarklet。因爲瀏覽器的書籤保存的是一個網址,因此javascript:網址也能夠保存在裏面,用戶選擇這個書籤的時候,就會在當前頁面執行這個腳本。爲了防止書籤替換掉當前文檔,能夠在腳本前加上void,或者在腳本最後加上void 0。
'
點擊
點擊
'

Void執行 但不返回值
上面這兩種寫法,點擊連接後,執行代碼都不會網頁跳轉

2.script 元素

2.1工做原理

1.html一邊下載一邊解析
2.遇到script標籤,中止解析,把網頁渲染的控制權轉交給 JavaScript 引擎
3.內部的js直接執行,外部的js下載和執行js代碼.多個js文件同時下載,按順序執行。若是有css,css在這以前下載解析,或者在js遇到css,停下後去解析css
3.執行完成後,繼續html下載解析。

加載外部腳本時,瀏覽器會暫停頁面渲染,等待腳本下載並執行完成後,再繼續渲染。緣由是 JavaScript 代碼能夠修改 DOM,因此必須把控制權讓給它,不然會致使複雜的線程競賽的問題。

解析和執行 CSS,也會產生阻塞。Firefox 瀏覽器會等到腳本前面的全部樣式表,都下載並解析完,再執行腳本;Webkit則是一旦發現腳本引用了樣式,就會暫停執行腳本,等到樣式表下載並解析完,再恢復執行。

此外,對於來自同一個域名的資源,好比腳本文件、樣式表文件、圖片文件等,瀏覽器通常有限制,同時最多下載6~20個資源,即最多同時打開的 TCP 鏈接有限制,這是爲了防止對服務器形成太大壓力。若是是來自不一樣域名的資源,就沒有這個限制。因此,一般把靜態文件放在不一樣的域名之下,以加快下載速度。

解決js在dom結構生成以前調用報錯,能夠把script標籤放在頁面最後。

另外一種解決方法是設定DOMContentLoaded事件的回調函數。

<head>
<script>

document.addEventListener(
  'DOMContentLoaded',
  function (event) {
    console.log(document.body.innerHTML);
  }
);

</script>
</head>

另外一種解決方法是,使用<script>標籤的onload屬性。當<script>標籤指定的外部腳本文件下載和解析完成,會觸發一個load事件,能夠把所需執行的代碼,放在這個事件的回調函數裏面。

<script src="jquery.min.js" onload="console.log(document.body.innerHTML)">
</script>
上面代碼中,指定DOMContentLoaded事件發生後,纔開始執行相關代碼。DOMContentLoaded事件只有在 DOM 結構生成以後纔會觸發

爲了解決腳本文件下載阻塞網頁渲染的問題,有了defer和async屬性,區別在defer在dom加載完成之後按順序執行,async直接不按順序執行。

2.2defer 屬性

defer屬性的運行流程以下。

瀏覽器開始解析 HTML 網頁。
1.解析過程當中,發現帶有defer屬性的<script>元素。
2.瀏覽器繼續往下解析 HTML 網頁,同時並行下載<script>元素加載的外部腳本。
3.瀏覽器完成解析 HTML 網頁,此時再回過頭執行已經下載完成的腳本。按順序執行

對於內置而不是加載外部腳本的script標籤,以及動態生成的script標籤,defer屬性不起做用。另外,使用defer加載的外部腳本不該該使用document.write方法

2.3async 屬性

1.瀏覽器開始解析 HTML 網頁。
2.解析過程當中,發現帶有async屬性的script標籤。
3.瀏覽器繼續往下解析 HTML 網頁,同時並行下載<script>標籤中的外部腳本。
4.腳本下載完成,瀏覽器暫停解析 HTML 網頁,開始執行下載的腳本。哪一個先下載完先執行
5.腳本執行完畢,瀏覽器恢復解析 HTML 網頁。

不該該使用document.write方法

defer屬性和async屬性到底應該使用哪個?

通常來講,若是腳本之間沒有依賴關係,就使用async屬性,若是腳本之間有依賴關係,就使用defer屬性。若是同時使用async和defer屬性,後者不起做用,瀏覽器行爲由async屬性決定

2.4腳本的動態加載(不按順序)

動態生成的script標籤不會阻塞頁面渲染,也就不會形成瀏覽器假死。可是問題在於,這種方法沒法保證腳本的執行順序,哪一個腳本文件先下載完成,就先執行哪一個。

若是想避免這個問題,能夠設置async屬性爲false。
'
['a.js', 'b.js'].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
'
須要注意的是,在這段代碼後面加載的腳本文件,會所以都等待b.js執行完成後再執行

2.5加載使用的協議

若是不指定協議,瀏覽器默認採用 HTTP 協議下載

若是要採用 HTTPS 協議下載,必需寫明。

<script src="https://example.js&quot;></script>
根據頁面自己的協議來決定加載協議,這時能夠採用下面的寫法。

<script src="//example.js"></script>

3.瀏覽器的組成

瀏覽器的核心是兩部分:渲染引擎和 JavaScript 解釋器(又稱 JavaScript 引擎)。

3.1渲染引擎

將網頁代碼渲染爲用戶視覺能夠感知的平面文檔

Firefox:gecko 引擎
safari:WebKit 引擎
Chrome:Blink 引擎
IE: Trident 引擎
Edge: EdgeHTML 引擎

渲染引擎處理網頁,一般分紅四個階段
1.解析代碼:html解析dom css解析爲cssom
2.對象合成:合成dom和cssom爲渲染renderr tree
3.佈局:計算渲染樹佈局layout
4.繪製:將渲染樹繪製到屏幕

每每第一步還沒完成,第二步和第三步就已經開始

3.2重流和重繪

渲染樹轉換爲網頁佈局,稱爲「佈局流」(flow);佈局顯示到頁面的這個過程,稱爲「繪製」(paint)

做爲開發者,應該儘可能設法下降重繪的次數和成本。好比,儘可能不要變更高層的 DOM 元素,而以底層 DOM 元素的變更代替;再好比,重繪table佈局和flex佈局,開銷都會比較大。

優化技巧。

讀取 DOM 或者寫入 DOM,儘可能寫在一塊兒,不要混雜。不要讀取一個 DOM 節點,而後馬上寫入,接着再讀取一個 DOM 節點。
緩存 DOM 信息。
不要一項一項地改變樣式,而是使用 CSS class 一次性改變樣式。
使用documentFragment操做 DOM
動畫使用absolute定位或fixed定位,這樣能夠減小對其餘元素的影響。
只在必要時才顯示隱藏元素。
使用window.requestAnimationFrame(),由於它能夠把代碼推遲到下一次重流時執行,而不是當即要求頁面重流。
使用虛擬 DOM(virtual DOM)庫
下面是一個window.requestAnimationFrame()對比效果的例子。
'
// 重繪代價高
function doubleHeight(element) {
var currentHeight = element.clientHeight;
element.style.height = (currentHeight * 2) + 'px';
}

all_my_elements.forEach(doubleHeight);

// 重繪代價低
function doubleHeight(element) {
var currentHeight = element.clientHeight;

window.requestAnimationFrame(function () {

element.style.height = (currentHeight * 2) + 'px';

});
}

all_my_elements.forEach(doubleHeight);

'
上面的第一段代碼,每讀一次 DOM,就寫入新的值,會形成不停的重排和重流。第二段代碼把全部的寫操做,都累積在一塊兒,從而 DOM 代碼變更的代價就最小化了

3.3JavaScript 引擎

JavaScript 引擎的主要做用是,讀取網頁中的 JavaScript 代碼,對其處理後運行

不須要編譯,由解釋器實時運行。這樣的好處是運行和修改都比較方便,刷新頁面就能夠從新解釋;缺點是每次運行都要調用解釋器,系統開銷較大,運行速度慢於編譯型語言
下面是目前最多見的一些 JavaScript 虛擬機:

Chakra (Microsoft Internet Explorer)Nitro/JavaScript Core (Safari)Carakan (Opera)SpiderMonkey (Firefox)V8 (Chrome, Chromium)

相關文章
相關標籤/搜索