11道瀏覽器原理面試題

来源:https://www.cnblogs.com/duxinyi/archive/2019/09/24/11576668.html
-Advertisement-
Play Games

瀏覽器與新技術 面試題來源於我的項目 "「前端面試與進階指南」" 本章關於瀏覽器原理部分的內容主要來源於 "瀏覽器工作原理" ,這是一篇很長的文章,可以算上一本小書了,有精力的非常建議閱讀。 常見的瀏覽器內核有哪些? | 瀏覽器/RunTime | 內核(渲染引擎) | JavaScript 引擎 ...


瀏覽器與新技術

面試題來源於我的項目「前端面試與進階指南」

本章關於瀏覽器原理部分的內容主要來源於瀏覽器工作原理,這是一篇很長的文章,可以算上一本小書了,有精力的非常建議閱讀。

常見的瀏覽器內核有哪些?

瀏覽器/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

瀏覽器的主要組成部分是什麼?

  1. 用戶界面 - 包括地址欄、前進/後退按鈕、書簽菜單等。除了瀏覽器主視窗顯示的您請求的頁面外,其他顯示的各個部分都屬於用戶界面。
  2. 瀏覽器引擎 - 在用戶界面和呈現引擎之間傳送指令。
  3. 呈現引擎 - 負責顯示請求的內容。如果請求的內容是 HTML,它就負責解析 HTML 和 CSS 內容,並將解析後的內容顯示在屏幕上。
  4. 網路 - 用於網路調用,比如 HTTP 請求。其介面與平臺無關,併為所有平臺提供底層實現。
  5. 用戶界面後端 - 用於繪製基本的視窗小部件,比如組合框和視窗。其公開了與平臺無關的通用介面,而在底層使用操作系統的用戶界面方法。
  6. JavaScript 解釋器。用於解析和執行 JavaScript 代碼。
  7. 數據存儲。這是持久層。瀏覽器需要在硬碟上保存各種數據,例如 Cookie。新的 HTML 規範 (HTML5) 定義了“網路資料庫”,這是一個完整(但是輕便)的瀏覽器內資料庫。

圖:瀏覽器的主要組件。

值得註意的是,和大多數瀏覽器不同,Chrome 瀏覽器的每個標簽頁都分別對應一個呈現引擎實例。每個標簽頁都是一個獨立的進程。

瀏覽器是如何渲染UI的?

  1. 瀏覽器獲取HTML文件,然後對文件進行解析,形成DOM Tree
  2. 與此同時,進行CSS解析,生成Style Rules
  3. 接著將DOM Tree與Style Rules合成為 Render Tree
  4. 接著進入佈局(Layout)階段,也就是為每個節點分配一個應出現在屏幕上的確切坐標
  5. 隨後調用GPU進行繪製(Paint),遍歷Render Tree的節點,並將元素呈現出來

2019-06-22-06-48-02

瀏覽器如何解析css選擇器?

瀏覽器會『從右往左』解析CSS選擇器。

我們知道DOM Tree與Style Rules合成為 Render Tree,實際上是需要將Style Rules附著到DOM Tree上,因此需要根據選擇器提供的信息對DOM Tree進行遍歷,才能將樣式附著到對應的DOM元素上。

以下這段css為例

.mod-nav h3 span {font-size: 16px;}

我們對應的DOM Tree 如下

2019-06-22-06-58-56

若從左向右的匹配,過程是:

  1. 從 .mod-nav 開始,遍歷子節點 header 和子節點 div
  2. 然後各自向子節點遍歷。在右側 div 的分支中
  3. 最後遍歷到葉子節點 a ,發現不符合規則,需要回溯到 ul 節點,再遍歷下一個 li-a,一顆DOM樹的節點動不動上千,這種效率很低。

如果從右至左的匹配:

  1. 先找到所有的最右節點 span,對於每一個 span,向上尋找節點 h3
  2. 由 h3再向上尋找 class=mod-nav 的節點
  3. 最後找到根元素 html 則結束這個分支的遍歷。

後者匹配性能更好,是因為從右向左的匹配在第一步就篩選掉了大量的不符合條件的最右節點(葉子節點);而從左向右的匹配規則的性能都浪費在了失敗的查找上面。

DOM Tree是如何構建的?

  1. 轉碼: 瀏覽器將接收到的二進位數據按照指定編碼格式轉化為HTML字元串
  2. 生成Tokens: 之後開始parser,瀏覽器會將HTML字元串解析成Tokens
  3. 構建Nodes: 對Node添加特定的屬性,通過指針確定 Node 的父、子、兄弟關係和所屬 treeScope
  4. 生成DOM Tree: 通過node包含的指針確定的關係構建出DOM
    Tree

2019-06-22-11-48-00

瀏覽器重繪與重排的區別?

  • 重排: 部分渲染樹(或者整個渲染樹)需要重新分析並且節點尺寸需要重新計算,表現為重新生成佈局,重新排列元素
  • 重繪: 由於節點的幾何屬性發生改變或者由於樣式發生改變,例如改變元素背景色時,屏幕上的部分內容需要更新,表現為某些元素的外觀被改變

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

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

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

如何觸發重排和重繪?

任何改變用來構建渲染樹的信息都會導致一次重排或重繪:

  • 添加、刪除、更新DOM節點
  • 通過display: none隱藏一個DOM節點-觸發重排和重繪
  • 通過visibility: hidden隱藏一個DOM節點-只觸發重繪,因為沒有幾何變化
  • 移動或者給頁面中的DOM節點添加動畫
  • 添加一個樣式表,調整樣式屬性
  • 用戶行為,例如調整視窗大小,改變字型大小,或者滾動。

如何避免重繪或者重排?

集中改變樣式

我們往往通過改變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);

提升為合成層

將元素提升為合成層有以下優點:

  • 合成層的點陣圖,會交由 GPU 合成,比 CPU 處理要快
  • 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
  • 對於 transform 和 opacity 效果,不會觸發 layout 和 paint

提升合成層的最好方式是使用 CSS 的 will-change 屬性:

#target {
  will-change: transform;
}

關於合成層的詳解請移步無線性能優化:Composite

前端如何實現即時通訊?

短輪詢

短輪詢的原理很簡單,每隔一段時間客戶端就發出一個請求,去獲取伺服器最新的數據,一定程度上模擬實現了即時通訊。

  • 優點:相容性強,實現非常簡單
  • 缺點:延遲性高,非常消耗請求資源,影響性能

comet

comet有兩種主要實現手段,一種是基於 AJAX 的長輪詢(long-polling)方式,另一種是基於 Iframe 及 htmlfile 的流(streaming)方式,通常被叫做長連接。

具體兩種手段的操作方法請移步Comet技術詳解:基於HTTP長連接的Web端實時通信技術

長輪詢優缺點:

  • 優點:相容性好,資源浪費較小
  • 缺點:伺服器hold連接會消耗資源,返回數據順序無保證,難於管理維護

長連接優缺點:

  • 優點:相容性好,消息即時到達,不發無用請求
  • 缺點:伺服器維護長連接消耗資源

SSE

使用指南請看Server-Sent Events 教程

SSE(Server-Sent Event,服務端推送事件)是一種允許服務端向客戶端推送新數據的HTML5技術。

  • 優點:基於HTTP而生,因此不需要太多改造就能使用,使用方便,而websocket非常複雜,必須藉助成熟的庫或框架
  • 缺點:基於文本傳輸效率沒有websocket高,不是嚴格的雙向通信,客戶端向服務端發送請求無法復用之前的連接,需要重新發出獨立的請求

2019-06-22-12-33-19

Websocket

使用指南請看WebSocket 教程

Websocket是一個全新的、獨立的協議,基於TCP協議,與http協議相容、卻不會融入http協議,僅僅作為html5的一部分,其作用就是在伺服器和客戶端之間建立實時的雙向通信。

  • 優點:真正意義上的實時雙向通信,性能好,低延遲
  • 缺點:獨立與http的協議,因此需要額外的項目改造,使用複雜度高,必須引入成熟的庫,無法相容低版本瀏覽器

2019-06-22-12-33-43

Web Worker

後面性能優化部分會用到,先做瞭解

Web Worker 的作用,就是為 JavaScript 創造多線程環境,允許主線程創建 Worker 線程,將一些任務分配給後者運行

Web Worker教程

Service workers

後面性能優化部分會用到,先做瞭解

Service workers 本質上充當Web應用程式與瀏覽器之間的代理伺服器,也可以在網路可用時作為瀏覽器和網路間的代理,創建有效的離線體驗。

Service workers教程

什麼是瀏覽器同源策略?

同源策略限制了從同一個源載入的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。

同源是指"協議+功能變數名稱+埠"三者相同,即便兩個不同的功能變數名稱指向同一個ip地址,也非同源。

下表給出了相對http://store.company.com/dir/page.html同源檢測的示例:

2019-06-23-10-25-28

瀏覽器中的大部分內容都是受同源策略限制的,但是以下三個標簽可以不受限制:

  • <img src=XXX>
  • <link href=XXX>
  • <script src=XXX>

如何實現跨域?

跨域是個比較古老的命題了,歷史上跨域的實現手段有很多,我們現在主要介紹三種比較主流的跨域方案,其餘的方案我們就不深入討論了,因為使用場景很少,也沒必要記這麼多奇技淫巧。

最經典的跨域方案jsonp

jsonp本質上是一個Hack,它利用<script>標簽不受同源策略限制的特性進行跨域操作。

jsonp優點:

  • 實現簡單
  • 相容性非常好

jsonp的缺點:

  • 只支持get請求(因為<script>標簽只能get)
  • 有安全性問題,容易遭受xss攻擊
  • 需要服務端配合jsonp進行一定程度的改造

jsonp的實現:

function JSONP({  
  url,
  params,
  callbackKey,
  callback
}) {
  // 在參數里制定 callback 的名字
  params = params || {}
  params[callbackKey] = 'jsonpCallback'
    // 預留 callback
  window.jsonpCallback = callback
    // 拼接參數字元串
  const paramKeys = Object.keys(params)
  const paramString = paramKeys
    .map(key => `${key}=${params[key]}`)
    .join('&')
    // 插入 DOM 元素
  const script = document.createElement('script')
  script.setAttribute('src', `${url}?${paramString}`)
  document.body.appendChild(script)
}

JSONP({  
  url: 'http://s.weibo.com/ajax/jsonp/suggestion',
  params: {
    key: 'test',
  },
  callbackKey: '_cb',
  callback(result) {
    console.log(result.data)
  }
})

最流行的跨域方案cors

cors是目前主流的跨域解決方案,跨域資源共用(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域、協議或埠請求一個資源時,資源會發起一個跨域 HTTP 請求。

如果你用express,可以這樣在後端設置

//CORS middleware
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://example.com');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');

    next();
}

//...
app.configure(function() {
    app.use(express.bodyParser());
    app.use(express.cookieParser());
    app.use(express.session({ secret: 'cool beans' }));
    app.use(express.methodOverride());
    app.use(allowCrossDomain);
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});

在生產環境中建議用成熟的開源中間件解決問題。

最方便的跨域方案Nginx

nginx是一款極其強大的web伺服器,其優點就是輕量級、啟動快、高併發。

現在的新項目中nginx幾乎是首選,我們用node或者java開發的服務通常都需要經過nginx的反向代理。

2019-06-24-10-19-34

反向代理的原理很簡單,即所有客戶端的請求都必須先經過nginx的處理,nginx作為代理伺服器再講請求轉發給node或者java服務,這樣就規避了同源策略。

#進程, 可更具cpu數量調整
worker_processes  1;

events {
    #連接數
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    #連接超時時間,伺服器會在這個時間過後關閉連接。
    keepalive_timeout  10;

    # gizp壓縮
    gzip  on;

    # 直接請求nginx也是會報跨域錯誤的這裡設置允許跨域
    # 如果代理地址已經允許跨域則不需要這些, 否則報錯(雖然這樣nginx跨域就沒意義了)
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;

    # srever模塊配置是http模塊中的一個子模塊,用來定義一個虛擬訪問主機
    server {
        listen       80;
        server_name  localhost;
        
        # 根路徑指到index.html
        location / {
            root   html;
            index  index.html index.htm;
        }

        # localhost/api 的請求會被轉發到192.168.0.103:8080
        location /api {
            rewrite ^/b/(.*)$ /$1 break; # 去除本地介面/api首碼, 否則會出現404
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://192.168.0.103:8080; # 轉發地址
        }
        
        # 重定向錯誤頁面到/50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

    }

}

其它跨域方案

  1. HTML5 XMLHttpRequest 有一個API,postMessage()方法允許來自不同源的腳本採用非同步方式進行有限的通信,可以實現跨文本檔、多視窗、跨域消息傳遞。
  2. WebSocket 是一種雙向通信協議,在建立連接之後,WebSocket 的 server 與 client 都能主動向對方發送或接收數據,連接建立好了之後 client 與 server 之間的雙向通信就與 HTTP 無關了,因此可以跨域。
  3. window.name + iframe:window.name屬性值在不同的頁面(甚至不同功能變數名稱)載入後依舊存在,並且可以支持非常長的 name 值,我們可以利用這個特點進行跨域。
  4. location.hash + iframe:a.html欲與c.html跨域相互通信,通過中間頁b.html來實現。 三個頁面,不同域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通信。
  5. document.domain + iframe: 該方式只能用於二級功能變數名稱相同的情況下,比如 a.test.com 和 b.test.com 適用於該方式,我們只需要給頁面添加 document.domain ='test.com' 表示二級功能變數名稱都相同就可以實現跨域,兩個頁面都通過js強制設置document.domain為基礎主域,就實現了同域。

其餘方案來源於九種跨域方式


參考文章:


公眾號

想要實時關註筆者最新的文章和最新的文檔更新請關註公眾號程式員面試官,後續的文章會優先在公眾號更新.

簡歷模板: 關註公眾號回覆「模板」獲取

《前端面試手冊》: 配套於本指南的突擊手冊,關註公眾號回覆「fed」獲取

2019-08-12-03-18-41


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 序 上一篇已經把基本架子搭起來了,現在來增加css、scss、自動生成html、css 提取等方面的打包。webpack 預設只能處理js模塊,所以其他文件類型需要做下轉換,而loader 恰恰是做這個事情的,來試試一步一步的完成這個事情吧。 1、安裝 normalize.css normalize ...
  • 一.示例代碼 註意:如果進行本地測試請在右上角 `本地設置 不校驗合法性打鉤` 二.參數 "微信官方解釋" 參數 | 屬性 | 類型 | 預設值 | 必填 | 說明 | | : | : : | : : | | | | url | string | | 是 | 開發者伺服器介面地址 | | data ...
  • layui獲取全部覆選框checkbox選中的值,layui獲取表單開關switch的值 ...
  • 學習jQuer對錶單、表格操作的過程中,按照書上的例子發現一個問題: 以下代碼同樣使用prop()函數,使用attr()方法也不能實現預期 ...
  • 小程式製作扭蛋機 2019-09-24 13:26:53 公司要製作活動小程式,其中有一個扭蛋機的效果實現抽獎的功能。在網上找了好久竟沒有找到(不知道是不是我找代碼的方式有問題)。最後還是自己做一個吧- _ - ,效果如下: 1.wxml 當然我這裡沒有用wx:for遍歷 2.wxss 這一步比較麻 ...
  • 跨域:顧名思義,跨埠,功能變數名稱,協議都算跨域, 平常中請求後臺,發送http請求,就一般用的就是axios跟jquery,用這個兩個發送請求時,在同域也就是不跨域條件下了瀏覽器會自動帶cookie 那現在webpack他有了proxy設置,就是解決了跨域問題,也就是說如果我本地項目想要請求一個http ...
  • webpack在build包的時候,有時候會遇到打包時間很長的問題,這裡提供了一個解決方案,讓打包如絲般順滑~ 1. 介紹 在用 Webpack 打包的時候,對於一些不經常更新的第三方庫,比如 ,`lodash vue` 我們希望能和自己的代碼分離開,Webpack 社區有兩種方案 CommonsC ...
  • 最近朋友圈和微博都刷了一波傑倫的回憶殺–說好不哭,想想都9012了,在學習react如火如荼的路上,也不妨停下腳步來總結總結,朝花夕拾一下。 為了便於闡述,我們還是來段小明和禪師的故事吧。 小明在學習路上遇到了一些問題,於是有了以下對話: <1> npm 對 yarn 小明:經歷了從 npm -> ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...