天天看點

浏覽器相關知識點總結

今天說說浏覽器相關問題:

  1. 常見的浏覽器核心,參見下表:
    浏覽器/RunTime 核心(渲染引擎) JavaScript 引擎
    Chrome

    Blink(28~)

    Webkit(Chrome 27)

    V8
    FireFox Gecko SpiderMonkey
    Safari Webkit JavaScriptCore
    Edge EdgeHTML Chakra(for JavaScript)
    IE Trident Chakra(for JScript)
    PhantomJS Webkit JavaScriptCore
    Node.js - V8
  2. 判斷浏覽器核心

    判斷是否原生Chrome:隻有原生Chrome中存在一種MimeType“application/vnd.chromium.remoting-viewer”,由此可以判斷浏覽器是加殼Chrome或是原生Chrome。 

    判斷是否為IE浏覽器:隻有IE核心的浏覽器存在ActiveXObject對象。由此可以判斷是否為IE浏覽

  3. 從輸入 URL 到頁面加載完成,發生了什麼?

    首先我們需要通過 DNS(域名解析系統)将 URL 解析為對應的 IP 位址,然後與這個 IP 位址确定的那台伺服器建立起 TCP 網絡連接配接,随後我們向服務端抛出我們的 HTTP 請求,服務端處理完我們的請求之後,把目标資料放在 HTTP 響應裡傳回給用戶端,拿到響應資料的浏覽器就可以開始走一個渲染的流程。渲染完畢,頁面便呈現給了使用者。

    1) DNS 解析:

    2) TCP 連接配接:

    3) HTTP 請求抛出

    4) 服務端處理請求,HTTP 響應傳回

    5) 浏覽器拿到響應資料,解析響應内容,把解析的結果展示給使用者

  4. 浏覽器重繪與重排的差別?

    重排: 部分渲染樹(或者整個渲染樹)需要重新分析并且節點尺寸需要重新計算,表現為重新生成布局,重新排列元素

    重繪: 由于節點的幾何屬性發生改變或者由于樣式發生改變,例如改變元素背景色時,螢幕上的部分内容需要更新,表現為某些元素的外觀被改變

    單單改變元素的外觀,肯定不會引起網頁重新生成布局,但當浏覽器完成重排之後,将會重新繪制受到此次重排影響的部分

    重排和重繪代價是高昂的,它們會破壞使用者體驗,并且讓UI展示非常遲緩,而相比之下重排的性能影響更大,在兩者無法避免的情況下,一般我們甯可選擇代價更小的重繪。

    『重繪』不一定會出現『重排』,『重排』必然會出現『重繪』。

  5. 如何觸發重排和重繪?

    任何改變用來建構渲染樹的資訊都會導緻一次重排或重繪:

    添加、删除、更新DOM節點

    通過display: none隐藏一個DOM節點-觸發重排和重繪

    通過visibility: hidden隐藏一個DOM節點-隻觸發重繪,因為沒有幾何變化

    移動或者給頁面中的DOM節點添加動畫

    添加一個樣式表,調整樣式屬性

    使用者行為,例如調整視窗大小,改變字号,或者滾動

  6. 如何避免重繪或者重排?

    集中改變樣式:通過改變class的方式來集中改變樣式

    // 判斷是否是黑色系樣式
    const theme = isDark ? 'dark' : 'light'
    
    // 根據判斷來設定不同的class
    ele.setAttribute('className', theme)
               
    使用DocumentFragment:通過createDocumentFragment建立一個遊離于DOM樹之外的節點,然後在此節點上批量操作,最後插入DOM樹中,是以隻觸發一次重排
    var fragment = document.createDocumentFragment();
    
    for (let i = 0;i<10;i++){
      let node = document.createElement("p");
      node.innerHTML = i;
      fragment.appendChild(node);
    }
    
    document.body.appendChild(fragment);
               
    提升為合成層:提升合成層的最好方式是使用 CSS 的 will-change 屬性
    #target {
      will-change: transform;
    }
               
  7. 前端如何實作即時通訊?

    1)短輪詢

    2)comet

    3)SSE

    4)Websocket

    5)Web Worker

    6)Service workers

    HTTP 協定有一個缺陷:通信隻能由用戶端發起。舉例來說,我們想了解今天的天氣,隻能是用戶端向伺服器送出請求,伺服器傳回查詢結果。HTTP 協定做不到伺服器主動向用戶端推送資訊。這種單向請求的特點,注定了如果伺服器有連續的狀态變化,用戶端要獲知就非常麻煩。我們隻能使用"輪詢":每隔一段時候,就發出一個詢問,了解伺服器有沒有新的資訊。最典型的場景就是聊天室。輪詢的效率低,非常浪費資源(因為必須不停連接配接,或者 HTTP 連接配接始終打開)。是以,可以用另一種方法  WebSocket 。

  8. Websocket

    WebSocket 協定最大特點就是,伺服器可以主動向用戶端推送資訊,用戶端也可以主動向伺服器發送資訊,是真正的雙向平等對話,屬于伺服器推送技術的一種。

    特點包括:

    (1)建立在 TCP 協定之上,伺服器端的實作比較容易。

    (2)與 HTTP 協定有着良好的相容性。預設端口也是80和443,并且握手階段采用 HTTP 協定,是以握手時不容易屏蔽,能通過各種 HTTP 代理伺服器。

    (3)資料格式比較輕量,性能開銷小,通信高效。

    (4)可以發送文本,也可以發送二進制資料。

    (5)沒有同源限制,用戶端可以與任意伺服器通信。

    (6)協定辨別符是

    ws

    (如果加密,則為

    wss

    ),伺服器網址就是 URL。

    WebSocket 的用法

    var ws = new WebSocket("wss://echo.websocket.org");
    
    ws.onopen = function(evt) { 
      console.log("Connection open ..."); 
      ws.send("Hello WebSockets!");
    };
    
    ws.onmessage = function(evt) {
      console.log( "Received Message: " + evt.data);
      ws.close();
    };
    
    ws.onclose = function(evt) {
      console.log("Connection closed.");
    };
               

    用戶端的 API

    1)WebSocket 構造函數,用于建立 WebSocket 執行個體。執行下面語句之後,用戶端就會與伺服器進行連接配接。

    var ws = new WebSocket('ws://localhost:8080');
               

    2)webSocket.readyState

    3)webSocket.onopen

    4)webSocket.onclose

    5)webSocket.onmessage

    6)webSocket.send()

    7)webSocket.bufferedAmount

    8)webSocket.onerror

    服務端的實作

    常用的 Node 實作有以下三種

    µWebSockets

    Socket.IO

    WebSocket-Node

    推薦一款非常特别的 WebSocket 伺服器:Websocketd。

  9. 浏覽器跨域問題解決(九種跨域方式)

    最經典的跨域方案jsonp

    最流行的跨域方案cors

    最友善的跨域方案nginx反向代理

    其它跨域方案:

  10. 常見web安全及防護

    web安全受到的威脅來自于以下幾種攻擊方式:

    sql注入,

    xss(惡意注入js和網頁元素),

    csrf(代替使用者完成互動并通路危險網站)

    我們的防護:

    1)對使用者輸入内容進行限制和校驗

    2)對所有關鍵資料進行加密

    3)接口送出方式嚴格規範,資料送出不使用get

  11. 浏覽器存儲和服務端存儲

    cookie和session的差別

    1)存儲位置有區分,cookie存在浏覽器緩存中,session存儲在伺服器上

    2)存儲大小和數量 cookie小于session

    3)保密性 cookie對遊覽器和使用者可見 session存在伺服器上,保密性更佳

    4)伺服器壓力 session對伺服器壓力更大

    在實際開發過程中,cookie和session都很少使用,更為常用的是token,它比上兩個優勢更大,更好用,安全性高,可擴充性強,多平台跨域,無狀态。可實作,單點登入和多點登入等功能。

    不要混淆session和session Storage

    session是身份驗證(類似于令牌),session是一次浏覽器和伺服器的互動的會話,當通路伺服器這個網頁的時候,會在伺服器端的記憶體裡開辟一塊記憶體,這塊記憶體就叫做session,而這個記憶體是跟浏覽器關聯在一起的。這個浏覽器指的是浏覽器視窗或浏覽器的子視窗,意思就是,隻允許目前這個session對應的浏覽器通路,就算是在同一個機器上新啟的浏覽器也是無法通路的。而另外一個浏覽器也需要記錄session的話,就會再啟一個屬于自己的session

    session Storage是一種存儲方式,資料儲存在浏覽器緩存中,關閉浏覽器和标簽頁就會消失。特别的一點在于,即便是相同域名下的兩個頁面,隻要它們不在同一個浏覽器視窗中打開,那麼它們的 Session Storage 内容便無法共享。

    local Storage存儲在本地需要手動删除才會消失,理論上 Cookie 無法勝任的、可以用簡單的鍵值對來存取的資料存儲任務,都可以交給 Local Storage 來做。存儲一些内容穩定的資源。比如圖檔内容豐富的電商網站會用它來存儲 Base64 格式的圖檔字元串,存儲一些不經常更新的 CSS、JS 等靜态資源。

    cookie是把少量的資訊存儲在使用者端浏覽器緩存中,它是全局的,在同一個域名下的所有請求,都會攜帶 Cookie。很快速友善。而且它本身存儲的尺寸大小也有限 4k左右,最關鍵是使用者是可見的,并可以随意的修改,很不安全。Cookie 雖然小,請求卻可以有很多,随着請求的疊加,這樣的不必要的 Cookie 帶來的開銷将是無法想象的。靜态資源往往并不需要 Cookie 攜帶什麼認證資訊。把靜态資源和首頁面置于不同的域名下,完美地避免了不必要的 Cookie 的出現!

    cookies和localStorage和sessionStorage是同一類,都是儲存方式,都遵循同源政策。隻是大小,儲存位置,儲存時間不一樣 大小 cookies<sessionStorage=localStorage 5-10M 之間

    儲存位置 本地

     IndexedDB

    IndexedDB 是沒有存儲上限的(一般來說不會小于 250M)

    IndexedDB 可以看做是 LocalStorage 的一個更新,當資料的複雜度和規模上升到了 LocalStorage 無法解決的程度,我們毫無疑問可以請出 IndexedDB 來幫忙。

  12. 前端造成記憶體洩漏的操作

    1)閉包由于閉包會使得函數中的變量都被儲存在記憶體中,記憶體消耗很大,過多的使用閉包,不及時釋放,會導緻記憶體洩漏。

    2)意外全局函數中定義參數,var 定義,會成為全局變量,使用es6的let可以避免這種情況。

    3)定時器

    4)生命周期中使用全局函數,未釋放

    5)echarts

    6)keep-alive元件

    什麼是記憶體洩漏?

    基于垃圾回收機制,當有變量和對象應該被回收而沒有被回收時,一直占用和停留在堆記憶體中,這就産生記憶體洩漏。

    記憶體洩漏會導緻記憶體被占用過多無法釋放,進而導緻系統記憶體配置設定不足,造成了記憶體溢出進而導緻應用Crash(浏覽器崩潰)。

    還有對于現在電腦來說,一些記憶體洩漏并不影響。

  13. 性能監控

    performance

    function getsec(time) {
          return time / 1000 + 's'
        }
        window.onload = function () {
          var per = window.performance.timing;
          console.log('DNS查詢耗時' + getsec(per.domainLookupEnd - per.domainLookupStart))
          console.log('TCP連結耗時' + getsec(per.connectEnd - per.connectStart))
          console.log('request請求響應耗時' + getsec(per.responseEnd - per.responseStart))
          console.log('dom渲染耗時' + getsec(per.domComplete - per.domInteractive))
          console.log('白屏時間' + getsec(firstPaint - pageStartTime))
          console.log('domready可操作時間' + getsec(per.domContentLoadedEventEnd - per.fetchStart))
        }