瀏覽器工做原理及web 性能優化(上)

瀏覽器工做原理

1、瀏覽器簡介

  • 分類:如今主要有五大主流瀏覽器: Chrome, Internet Explorer, Firefox, Safari and Opera.移動端上是Android Browser, iPhone, Opera Mini and Opera Mobile, UC Browser, the Nokia S40/S60 browsers,除了Opera,這些瀏覽器都是基於WebKit內核的(目前可能有變)。
  • 功能 :根據W3C制定的一系列規範,從服務端請求並渲染資源
  • 廣泛外觀:地址欄,前進後退,書籤,刷新及取消,主頁
  • 深層結構:下邊主要介紹———渲染引擎及JS引擎
    • 用戶界面(User Interface) :包括地址欄、前進/後退按鈕、書籤菜單等。除了瀏覽器主窗口顯示的您請求的頁面外,其餘顯示的各個部分都屬於用戶界面。
    • 瀏覽器引擎(Browser engine): 在用戶界面和呈現引擎之間傳送指令。
    • 呈現引擎(Rendering engine): 負責顯示請求的內容。若是請求的內容是 HTML,它就負責解析 HTML 和 CSS 內容,並將解析後的內容顯示在屏幕上。
    • 網絡(Networking) : 用於網絡調用,好比 HTTP 請求。其接口與平臺無關,併爲全部平臺提供底層實現。
    • 用戶界面後端(UI backend):用於繪製基本的窗口小部件,好比組合框和窗口。其公開了與平臺無關的通用接口,而在底層使用操做系統的用戶界面方法。
    • JavaScript 解釋器(JS Interpreter):用於解析和執行 JavaScript 代碼。
    • 數據存儲(Datapersistence):這是持久層。瀏覽器須要在硬盤上保存各類數據,例如 Cookie。新的 HTML 規範 (HTML5) 定義了「網絡數據庫」,這是一個完整(可是輕便)的瀏覽器內數據庫。

2、渲染引擎

  • 分類:不一樣瀏覽器用不一樣渲染引擎。

Internet Explorer uses Trident, Firefox uses Gecko, Safari uses WebKit. Chrome and Opera (from version 15) use Blink, a fork of WebKit.WebKit 是一個開源渲染引擎,起初做爲Linux platform的引擎,後被 Apple應用於Mac. 詳見 webkit.org.css

  • 加載:

    • 瀏覽器根據 DNS 服務器獲得域名的 IP 地址
    • 向這個 IP 的機器發送 HTTP 請求
    • 服務器收到、處理並返回 HTTP 請求
    • 瀏覽器獲得返回內容

客戶端:如在瀏覽器輸入https://www.baidu.com/,通過 DNS 解析,查到baidu.com對應的 IP(不一樣時間、地點對應的 IP 可能會不一樣),瀏覽器向該 IP 發送 HTTP 請求。html

服務端:服務器接收到 HTTP 請求,經計算,返回 HTTP 請求前端

  • 過程以下:
    在Chrome開發者工具中的Timeline中能夠詳細看到具體過程。

  • 內容以下:

  • 渲染:

瀏覽器對請求的呈現。默認渲染引擎能夠呈現html,xml及圖片。(經過插件)也能夠呈現其它數據,好比pdf等。 目前只考慮html和css方面。html5

渲染主流程:

Figure : Rendering engine basic flow

Figure : WebKit main flow

content tree:在此,渲染引擎解析html文檔並將元素轉換成DOM節點。node

render tree:在此,渲染引擎解析style(外部css文件或內聯style)並轉換。react

這是一個漸進式過程. 爲保證好的UED,渲染引擎儘早展示內容. 在開始構建並展示render樹以前,它不會等全部html被解析。部份內容被解析並呈現,同時進程繼續解析網絡中不斷請求到的其他內容。webpack

具體渲染過程
  • 根據 HTML 結構生成 DOM 樹
  • 根據 CSS 生成 CSSOM
  • 將 DOM 和 CSSOM 整合造成 RenderTree
  • 根據 RenderTree 開始渲染和展現
  • 遇到<script>時,會執行並阻塞渲染

瀏覽器拿到了 server 端返回的 HTML 內容後,開始解析並渲染。最初拿到的內容就是一堆字符串,必須先結構化成計算機擅長處理的基本數據結構--- DOM 樹 (最基本的數據結構之一)。git

解析過程當中,若是遇到<link href="..."><script src="...">這種外鏈加載 CSS 和 JS 的標籤,瀏覽器會異步下載,下載過程和上文中下載 HTML 的流程同樣。只不過,這裏下載下來的字符串是 CSS 或者 JS 格式的。實際應用中,經常使用媒體類型(media type)和媒體查詢(media query)來解除對渲染的阻塞。github

<link href="index.css" rel="stylesheet"> <--阻塞-->
<link href="print.css" rel="stylesheet" media="print"><--加載&& !阻塞,僅在print時實用-->
<link href="other.css" rel="stylesheet" media="(min-width: 30em) and (orientation: landscape)"><--符合條件時阻塞-->
複製代碼
  • 查看下邊一段代碼:
<p>根據 HTML 結構生成 DOM 樹,稍等。。。</p>
<script src="app.js"></script>
<p>根據 CSS 生成 CSSOM,稍等。。。</p>
<script>console.log("inline")</script>
<p>將 DOM 和 CSSOM 整合造成 RenderTree</p>
複製代碼

這段html被解析時會被js代碼阻塞(加載+執行),所以常把css放在頭部(保證渲染),把js放在底部(保證非阻塞)。web

  • Normal execution <script> 瀏覽器默認:當執行script時解析html代碼暫停。對於慢服務和重script的狀況意味着webpage呈現將被延後。
  • Deferred execution <script defer> 簡而言之:推遲script執行直到html解析結束。該屬性的好處就是DOM渲染友好,對於你的script。然而,並不是每一個瀏覽器支持該屬性,故不要期望它!
  • Asynchronous execution <script async> 不用管script什麼時候好?async對於兩個都是最好的:html解析可能持續且script將被執行一旦ready。對script標籤推薦這個屬性,如google analytics所分析。
document.createElement("script").async
// true
複製代碼
<script src="app1.js" defer></script>
<script src="app2.js" defer></script>

<script src="init1.js" async></script>
<script src="init2.js" async></script>
複製代碼
  • 瀏覽器的容錯機制

您在瀏覽 HTML 網頁時歷來不會看到「語法無效」的錯誤。這是由於瀏覽器會糾正任何無效內容,而後繼續工做。

1. 明顯不能在某些外部標籤中添加的元素。在此狀況下,咱們應該關閉全部標籤,直到出現禁止添加的元素,而後再加入該元素。
2. 咱們不能直接添加的元素。這極可能是網頁做者忘記添加了其中的一些標籤(或者其中的標籤是可選的)。這些標籤可能包括:HTML HEAD BODY TBODY TR TD LI(還有遺漏的嗎?)。
3. 向 inline 元素內添加 block 元素。關閉全部 inline 元素,直到出現下一個較高級的 block 元素。
4. 若是這樣仍然無效,可關閉全部元素,直到能夠添加元素爲止,或者忽略該標記。
複製代碼

CSS 解析

和 HTML 不一樣,CSS 是上下文無關的語法,可使用簡介中描述的各類解析器進行解析。事實上,CSS 規範定義了 CSS 的詞法和語法。

  • WebKit CSS 解析器 WebKit 使用 Flex 和 Bison 解析器生成器,經過 CSS 語法文件自動建立解析器。正如咱們以前在解析器簡介中所說,Bison 會建立自下而上的移位歸約解析器。Firefox 使用的是人工編寫的自上而下的解析器。這兩種解析器都會將 CSS 文件解析成 StyleSheet 對象,且每一個對象都包含 CSS 規則。CSS 規則對象則包含選擇器和聲明對象,以及其餘與 CSS 語法對應的對象。
圖:解析 CSS

處理腳本和樣式表的順序

  • 腳本

:遇到 <script>文檔的解析將中止,直到腳本執行完畢。若是腳本是外部的,那麼解析過程會中止,直到從網絡同步抓取資源完成後再繼續。HTML5 增長了一個選項----「defer」,這樣不會中止文檔解析,而是等到解析結束才執行.

  • 預解析 WebKit 和 Firefox 都進行了這項優化。**在執行腳本時,其餘線程會解析文檔的其他部分,找出並加載須要經過網絡加載的其餘資源。**經過這種方式,資源能夠在並行鏈接上加載,從而提升整體速度。請注意,預解析器不會修改 DOM 樹,而是將這項工做交由主解析器處理;預解析器只會解析外部資源(例如外部腳本、樣式表和圖片)的引用。

  • 樣式表: 理論上來講,應用樣式表不會更改 DOM 樹,所以彷佛沒有必要等待樣式表並中止文檔解析

  • render樹構建: 在 DOM 樹構建的同時,瀏覽器還會構建:render樹---按照正確的順序繪製內容。

  • render樹和 DOM 樹的關係:呈現器是和 DOM 元素相對應的,但並不是一一對應。非可視化的 DOM 元素不會插入呈現樹中,例如「head」元素。若是元素的 display 屬性值爲「none」,那麼也不會顯示在呈現樹中(可是 visibility 屬性值爲「hidden」的元素仍會顯示)。

Web 安全

常見的web攻擊方式:

  • SQL 注入:輸入時進行了惡意的 SQL 拼裝,致使最後生成的 SQL 有問題。攻擊方式低端,常出如今比較低端的系統。

  • XSS(Cross Site Scripting,跨站腳本攻擊)

前端最多見的攻擊方式,不少網站(如 阮一峯博客)都被 XSS 攻擊過。

原理:經過某種方式(發佈文章、發佈評論等)將一段特定的 JS 代碼隱蔽地輸入進去。JS 代碼一旦執行,那可就不受控制了,由於它跟網頁原有的 JS 有一樣的權限,例如能夠獲取 server 端數據、能夠獲取 cookie 等。因而,攻擊就這樣發生了。

預防:最根本的方式,用正則表達式進行轉換處理,如:

< 替換爲:&lt;
> 替換爲:&gt;
& 替換爲:&amp;
‘ 替換爲:&#x27;
」 替換爲:&quot;
複製代碼

另外,還能夠經過對 cookie控制,好比對敏感的 cookie 增長http-only限制,讓 JS 獲取不到 cookie 的內容。

  • CSRF(Cross-site request forgery,跨站請求僞造) 理論基礎:藉助了一個 cookie的特性,劫持操做者的權限來偷偷地完成某個操做,而不是拿到用戶的信息。

預防 :加入各個層級的權限驗證,如涉及現金交易,必需要輸入密碼或者指紋才行等。

瀏覽器從新渲染(re-render):重繪(repaint)與重排(reflow)

從新渲染,就須要從新生成佈局和從新繪製。前者叫作"重排"(reflow),後者叫作"重繪"(repaint)。"重繪"不必定須要"重排","重排"必然致使"重繪"。重排和重繪會不斷觸發,這是不可避免的。可是,它們很是耗費資源,是致使網頁性能低下的根本緣由。提升網頁性能,就是要下降"重排"和"重繪"的頻率和成本,儘可能少觸發從新渲染。

  • 觸發從新渲染的常見狀況:
    • 修改DOM
    • 修改樣式表
    • 用戶事件(好比鼠標懸停、頁面滾動、輸入框鍵入文字、改變窗口大小等等)
    • 訪問元素屬性:
      • offsetTop/offsetLeft/offsetWidth/offsetHeight
      • scrollTop/scrollLeft/scrollWidth/scrollHeight
      • clientTop/clientLeft/clientWidth/clientHeight
      • getComputedStyle()
  • 提升性能的技巧:
    • css:

      • display(none->block)
      • position(absolute,fixed)
      • visibility(hidden)
    • js:

      • 使用優秀類庫的Virtual DOM(React,Vue)
      • 注意做用域
        • 避免全局查找
        • 避免with
      • 選擇正確的方法
        • 避免不必的查找(性能部分的一部分是和解決問題的算法和方法相關的)
        • 優化循環
        • 展開循環
        • 避免雙重解釋
        • 其它
          • 原生方法較快:
          • switch語句較快
          • 位運算符較快
      • 最小化語句數
        • 多個變量聲明
        • 插入迭代值
        • 使用數組和對象字面量
      • 優化DOM交互(以下)
    • BOM:

      • window.requestAnimationFrame
      • window.requestIdleCallback
    • DOM:

      • document.createDocumentFragment:最小化現場更新
      • innerHTML:要不使用標準DOM方法建立一樣的DOM快得多(內部方法是編譯好而非解釋執行的)。
      • 事件代理:頁面處理程序的數量和頁面響應UED速度負相關。事件處理程序本質上是一種函數,是一種對象,存放在內存中,設置大量的事件處理程序會使內存中的對象變多,Web 程序的性能會變得愈來愈差,用戶體驗很很差。
      • 注意HTMLCollection:最小化對它們的訪問,如下會返回HTMLCollection對象
        • getElementByTagName
        • childNodes
        • attributes
        • 特殊集合:document.forms,document.images etc.
      • cloneNode
      • 集中DOM的讀寫操做
      • 使用 SSR 後端渲染,數據直接輸出到 HTML 中,減小瀏覽器使用 JS 模板渲染頁面 HTML 的時間
    • HTTP: 減小頁面體積,提高網絡加載

      • 靜態資源的壓縮合並(JS 代碼壓縮合並、CSS 代碼壓縮合並、雪碧圖)
      • 靜態資源緩存(資源名稱加 MD5 戳)
      • 使用 CDN 讓資源加載更快

應用示例

  • js中應用到的簡單算法

標記名稱 描述
O(1) 常數:無論有多少值,執行時間恆定,通常表示簡單值和存儲在變量中的值(數組元素訪問)
O(logn) 對數:執行總時間與數量相關,但要完成算法並不必定獲取每一個值。如二分查找
O(n) 線性:執行總時間和數量直接相關。如遍歷操做
O(n2) 平方:執行總時間與數量有關,每一個值至少要獲取n次。如插入排序
  • 代碼示例

  1. 避免全局查找

測試網頁

// bad(3次全局查找) 
function updateUI(){
console.time()
	var images=document.getElementsByTagName('img')
for(var i=0,len=images.length;i<len;i++){
	images[i].title=document.title+'image'+i;
}
return console.timeEnd()
}
// default: 0.5439453125ms
// good(一次全局查找)
function updateUI(){
console.time()
var doc = document
	var images=doc.getElementsByClassName('img')
for(var i=0,len=images.length;i<len;i++){
	images[i].title=doc.title+'image'+i;
}
return console.timeEnd()
}
// default: 0.120849609375ms
複製代碼

2.避免沒必要要的屬性查找(見JS常見算法類型) 最簡單最便捷的算法是常數O(1)

// 四次查找(5,value,10,sum)
var value = 5
var sum = value + 10 
console.log(sum)
// default: 0.375ms
複製代碼

3.使用變量和數組要比訪問對象上屬性(O(n))更高效。

// good
var values = [1, 2, 3]
var sum = values[0] + values[1]
console.log(sum)
// default: 0.298095703125ms
// bad
var values = {a:1, b:2}
var sum = values.a + values.b
console.log(sum)
// default: 0.302978515625ms
複製代碼

4.⚠️注意獲取單個值的屬性查找

// bad
var query = window.location.href.substring(window.location.href.indexOf('?'))
// default: 0.02783203125ms
// good
var url = window.location.href
var query = url.substring(url.indexOf('?'))
// default: 0.02001953125ms
複製代碼
  1. 優化循環
// bad
var values=new Array().fill(10000)
for(var i=0;i<values.length;i++){i}
// default: 0.101806640625ms
// good(使用減值迭代:將終止條件從values.length的O(n)調用簡化成了O(1)調用)
for(var i = values.length-1;i>0;i--){}
// default: 0.01708984375ms (一個數量級)
// good(後測試循環:循環部分已徹底優化,任何進一步的優化只能在處理語句中進行了),這種寫法的弊端就是可讀性不好
var i = values.length - 1
if(i > -1){
    do{
        //
    }while(--i >= 0)
}
// default: 0.011962890625ms
複製代碼

6.避免雙重解釋(JS 代碼運行的同時必須新啓動一個解析器來解析新的代碼。實例化一個新的解析器有不容忽視的開銷)

eval("alert('hi')")// bad
var say = new Function("alert('hi')")// bad
setTimeout('alert("hi")',500)// bad
alert('hi')// good
var say = function (){
    alert('hi')
}// good
setTimeout(function (){
    alert('hi')
},500)// good
複製代碼
  1. 最小化語句數
// 多個變量聲明
var count = 0,
    color = 'red',
    values = [],
    now = new Date()
// 插入迭代值
var value = values[i]; i ++ // bad
var value = values[i++] // good
// 使用數組和對象字面量
var values = new Array(); values[0] = 1; values[1] = 2 // bad(3個語句)
var person = new Object();
person.a = 1;
person.b = 2 // bad(3個語句)
var values = [1,2] // good(2個語句建立和初始化數組)
var person = {a:1,b:2}// good(2個語句建立和初始化對象)
複製代碼
  1. 優化DOM交互(因DOM內部包含了很是多的屬性及方法,DOM 交互很是耗時)
// DOM裏的內容
var div = document.createElement('div'), result =  ''
for(var i in div){
    result += i + ' '
}
/*
"align title lang translate dir dataset hidden tabIndex accessKey draggable spellcheck autocapitalize contentEditable isContentEditable inputMode offsetParent offsetTop offsetLeft offsetWidth offsetHeight style innerText outerText onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmousedown onmouseenter onmouseleave onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange onreset onresize onscroll onseeked onseeking onselect onstalled onsubmit onsuspend ontimeupdate ontoggle onvolumechange onwaiting onwheel onauxclick ongotpointercapture onlostpointercapture onpointerdown onpointermove onpointerup onpointercancel onpointerover onpointerout onpointerenter onpointerleave nonce click focus blur namespaceURI prefix localName tagName id className classList slot attributes shadowRoot assignedSlot innerHTML outerHTML scrollTop scrollLeft scrollWidth scrollHeight clientTop clientLeft clientWidth clientHeight onbeforecopy onbeforecut onbeforepaste oncopy oncut onpaste onsearch onselectstart previousElementSibling nextElementSibling children firstElementChild lastElementChild childElementCount onwebkitfullscreenchange onwebkitfullscreenerror setPointerCapture releasePointerCapture hasPointerCapture hasAttributes getAttributeNames getAttribute getAttributeNS setAttribute setAttributeNS removeAttribute removeAttributeNS hasAttribute hasAttributeNS getAttributeNode getAttributeNodeNS setAttributeNode setAttributeNodeNS removeAttributeNode closest matches webkitMatchesSelector attachShadow getElementsByTagName getElementsByTagNameNS getElementsByClassName insertAdjacentElement insertAdjacentText insertAdjacentHTML requestPointerLock getClientRects getBoundingClientRect scrollIntoView scrollIntoViewIfNeeded animate before after replaceWith remove prepend append querySelector querySelectorAll webkitRequestFullScreen webkitRequestFullscreen attributeStyleMap scroll scrollTo scrollBy createShadowRoot getDestinationInsertionPoints computedStyleMap ELEMENT_NODE ATTRIBUTE_NODE TEXT_NODE CDATA_SECTION_NODE ENTITY_REFERENCE_NODE ENTITY_NODE PROCESSING_INSTRUCTION_NODE COMMENT_NODE DOCUMENT_NODE DOCUMENT_TYPE_NODE DOCUMENT_FRAGMENT_NODE NOTATION_NODE DOCUMENT_POSITION_DISCONNECTED DOCUMENT_POSITION_PRECEDING DOCUMENT_POSITION_FOLLOWING DOCUMENT_POSITION_CONTAINS DOCUMENT_POSITION_CONTAINED_BY DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC nodeType nodeName baseURI isConnected ownerDocument parentNode parentElement childNodes firstChild lastChild previousSibling nextSibling nodeValue textContent hasChildNodes getRootNode normalize cloneNode isEqualNode isSameNode compareDocumentPosition contains lookupPrefix lookupNamespaceURI isDefaultNamespace insertBefore appendChild replaceChild removeChild addEventListener removeEventListener dispatchEvent "
*/
複製代碼
  1. 最小化現場更新
// bad
console.time()
var list = document.createElement('ul'),
    item,
    i
for(i=0;i<10000;i++){
    item = document.createElement('li')
    list.appendChild(item)
    item.appendChild(document.createTextNode('item' + i))
}
document.body.appendChild(list)
console.timeEnd()
// default: 26.902099609375ms

// good
console.time()
var list = document.createElement('ul'),
    fragment = document.createDocumentFragment(),
    item,
    i
for(i=0;i<10000;i++){
    item = document.createElement('li')
    fragment.appendChild(item)
    item.appendChild(document.createTextNode('item' + i))
}
document.body.appendChild(fragment)
console.timeEnd()
// VM7017:12 default: 29.4169921875ms ???
// good (使用innerHTML(編譯好的而非解釋執行的))
console.time()
var list = document.createElement('ul'),
    html = '',
    i
for(i=0;i<10000;i++){
    // list.innerHTML += "<li>Item " + i + "</li>" // 避免
   html += "<li>Item " + i + "</li>"
}
list.innerHTML = html
document.body.appendChild(list)
console.timeEnd()
//default: 25.760986328125ms
複製代碼
  1. 最小化對HTMLColletion的訪問
console.time()
var images = document.getElementsByTagName('img'),
i,
image,
len,
temp
for(i = 0, len=images.length;i<len;i++){
    image = images[i]// 保存當前image,避免了對images的訪問
	temp=image+Image
}
console.timeEnd()
// default: 0.06591796875ms
console.time()
var images = document.getElementsByTagName('img'),
i,
len,
temp
for(i = 0, len=images.length;i<len;i++){
	temp=images[i]+images[i]
}
console.timeEnd()
// VM6253:9 default: 0.115966796875ms
複製代碼

靜態資源緩存

經過連接名稱控制緩存

<script src="a.js"></script>
複製代碼

只有內容改變的時候,連接名稱纔會改變

<script src="b.js"></script>
複製代碼

可經過前端構建工具根據文件內容,爲文件名稱添加 MD5 後綴。

使用CDN(專業的加載優化方案)讓資源加載更快

<script src="https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>
複製代碼

事件節流:函數節流(throttle)與函數去抖(debounce)

  • 概念:
    • 函數節流:若是將水龍頭擰緊直到水是以水滴的形式流出,那你會發現每隔一段時間,就會有一滴水流出。也就是說會預先設定一個執行週期,當調用動做的時刻大於等於執行週期則執行該動做,而後進入下一個新週期。
    • 函數去抖:若是用手指一直按住一個彈簧(debounce),它將不會彈起直到你鬆手爲止。也就是說當調用動做n毫秒後,纔會執行該動做,若在這n毫秒內又調用此動做則將從新計算執行時間。
  • 爲何要節流?

因爲事件頻繁被觸發,於是頻繁執行DOM操做、資源加載等重行爲,致使UI停頓甚至瀏覽器崩潰。

  • 什麼時候考慮節流?

    • window對象的resize、scroll事件;
    • 拖拽時的mousemove事件;
    • 遊戲中的mousedown、keydown、mouseenter,mouseover事件;
    • 文字輸入input、blur、自動完成的keyup事件等。
  • 具體作法:

// throttle
var throttle = function(delay, action){
  var last = 0return function(){
    var curr = +new Date()
    if (curr - last > delay){
      action.apply(this, arguments)
      last = curr 
    }
  }
}
// debounce
var textarea = document.getElementById('btn')
var timeoutId
textarea.addEventListener('keyup', function () {
    if (timeoutId) {
        clearTimeout(timeoutId)
    }
    timeoutId = setTimeout(function () {
        // 觸發 change 事件
    }, 100)
})
複製代碼

web性能優化(web performance)

爲何要性能優化

  • 留住用戶(Performance is about retaining users)
  • 提高轉化率(Performance is about improving conversions)
轉化率就是金錢
  • 關乎用戶體驗(Performance is about the user experience)
  • 以人爲本(Performance is about people)

如何去優化

要發送什麼(Mind what resources you send)

構建高性能應用程序的有效方法是審覈發送給用戶的資源。雖然Chrome開發人員工具中的網絡面板能夠很好地總結給定頁面上使用的全部資源,但若是您到目前爲止還沒有考慮性能,那麼知道從哪裏開始可能會使人生畏。如下是一些建議:

  • 若是您使用Bootstrap或Foundation來構建UI,請問本身是否有必要。這些抽象添加了瀏覽器必須下載,解析和應用於頁面的大量CSS,全部這些都是在特定於站點的CSS進入圖片以前。 Flexbox和Grid在使用相對較少的代碼建立簡單和複雜佈局方面很是出色。因爲CSS是一種render阻塞資源,所以CSS框架的開銷可能會顯着延遲渲染。您應該經過消除沒必要要的開銷來尋求加速渲染,而非儘量依賴於瀏覽器中的工具。

  • JavaScript庫很方便,但並不老是必要的。以jQuery爲例:因爲querySelectorquerySelectorAll等方法,元素選擇獲得了極大的簡化。使用addEventListener能夠輕鬆進行事件綁定。classList,setAttributegetAttribute提供了使用類和元素屬性的簡便方法。若是你必須使用類庫,研究更精簡的替代品。例如,Zepto是一個較小的jQuery替代品,Preact是React的一個小得多的替代品。

  • 並不是全部網站都須要是單頁面應用程序(SPA),由於它們常常普遍使用JavaScript。 JavaScript是咱們在字節的web字節上提供的最昂貴的資源,由於它不只必須下載,還必須解析,編譯和執行。例如,具備優化前端架構的新聞和博客站點能夠像傳統的多頁體驗同樣表現良好。

如何發送(Mind how you send resources)

當您知道須要爲您的應用發送哪些資源以使其成爲您想要的美觀和功能時,請考慮下一步如何發送它們。與預見和預防同樣,交付對於構建快速用戶體驗相當重要。

  • 遷移到HTTP / 2。 HTTP / 2解決了HTTP / 1.1中固有的許多性能問題,例如併發請求限制和缺乏頭壓縮。
  • 使用資源提示加快資源交付。 rel = preload是一個這樣的資源提示,它容許在瀏覽器發現它們以前提早獲取關鍵資源。這能夠對頁面呈現產生明顯的積極影響,並在明智地使用時下降交互時間。 rel = preconnect是另外一個資源提示,能夠掩蓋爲第三方域上託管的資源打開新鏈接的延遲。
  • 現代網站平均提供大量JavaScript和CSS。在HTTP / 1環境中將樣式和腳本捆綁到大型捆綁包中很常見。這樣作是由於大量請求對性能有害。如今再也不是HTTP / 2在場的狀況,由於多個同時請求更便宜。考慮在webpack中使用代碼拆分來限制下載的腳本數量,使其僅限於當前頁面或視圖所需的內容。將CSS分紅較小的模板或特定於組件的文件,並僅包含可能使用它們的資源。

數據規模(Mind how much data you send)

經過一些關於哪些資源適合發送以及如何發送它們的想法,咱們將介紹一些限制發送數據的建議:

  1. 配置服務器以壓縮資源。壓縮會大大減小您發送給用戶的數據量,尤爲是在涉及文本資產的狀況下。 GZIP在這個領域是一種很是優秀的格式,但Brotli壓縮能夠更進一步。可是,要理解壓縮並非性能問題的所有:一些隱式壓縮的文件格式(例如,JPEG,PNG,GIF,WOFF等)不響應壓縮,由於它們已經被壓縮。
  2. 優化圖片以確保您的網站儘量少地發送圖片數據。因爲圖片構成了網絡上每頁平均有效負載的很大一部分,所以圖片優化表明了提高性能的獨特機會。
  3. 客戶端提示可用於根據當前網絡條件和設備特徵定製資源交付。 DPR,Width和Viewport-Width標頭能夠幫助您使用服務器端代碼爲設備提供最佳圖像,並提供更少的標記。 Save-Data標頭能夠幫助您爲明確要求您的用戶提供更輕鬆的應用程序體驗。
  4. NetworkInformation API公開有關用戶網絡鏈接的信息。此信息可用於修改較慢網絡上的用戶的應用程序體驗。
  5. 減小請求(HTTP requests reduction)
  6. 文件壓縮(File compression)
  7. 優化web緩存(Web caching optimization)

Web Caching Optimization reduces server load, bandwidth usage, and latency. CDNs use dedicated web caching software to store copies of documents passing through their system. Leveraging the browser cache is crucial. It is recommended to have a max-age of 7 days in such cases. This saves server time and makes things altogether faster.

  1. 簡化代碼(Code minification)
  2. 矢量圖替換位圖(Replacement of vector graphics)
  3. 避免301 重定向

Redirects are performance killers. Avoid them whenever possible. A redirect will generate additional round-trip times and therefore quickly doubles the time that is required to load the initial HTML document before the browser even starts to load other assets.

  1. 採用基於雲的網站監控(Adopt Cloud-based Website Monitoring)

  2. 資源預加載:Pre-fetching是一種提示瀏覽器預先加載用戶以後可能會使用到的資源的方法。

  • 使用dns-prefetch來提早進行DNS解析,以便以後能夠快速地訪問另外一個主機名(瀏覽器會在加載網頁時對網頁中的域名進行解析緩存,這樣你在以後的訪問時無需進行額外的DNS解析,減小了用戶等待時間,提升了頁面加載速度)。
<link rel="dns-prefetch" href="other.hostname.com">
複製代碼
  • 使用prefetch屬性能夠預先下載資源,不過它的優先級是最低的。
<link rel="prefetch"  href="/some_other_resource.jpeg">
複製代碼
  • Chrome容許使用subresource屬性指定優先級最高的下載資源(當全部屬性爲subresource的資源下載完完畢後,纔會開始下載屬性爲prefetch的資源)。
<link rel="subresource"  href="/some_other_resource.js">
複製代碼
  • prerender能夠預先渲染好頁面並隱藏起來,以後打開這個頁面會跳過渲染階段直接呈如今用戶面前(推薦對用戶接下來必須訪問的頁面進行預渲染,不然得不償失)。
<link rel="prerender"  href="//domain.com/next_page.html">
複製代碼
  1. SSL certificate/ HTTPS

  2. 熱連接保護(Hotlink protection) 限制HTTP引用,以防止他人將您的資源嵌入其餘網站。 熱連接保護將經過禁止其餘網站顯示您的圖像來節省帶寬。

  3. 基礎設施(Infrastructure)

  4. 數據庫優化(Database Optimization)

  5. 使用webworker

(前端)主流框架主流作法案例(見下一篇)

參考連接

相關文章
相關標籤/搜索