Web前端入門必知——瀏覽器基礎知識

瀏覽器的主要功能:javascript

是將用戶選擇的web資源呈現出來,它須要從服務器請求資源,並將其顯示在瀏覽器窗口中,資源的格式一般是HTML,也包括PDF、image及其餘格式。用戶用URI(Uniform Resource Identifier統一資源標識符)來指定所請求資源的位置。css

瀏覽器的主要組件包括:html

  1. 用戶界面 - 包括地址欄、後退/前進按鈕、書籤目錄等,也就是你所看到的除了用來顯示你所請求頁面的主窗口以外的其餘部分。前端

  2. 瀏覽器引擎 - 用來查詢及操做渲染引擎的接口。html5

  3. 渲染引擎 - 用來顯示請求的內容,例如,若是請求內容爲html,它負責解析html及css,並將解析後的結果顯示出來。java

  4. 網絡 - 用來完成網絡調用,例如http請求,它具備平臺無關的接口,能夠在不一樣平臺上工做。jquery

  5. UI後端 - 用來繪製相似組合選擇框及對話框等基本組件,具備不特定於某個平臺的通用接口,底層使用操做系統的用戶接口。css3

  6. JS解釋器 - 用來解釋執行JS代碼。web

  7. 數據存儲 - 屬於持久層,瀏覽器須要在硬盤中保存相似cookie的各類數據,HTML5定義了web database技術,這是一種輕量級完整的客戶端存儲技術。面試


1. 瀏覽器輸入URL到顯示頁面發生了什麼?

老問題,你們面試的時候應該都被問過這種問題,網上的答案千篇一概,咱們來更深刻的瞭解一下。

1.1 在瀏覽器中輸入url

用戶輸入url,例如http://www.feng.com。其中http爲協議,www.feng.com爲網絡地址,及指出須要的資源在哪臺計算機上。通常網絡地址能夠爲域名或IP地址,此處爲域名。使用域名是爲了方便記憶,一串數字哦咱們很容易會記錯,可是爲了讓計算機理解這個地址還須要把它解析爲IP地址。

1.2 查看瀏覽器緩存

若是訪問過該url,會先進入瀏覽器緩存中查詢是否有要請求的文件(瀏覽器緩存是在本地保存資源副本)。

當瀏覽器發現請求的資源已經在瀏覽器緩存中存有副本,它會攔截請求,返回該資源的副本,並直接結束請求,而不會再去源服務器從新下載。若是緩存查找失敗,就會進入網絡請求過程了。

在network中會標註該請求是在服務器中請求的仍是瀏覽器緩存中的。

一條域名的DNS記錄會在本地有兩種緩存:瀏覽器緩存和操做系統(OS)緩存。

1.2.1 瀏覽器緩存 – 瀏覽器會緩存DNS記錄一段時間。通常是2分鐘到30分鐘不等。查找瀏覽器緩存時會按順序查找: Service Worker-->Memory Cache-->Disk Cache-->Push Cache。

Service Worker:

是運行在瀏覽器背後的獨立線程,通常能夠用來實現緩存功能。使用 Service Worker的話,傳輸協議必須爲 HTTPS。由於 Service Worker 中涉及到請求攔截,因此必須使用 HTTPS 協議來保障安全。Service Worker 的緩存與瀏覽器其餘內建的緩存機制不一樣,它可讓咱們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,而且緩存是持續性的。

Memory Cache:

內存中的緩存,主要包含的是當前中頁面中已經抓取到的資源,例如頁面上已經下載的樣式、腳本、圖片等。讀取內存中的數據確定比磁盤快,內存緩存雖然讀取高效,但是緩存持續性很短,會隨着進程的釋放而釋放。一旦咱們關閉 Tab 頁面,內存中的緩存也就被釋放了。

Disk Cache:

存儲在硬盤中的緩存,讀取速度慢點,可是什麼都能存儲到磁盤中,比之 Memory Cache 勝在容量和存儲時效性上。

在全部瀏覽器緩存中,Disk Cache 覆蓋面基本是最大的。它會根據 HTTP Herder 中的字段判斷哪些資源須要緩存,哪些資源能夠不請求直接使用,哪些資源已通過期須要從新請求。而且即便在跨站點的狀況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數據。絕大部分的緩存都來自 Disk Cache。

Push Cache:

Push Cache(推送緩存)是 HTTP/2 中的內容,當以上三種緩存都沒有命中時,它纔會被使用。它只在會話(Session)中存在,一旦會話結束就被釋放,而且緩存時間也很短暫,在Chrome瀏覽器中只有5分鐘左右,同時它也並不是嚴格執行HTTP頭中的緩存指令。

1.2.2系統緩存 – 若是在瀏覽器緩存裏沒有找到須要的記錄,瀏覽器會作一個系統調用得到系統緩存中的記錄(windows裏是gethostbyname)。

1.2.3 路由器緩存** – 接着,前面的查詢請求發向路由器,它通常會有本身的DNS緩存。

1.2.4 ISP DNS 緩存** – 接下來要check的就是ISP緩存DNS的服務器。在這通常都能找到相應的緩存記錄。

1.2.5 遞歸搜索** – 你的ISP的DNS服務器從跟域名服務器開始進行遞歸搜索,從.com頂級域名服務器到Facebook的域名服務器。通常DNS服務器的緩存中會有.com域名服務器中的域名,因此到頂級服務器的匹配過程不是那麼必要了。

1.3 DNS域名解析

若是沒有訪問過該url,就會進行DNS域名解析了。

IP地址和域名同樣都是用來作網絡標識的,域名和 IP 地址是一一對應的映射關係。

DNS:Domain Name System域名系統(基於RFC規範解釋),是萬維網上做爲域名和IP地址相互映射的一個分佈式數據庫,可以使用戶更方便的訪問互聯網,而不用去記住可以被機器直接讀取的IP數串。

DNS解析過程:

1.3.1 用戶主機上運行着DNS的客戶端,就是咱們的PC機或者手機客戶端運行着DNS客戶端。

1.3.2 瀏覽器將接收到的url中抽取出域名字段,就是訪問的主機名,好比www.feng.com, 並將這個主機名傳送給DNS應用的客戶端.

1.3.3 DNS客戶機端向DNS服務器端發送一份查詢報文,報文中包含着要訪問的主機名字段(中間包括一些列緩存查詢以及分佈式DNS集羣的工做)。

1.3.4 該DNS客戶機最終會收到一份回答報文,其中包含有該主機名對應的IP地址。

1.3.5 一旦該瀏覽器收到來自DNS的IP地址,就能夠向該IP地址定位的HTTP服務器發起TCP鏈接。

1.4 獲取端口號

可能域名下有多個端口號,對應着不一樣的網絡功能,因此在DNS解析以後,瀏覽器還會獲取端口號。

1.5 創建TCP鏈接

TCP鏈接,就是耳熟能詳的三次握手好朋友,四次揮手是路人。

TCP鏈接過程:

1.5.1 服務端經過socket,bind和listen準備好接受外來的鏈接,此時服務端狀態爲Listen。

1.5.2 客戶端經過調用connect來發起主動鏈接,致使客戶端TCP發送一個SYN(同步)字節,告訴服務器客戶將在(待創建的)鏈接中發送的數據的初始序列號,客戶端狀態爲SYN_SENT。

1.5.3 服務器確認(ACK)客戶的SYN,並本身也發送一個SYN,它包含服務器將在同一鏈接中發送數據的初始序列號。

1.5.4 客戶端確認服務的ACK和SYN,向服務器發送ACK,客戶端狀態ESTABLISHED。

1.5.5 服務器接收ACK,服務器狀態ESABLISHED。

1.6 HTTP請求

既然咱們握手成功了,鏈接到了Web服務器,瀏覽器會根據解析到的IP地址和端口號發起HTTP請求。

1.6.1 http協議向服務器發送請求,發送請求的過程當中,瀏覽器會向Web服務器以Stream(流)的形式傳輸數據,告訴Web服務器要訪問服務器裏面的哪一個Web應用下的Web資源。

1.6.2 服務器接收到瀏覽器傳輸的數據後,開始解析接收到的數據,服務器解析請求裏面的內容時知道客戶端瀏覽器要訪問的是應用裏面的哪這個Web資源,而後服務器就去讀取這個Web資源裏面的內容,將讀到的內容再以Stream(流)的形式傳輸給瀏覽器。

1.7 關閉TCP

TCP鏈接停止過程:

1.7.1 某端首先調用close,成爲主動關閉端,向另外一端發送FIN分節,表示數據發送完畢,此時主動關閉端狀態FIN_WAIT_1;

1.7.2 接收到FIN的是被動關閉端,FIN由TCP確認,先向主動關閉端發送ACK,做爲一個文件結束符傳遞給接收端應用進程(放在已排隊等候該應用進程接收到的任何其餘數據以後),由於FIN的接收意味着接收端應用進程在相應鏈接無額外數據可接收,接收端狀態CLOSE_WAIT;主動關閉端接收到ACK狀態變爲FIN_WAIT_2;

1.7.3 一段時間後,接收端接收到這個文件結束符的應用進程調用close關閉套接字,向主動關閉端發送FIN,接收端狀態爲LAST_ACK;

1.7.4 主動關閉端確認FIN,狀態變爲TIME_WAIT,並向接收端發送ACK,接收端接收到ACK關閉TCP,而主動關閉端一段時間後也關閉TCP;

1.8 瀏覽器渲染

當瀏覽器得到一個html文件時,會自上而下的加載,並在加載過程當中進行解析渲染。

解析:

1. 瀏覽器會將HTML解析成一個DOM樹,DOM 樹的構建過程是一個深度遍歷過程:當前節點的全部子節點都構建好後纔會去構建當前節點的下一個兄弟節點。

2. 將CSS解析成 CSS Rule Tree 。

3. 根據DOM樹和CSSOM來構造 Rendering Tree。注意:Rendering Tree 渲染樹並不等同於 DOM 樹,由於一些像 Header 或 display:none 的東西就不必放在渲染樹中了。

4. 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關係。下一步操做稱之爲Layout,顧名思義就是計算出每一個節點在屏幕中的位置。

  1. 再下一步就是繪製,即遍歷render樹,並使用UI後端層繪製每一個節點

渲染:

1. 接收服務器返回html文件。

2. 瀏覽器開始載入html代碼,發現<head>標籤內有一個<link>標籤引用外部CSS文件,瀏覽器又發出CSS文件的請求,服務器返回這個CSS文件。

3. 瀏覽器繼續載入html中<body>部分的代碼,而且CSS文件已經拿到手了,能夠開始渲染頁面了。

4. 瀏覽器在代碼中發現一個<img>標籤引用了一張圖片,向服務器發出請求。此時瀏覽器不會等到圖片下載完,而是繼續渲染後面的代碼。

5. 服務器返回圖片文件,因爲圖片佔用了必定面積,影響了後面段落的排布,所以瀏覽器須要回過頭來從新渲染這部分代碼。

6. 瀏覽器發現了一個包含一行Javascript代碼的<script>標籤,趕快運行它。

7. Javascript腳本執行了這條語句,它命令瀏覽器隱藏掉代碼中的某個<div> (style.display=」none」)。忽然少了這麼一個元素,瀏覽器不得不從新渲染這部分代碼。

8. 終於等到了</html>的到來,瀏覽器淚流滿面。

9. 等等,還沒完,用戶點了一下界面中的「換膚」按鈕,Javascript讓瀏覽器換了一下<link>標籤的CSS路徑。

10. 瀏覽器召集了在座的各位<div><span><ul><li>們,「大夥兒收拾收拾行李,咱得從新來過……」,瀏覽器向服務器請求了新的CSS文件,從新渲染頁面。

2. 瀏覽器是如何解析代碼的?

上面已經描述了大概,咱們深刻的瞭解一下,瞭解以後能夠考慮考慮咱們怎麼寫代碼能夠給瀏覽器減小點工做量。

2.1 解析HTML

HTML的解析是逐行解析。

瀏覽器的渲染引擎會解析HTML文檔並把標籤轉換成內容樹中的DOM節點。

它會解析style元素和外部文件中的樣式數據。樣式數據和HTML中的顯示控制將共同用來建立另外一棵樹——渲染樹。

渲染引擎會嘗試儘快的把內容顯示出來。它不會等到全部HTML都被解析完才建立並佈局渲染樹。它會 在處理後續內容的同時把處理過的局部內容先展現出來。

瀏覽器的解析器一般把工做分給兩個組件——分詞程序負責把輸入切分紅合法符號序列,解析程序負責按照句法規則分析文檔結構和構建句法樹。詞法分析器知道如何過濾像空格,換行之類的無關字符。

解析器輸出的樹是由DOM元素和屬性節點組成的。

DOM與標籤幾乎有着一一對應的關係,以下面的標籤

<html>
    <body>
        <p>
            Hello 楓
        </p>
        <div> <img src="feng.png"/></div>
    </body>
</html>

會被轉換成如的DOM樹:

2.2 解析CSS

CSS選擇器的讀取順序是從右向左。

#molly div.haha span{color:#f00}

如上面的代碼,瀏覽器會按照從右向左的順序去讀取選擇器。

先找到span而後順着往上找到class爲「haha」的div再找到id爲「molly」的元素。

成功匹配到則加入結果集,若是直到根元素html都沒有匹配,則再也不遍歷這條路徑,從下一個span開始重複這個過程。

整個過程會造成一條符合規則的索引樹,樹由上至下的節點是規則中從右向左的一個個選擇符匹配的節點。

若是從左向右的順序讀取,在執行到左邊的分支後發現沒有相對應標籤匹配,則會回溯到上一個節點再繼續遍歷,直到找到或者沒有相匹配的標籤才結束。

若是有100個甚至1000個分支的時候會消耗不少性能。反之從右向左查找極大的縮小的查找範圍從而提升了性能。

這就解釋了爲何id選擇器大於類選擇器,類選擇器大於元素選擇器。

2.3 解析JS

在瀏覽器中有一個js解析器的工具,專門用來解析咱們的js代碼。

當瀏覽器遇到js代碼時,立馬召喚「js解析器」出來工做。

解析器會找到js當中的全部變量、函數、參數等等,而且把變量賦值爲未定義(undefined)。

把函數取出來成爲一個函數塊,而後存放到倉庫當中。這件事情作完了以後纔開始逐行解析代碼(由上向下,由左向右),而後再去和倉庫進行匹配。

<script>
alert(a);   //undefined
var a = 1;
alert(a);   //1
</script>

<script>
a = 1;
alert(a);
//這個時候會運行報錯!
//這時候a並非一個變量,解析器找不到,倉庫裏面並無a
</script>

再看一下這段代碼

<script>
    alert(a);    //function a(){alert(4)}
    var a = 1;
    alert(a);    //1
    function a(){alert(2)}
    alert(a);    //1
    var a = 3;
    alert(a);    //3
    function a(){alert(4)}
    alert(a);    //3
</script>

在js預解析的時候,在遇到變量和函數重名的時候,只會保留函數塊。在逐行解析代碼的時候表達式(+、-、*、/、%、++、–、 參數 ……)會改變倉庫裏對應的值。

咱們來了解一個詞「做用域」,如今把這個詞拆分一下。 做用:讀、寫操做 域:空間、範圍、區域… 連起來就是可以進行讀寫操做的一個區域。 「域」:函數、json、……都是做爲一塊做用域。 全局變量、局部變量、全局函數 一段 也是一塊域。在域解析的時候,也是由上向下開始解析。這就解釋了爲何引用的外部公共js文件(好比:jquery)應該放到自定義js上邊的緣由。

再來看一下這段代碼

<script>
    var a = 1;
    function fn(){
        alert(a);    //undefined
        var a = 2;
    }
    fn();
    alert(a);    //1
</script>

繼續跟蹤一下解析器的解析過程:首先函數fn()外部的a是一個全局變量,fn()裏面的a是一個局部變量。fn()函數同時是一個做用域,只要是做用域,就得作預解析和逐行解析的步驟。因此第一個alert打印的是fn()做用域的倉庫指向的變量a,即爲undefined。第二個alert打印的是全局的變量a,即爲1。

接下來繼續看代碼,基本雷同的代碼,我改變其中一小個地方。

<script>
    var a = 1;
    function fn(){
        alert(a);    //1
        a = 2;
    }
    fn();
    alert(a);    //2
</script>

看到這裏當解析到fn()的時候,發現裏面並無任何變量,因此也就不往倉庫裏面存什麼,此時的倉庫裏面是空的,啥也沒有。可是這個時候解析並無結束,而是從函數裏面向外開始找,找到全局的變量a。此時打印的正式全局變量a的值。

這裏就涉及到一個做用域鏈的問題。整個解析過程像是一條鏈子同樣。由上向下,由裏到外。局部可以讀寫全局,全局沒法讀寫局部。

來,繼續看代碼,基本雷同的代碼,我再次改變其中一小個地方。

<script>
    var a = 1;
    function fn(a){
        alert(a);    //undefined
        a = 2;
    }
    fn();
    alert(a);    //1
</script>

千萬不能忘了,在預解析的時候瀏覽器除了要找變量和函數以外還須要找一些參數,而且賦值爲未定義。因此這裏的fn(a)至關於fn(var a),這個時候的邏輯就和第一段實例代碼同樣了。

繼續搞事情,繼續看代碼,基本雷同的代碼,我再次改變其中一小個地方。

<script>
    var a = 1;
    function fn(a){
        alert(a);    //1
        a = 2;
    }
    fn(a);
    alert(a);    //1
</script>

當代碼執行到fn(a);的時候調用的fn()函數而且把全局變量a做爲參數傳遞進去。

此時打印的天然是1,要記住function fn(a)至關於function fn(var a),因此這時候a=2;改變的是局部變量a,並無影響到全局變量a,因此第二次打印的依然是1。

3. 瀏覽器的垃圾回收機制

因爲字符串、對象和數組沒有固定大小,全部當他們的大小已知時,才能對他們進行動態的存儲分配。JavaScript程序每次建立字符串、數組或對象時,解釋器都必須分配內存來存儲那個實體。只要像這樣動態地分配了內存,最終都要釋放這些內存以便他們可以被再用,不然,JavaScript的解釋器將會消耗完系統中全部可用的內存,形成系統崩潰。

JavaScript的解釋器能夠檢測到什麼時候程序再也不使用一個對象了,當他肯定了一個對象是無用的時候,他就知道再也不須要這個對象,能夠把它所佔用的內存釋放掉了。

var a = "before";
var b = "override a";
var a = b; //重寫a

這段代碼運行以後,「before」這個字符串失去了引用(以前是被a引用),系統檢測到這個事實以後,就會釋放該字符串的存儲空間以便這些空間能夠被再利用。

瀏覽器一般用採用的垃圾回收有兩種方法:標記清除、引用計數。

3.1 標記清除

這是javascript中最經常使用的垃圾回收方式。當變量進入執行環境是,就標記這個變量爲「進入環境」。從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到他們。當變量離開環境時,則將其標記爲「離開環境」。

垃圾收集器在運行的時候會給存儲在內存中的全部變量都加上標記。而後,它會去掉環境中的變量以及被環境中的變量引用的標記。而在此以後再被加上標記的變量將被視爲準備刪除的變量,緣由是環境中的變量已經沒法訪問到這些變量了。最後。垃圾收集器完成內存清除工做,銷燬那些帶標記的值,並回收他們所佔用的內存空間。

當對象,沒法從根對象沿着引用遍歷到,即不可達(unreachable),進行清除。對於上面的例子,fn() 裏面的 a 和 b 在函數執行完畢後,就不能經過外面的上下文進行訪問了,因此就能夠清除了。

這是當前主流的GC算法,V8裏面就是用這種。

無論是高級語言,仍是低級語言。內存的管理都是:分配內存使用內存(讀或寫)釋放內存前兩步,你們都沒有太大異議。關鍵是釋放內存這一步,各類語言都有本身的垃圾回收(garbage collection, 簡稱GC)機制。

在大部分的應用場景:一個新建立的對象,生命週期一般很短。因此,V8裏面,GC處理分爲兩大類:新生代和老生代。

新生代的堆空間爲1M~8M,並且被平分紅兩份(to-space和from-space),一般一個新建立的對象,內存被分配在新生代。當to-space滿的時候,to-space和form-space交換位置(此時,to空,from滿),並執行GC。若是一個對象被判定爲,未被引用,就清除;有被引用,逃逸次數+1(若是此時逃逸次數爲2,就移入老生代,不然移入to-space)。

老生代的堆空間大,GC不適合像新生代那樣,用平分紅兩個space這種空間換時間的方式。老生代的垃圾回收,分兩個階段:標記、清理(有Sweeping和Compacting這兩種方式)。

標記,採用3色標記:黑、白、灰。步驟以下:

GC開始,因此對象標記爲白色。

根對象標記爲黑色,並開始遍歷其子節點(引用的對象)。

當前被遍歷的節點,標記爲灰色,被放入一個叫 marking bitmap 的棧。在棧中,把當前被遍歷的節點,標記爲黑色,並出棧,同時,把它的子節點(若是有的話)標記爲灰色,並壓入棧。(大對象比較特殊,這裏不展開)

當全部對象被遍歷完後,就只剩下黑和白。經過Sweeping或Compacting的方式,清理掉白色,完成GC。

3.2 引用計次

引用計數的含義是跟蹤記錄每一個值被引用的次數。當聲明瞭一個變量並將一個引用類型賦值給該變量時,則這個值的引用次數就是1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的引用次數就減1。當這個引用次數變成0時,則說明沒有辦法再訪問這個值了,於是就能夠將其所佔的內存空間給收回來。這樣,垃圾收集器下次再運行時,它就會釋放那些引用次數爲0的值所佔的內存。

可是用這種方法存在着一個問題,下面來看看代碼:

function problem() {
    var objA = new Object();
    var objB = new Object();

    objA.someOtherObject = objB;
    objB.anotherObject = objA;
}

  在這個例子中,objA和objB經過各自的屬性相互引用;也就是說這兩個對象的引用次數都是2。在採用引用計數的策略中,因爲函數執行以後,這兩個對象都離開了做用域,函數執行完成以後,objA和objB還將會繼續存在,由於他們的引用次數永遠不會是0。這樣的相互引用若是說很大量的存在就會致使大量的內存泄露。

大多數瀏覽器已經放棄了這種回收方式。

4. 瀏覽器的本地存儲

若是我問你,瀏覽器中的緩存有哪些,我相信絕大部分人會說有三種:cookie,sessionStorage,localStorage。

可是誒,我不知爲何你們都叫這三個爲緩存,他們叫緩存,咱們上面提到的Memory Cache等cache也叫緩存,不是很亂嗎,並且瀏覽器把他們歸到了storage裏面,storage翻譯過來爲存儲。

還有一點,這裏有五種:Cookies、Local Storage、Session Storage、WebSQL 和 IndexedDB。

4.1 cookie

Cookies 是最先的本地存儲,是瀏覽器提供的功能,而且對服務器和 JS 開放,這意味着咱們能夠經過服務器端和客戶端保存 Cookies。不過能夠存儲的數據總量大小隻有 4KB,若是超過了這個限制就會忽略,無法進行保存。

HTTP協議自己是無狀態的。什麼是無狀態呢,即服務器沒法判斷用戶身份。Cookie其實是一小段的文本信息(key-value格式)。客戶端向服務器發起請求,若是服務器須要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。客戶端瀏覽器會把Cookie保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。服務器檢查該Cookie,以此來辨認用戶狀態。

4.2 Local Storage Session Storage

Local Storage 與 Session Storage 都屬於 Web Storage。Web Storage 和 Cookies 相似,區別在於它有更大容量的存儲。其中 Local Storage 是持久化的本地存儲,除非咱們主動刪除數據,不然會一直存儲在本地。Session Storage 只存在於 Session 會話中,也就是說只有在同一個 Session 的頁面才能使用,當 Session 會話結束後,數據也會自動釋放掉。

4.3 cookie Local Storage Session Storage比較

通常在咱們面試的時候,面試官都會問cookie Local Storage Session Storage之間有什麼區別。

特性

Cookie

Local Storage

Session Storage

數據的生命期

可設置失效時間,默認是關閉瀏覽器後失效

除非被顯式清除,不然永久保存

會話級存儲,僅在當前會話下有效,會話結束,關閉頁面或瀏覽器後被清除

存放數據大小

4KB左右

5MB~10MB(瀏覽器不一樣,狀況不一樣)

5MB~10MB(瀏覽器不一樣,狀況不一樣)

與服務器端通訊

每次都會攜帶在HTTP頭中,若是使用cookie保存過多數據會帶來性能和安全問題

僅在客戶端(即瀏覽器)中保存,不參與和服務器的通訊

僅在客戶端(即瀏覽器)中保存,不參與和服務器的通訊

易用性

源生的Cookie接口不友好,開發者須要根據需求封裝

源生接口良好,亦可再次封裝來對Object和Array有更好的支持

源生接口良好,亦可再次封裝來對Object和Array有更好的支持

應用場景

用戶登陸時,保存服務器返回的一段加密過的惟一辨識單一用戶的code,用以判斷當前用戶登陸狀態,或者以前電商網站用來保存用戶的購物車信息。

Local Storage能夠替代Cookie完成用戶購物車信息的前端保存功能,同時能夠看成HTML5遊戲的本地數據的存儲空間。

當頁面存在多表單的狀況下,能夠利用Session Storage實現表單頁拆分,優化用戶體驗。

注意

不要將系統敏感的數據保存到Cookie,Local Storage,Session Storage中,防止XSS注入的風險。由於XSS注入能夠經過控制檯對你的屬性值進行修改,具體能夠參考我寫的另外一篇博客,前端黑客技術。

4.4 WebSQL

WebSQL 與 IndexedDB 都是最新的 HTML5 本地緩存技術,相比於 Local Storage 和 Session Storage 來講,存儲功能更強大,支持的數據類型也更多,好比圖片、視頻等。

WebSQL 更準確的說是 WebSQL DB API,它是一種操做本地數據庫的網頁 API 接口,經過 API 能夠完成客戶端數據庫的操做。當咱們使用 WebSQL 的時候,能夠方便地用 SQL 來對數據進行增刪改查。而這些瀏覽器客戶端,好比 Chrome 和 Safari 會用 SQLite 實現本地存儲,微信就採用了 SQLite 做爲本地聊天記錄的存儲。

4.5 IndexedDB

IndexedDB就是瀏覽器提供的本地數據庫,它能夠被網頁腳本建立和操做。它能夠存儲大量數據,提供了查找接口,可以創建索引。可是不屬於關係型數據庫(不支持SQL查詢語句,更相似於NoSQL數據庫)。

IndexedDB的特色:

  1. 鍵值對存儲:IndexedDB內部採用對象倉庫(object store)存放數據。全部類型的數據均可以直接存入,包括JavaScript對象。對象倉庫中,數據以「鍵值對」的形式保存。每個數據記錄都有對應的主鍵,主鍵是惟一的,若是重複會拋出一個錯誤。

  2. 異步:IndexedDB操做不會鎖死瀏覽器,用戶依然能夠進行其餘操做,與Local Storage的同步操做不一樣,異步的設計是爲了大量數據的讀寫,拖慢頁面的表現,下降用戶體驗。

  3. 支持事務:IndexedDB支持事務(transaction),這意味着一些列操做步驟中,只要有某個步驟出現異常,則整個事務就會消失,數據庫回滾到事務發生以前的狀態,不存在只寫一部分數據的狀況。

  4. 同源限制:IndexedDB受到同源限制,每個數據庫對應建立它的域名。網頁只能訪問自身域名下的數據庫,而不可以訪問跨域的IndexedDB數據庫。

  5. 存儲空間大:IndexedDB的存儲空間通常很多於250MB,或者更大。

  6. 支持二進制存儲:IndexedDB不只能夠儲存字符串,還能夠儲存二進制數據(ArrayBuffer對象和Blob對象)。

事務是數據庫的概念,事務( transaction)是訪問並可能操做各類數據項的一個數據庫操做序列,這些操做要麼所有執行,要麼所有不執行,是一個不可分割的工做單位。事務由事務開始與事務結束之間執行的所有數據庫操做組成。

5. 瀏覽器的線程

咱們日常使用JavaScript的時候都知道js是單線程的,而瀏覽器,則是多線程的。

5.1 CPU

CPU是計算機的核心,其負責承擔計算機的計算任務。這裏咱們比喻爲一個工廠。

5.2 進程

進程是一個具備必定獨立功能的程序在一個數據集上的一次動態執行的過程,是操做系統進行資源分配和調度的一個獨立單位,是應用程序運行的載體。

咱們這裏將進程比喻爲工廠的車間,它表明CPU所能處理的單個任務。任一時刻,CPU老是運行一個進程,其餘進程處於非運行狀態。

5.3 線程

線程是程序執行中一個單一的順序控制流程,是程序執行流的最小單元。

這裏把線程比喻一個車間的工人,即一個車間能夠容許由多個工人協同完成一個任務。

5.4 瀏覽器的多線程

瀏覽器內核是多線程,在內核控制下各線程相互配合以保持同步,一個瀏覽器一般由如下常駐線程組成:

  • GUI 渲染線程
  • JavaScript引擎線程
  • 事件觸發線程
  • 定時觸發器線程
  • 異步http請求線程

5.4.1 GUI渲染線程

GUI渲染線程負責渲染瀏覽器界面HTML元素,解析HTML,CSS,構建DOM樹和RenderObject樹,佈局和繪製等。

當界面須要重繪(Repaint)或因爲某種操做引起迴流(重排)(reflow)時,該線程就會執行。

在Javascript引擎運行腳本期間,GUI渲染線程都是處於掛起狀態的,也就是說被」凍結」了,GUI更新會被保存在一個隊列中等到JS引擎空閒時當即被執行。

5.4.2 JavaScript引擎線程

JavaScript引擎,也能夠稱爲JS內核,主要負責處理Javascript腳本程序,例如V8引擎。

JS引擎一直等待着任務隊列中任務的到來,而後加以處理,一個Tab頁(renderer進程)中不管何時都只有一個JS線程在運行JS程序(單線程)。

注意:GUI渲染線程和JavaScript引擎線程互斥。

因爲JavaScript是可操縱DOM的,若是在修改這些元素屬性同時渲染界面(即JavaScript線程和UI線程同時運行),那麼渲染線程先後得到的元素數據就可能不一致了。

所以爲了防止渲染出現不可預期的結果,瀏覽器設置GUI渲染線程與JavaScript引擎爲互斥的關係,當JavaScript引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到引擎線程空閒時當即被執行。

若是JS執行的時間過長,這樣就會形成頁面的渲染不連貫,致使頁面渲染加載阻塞的感受。

5.4.3 事件觸發線程

當一個事件被觸發時該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理。

這些事件能夠是當前執行的代碼塊如定時任務、也可來自瀏覽器內核的其餘線程如鼠標點擊、AJAX異步請求等,但因爲JS的單線程關係全部這些事件都得排隊等待JS引擎處理。

5.4.4 定時觸發器線程

setIntervalsetTimeout所在線程

瀏覽器定時計數器並非由JavaScript引擎計數的, 由於JavaScript引擎是單線程的, 若是處於阻塞線程狀態就會影響記計時的準確。

經過單獨線程來計時並觸發定時(計時完畢後,添加到事件隊列中,等待JS引擎空閒後執行)

注意,W3C在HTML標準中規定,規定要求setTimeout中低於4ms的時間間隔算爲4ms。

5.4.5 異步http請求線程

在XMLHttpRequest在鏈接後是經過瀏覽器新開一個線程請求。

將檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件,將這個回調再放入事件隊列中,再由JavaScript引擎執行。

6. 瀏覽器的兼容

瀏覽器的兼容問題一直是一個讓人很頭痛的問題,前段時間我還在爲qiankun兼容IE弄得焦頭爛額。

6.1 爲何咱們的代碼在瀏覽器中會出現兼容問題?

由於不一樣的瀏覽器對同一段代碼有不一樣的解析,形成頁面顯示效果不統一的狀況。在大多數狀況下,咱們的需求是,不管用戶用什麼瀏覽器來查看咱們的網站或者登錄咱們的系統,都應該是統一的顯示效果。

版本越高的瀏覽器,支持的特性越多,咱們用的某個插件使用的特性可能高版本的瀏覽器支持,低版本的不支持。

6.2 咱們要怎麼解決呢?

6.2.1 CSS Hack

在CSS方面,咱們可使用CSS Hack。CSS hack是經過在CSS樣式中加入一些特殊的符號也就是瀏覽器前綴,讓不一樣的瀏覽器識別不一樣的符號(什麼樣的瀏覽器識別什麼樣的符號是有標準的,CSS hack就是讓你記住這個標準),以達到應用不一樣的CSS樣式的目的。

6.2.2 polyfill

在JS方面,咱們可使用polyfill。polyfill 是一段代碼(或者插件),提供了那些開發者們但願瀏覽器原生提供支持的功能。程序庫先檢查瀏覽器是否支持某個API,若是不支持則加載對應的 polyfill。好比,html5的storage。不一樣瀏覽器,不一樣版本,有些支持,有些不支持。其實polyfill就是shim的一種。

Shim 指的是在一箇舊的環境中模擬出一個新 API ,並且僅靠舊環境中已有的手段實現,以便全部的瀏覽
器具備相同的行爲。

6.2.3 PostCSS

PostCSS是一個利用JS插件來對CSS進行轉換的工具,這些插件很是強大,強大到無所不能。其中,Autoprefixer就是衆多PostCSS插件中最流行的一個。

Autoprefixer能夠自動幫咱們加上瀏覽器前綴。

6.2.4 Modernizr.js

Modernizr.js十分的強大,既能給老版本瀏覽器打補丁,又能保證新瀏覽器漸進加強的用戶體驗。

Modernizr默認作的事情不多,除了(在你選擇的狀況下)給不支持html5的標籤的瀏覽器,如IE6,7,8追加一點由Remy Sharp開發的html5墊片腳本,使其識別

、等html5元素以外,它主要作的就是瀏覽器功能檢測。所以,它知道瀏覽器是否支持各類html5和css3特性。

7. 最後

最近斷斷續續整理了一些阿里、騰訊、字節等等大廠的面試題,目的是想了解一下大廠招聘的技術熱點,不斷提高學習。

其中包含HTML、CSS、JavaScript、服務端與網絡、Vue、瀏覽器等等,免費分享給你們,還在持續整理收集整理中,有須要的朋友 點擊這裏免費領取題目+解析PDF。

篇幅有限,僅展現部份內容

相關文章
相關標籤/搜索