github-myBlob
css
script
標籤的話,會判斷是否存在async
或者defer
,前者會並行進行下載並執行 JS,後者會先下載文件,而後等待 HTML 解析完成後順序執行,若是以上都沒有,就會阻塞住渲染流程直到 JS 執行完畢。遇到文件下載的會去下載文件,這裏若是使用 HTTP 2.0 協議的話會極大的提升多圖的下載效率。DOMContentLoaded
事件根據上面的過程能夠看到,頁面的加載過程主要分爲下載、解析、渲染三個步驟,總體能夠從兩個角度來考慮:html
咱們能夠打開 Chrome 的調試工具來分析此階段的性能指標
在創建 TCP 鏈接的階段(HTTP 協議是創建在 TCP 協議之上的)前端
在請求響應的階段vue
依據上面的指標給出如下幾點優化方案(僅供參考)react
條件:擁有多個域名
Chrome 瀏覽器只容許每一個源擁有 6 個 TCP 鏈接,所以能夠經過劃分子域的方式,將多個資源分佈在不一樣子域上用來減小請求隊列的等待時間。然而,劃分子域並非一勞永逸的方式,多個子域意味着更多的 DNS 查詢時間。一般劃分爲 3 到 5 個比較合適。
對如何拆分資源有以下建議:webpack
DNS 解析也是須要時間的,能夠經過預解析的方式來預先得到域名所對應的 IP,方法是在 head 標籤裏寫上幾個 link 標籤ios
<link rel="dns-prefetch" href="https://www.google.com"> <link rel="dns-prefetch" href="https://www.google-analytics.com">
對以上幾個網站提早解析,這個過程是並行的,不會阻塞頁面渲染。git
在開發中,可能會遇到這樣的狀況。有些資源不須要立刻用到,可是但願儘早獲取,這時候就可使用預加載。
預加載實際上是聲明式的 fetch,強制瀏覽器請求資源,而且不會阻塞 onload 事件,可使用如下代碼開啓預加載:github
<link rel="preload" href="http://example.com">
預加載能夠必定程度上下降首屏的加載時間,由於能夠將一些不影響首屏但重要的文件延後加載,惟一缺點就是兼容性很差。web
HTTP 是一個無狀態的面向鏈接的協議,即每一個 HTTP 請求都是獨立的。然而無狀態並不表明 HTTP 不能保持 TCP 鏈接,Keep-Alive 正是 HTTP 協議中保持 TCP 鏈接很是重要的一個屬性。 HTTP1.1 協議中,Keep-Alive 默認打開,使得通訊雙方在完成一次通訊後仍然保持必定時長的鏈接,所以瀏覽器能夠在一個單獨的鏈接上進行多個請求,有效地下降創建 TCP 請求所消耗的時間。
使用 CND 加速能夠減小客戶端到服務器的網絡距離。
還有一種比較流行的作法是讓一些項目依賴走 CDN,好比 vuex、vue-router 這些插件經過外鏈的形式來引入,由於它們都有本身免費的 CDN,這樣能夠減小打包後的文件體積。
緩存對於前端性能優化來講是個很重要的點,良好的緩存策略能夠下降資源的重複加載提升網頁的總體加載速度。
一般瀏覽器緩存策略分爲兩種:強緩存 和 協商緩存。
Expires
和Cache-Control
強緩存表示在緩存期間不須要向服務器發送請求Last-Modified/If-Modified-Since
和ETag/If-None-Match
實現HTTP 頭中與緩存相關的屬性,主要有如下幾個:
(1) Expires: 指定緩存過時的時間,是一個絕對時間,但受客戶端和服務端時鐘和時區差別的影響,是 HTTP/1.0 的產物
形如Expires: Wed, 22 Oct 2018 08:41:00 GMT
(2) Cache-Control:比 Expires 策略更詳細,max-age 優先級比 Expires 高,其值能夠是如下五種狀況
(3) Last-Modified / If-Modified-Since: Last-Modified
表示本地文件最後修改日期,If-Modified-Since
會將上次從服務器獲取的Last-Modified
的值發送給服務器,詢問服務器在該日期後資源是否有更新,有更新的話就會將新的資源發送回來。
可是若是(服務器)在本地打開緩存文件(或者刪了個字符 a 後又填上去),就會形成Last-Modified
被修改,因此在 HTTP / 1.1 出現了ETag
。
(4) Etag / If-None-Match: ETag
相似於文件指紋,If-None-Match
會將當前ETag
發送給服務器,詢問該資源ETag
是否變更,有變更的話就將新的資源發送回來。而且ETag
優先級比Last-Modified
高。
因爲 etag 要使用少數的字符表示一個不定大小的文件(如 etag: "58c4e2a1-f7"),因此 etag 是有重合的風險的,若是網站的信息特別重要,連很小的機率如百萬分之一都不容許,那麼就不要使用 etag 了。使用 etag 的代價是增長了服務器的計算負擔,特別是當文件比較大時。
選擇合適的緩存策略
對於大部分的場景均可以使用強緩存配合協商緩存解決,可是在一些特殊的地方可能須要選擇特殊的緩存策略
Cache-control: no-store
,表示該資源不須要緩存Cache-Control: no-cache
並配合ETag
使用,表示該資源已被緩存,可是每次都會發送請求詢問資源是否更新。Cache-Control: max-age=31536000
並配合策略緩存使用,而後對文件進行指紋處理,一旦文件名變更就會馬上下載新的文件。由於瀏覽器會有併發請求限制,在 HTTP / 1.1 時代,每一個請求都須要創建和斷開,消耗了好幾個 RTT 時間,而且因爲 TCP 慢啓動的緣由,加載體積大的文件會須要更多的時間。
在 HTTP / 2.0 中引入了多路複用,可以讓多個請求使用同一個 TCP 連接,極大的加快了網頁的加載速度。而且還支持 Header 壓縮,進一步的減小了請求的數據大小。
這又涉及到不少知識點了,簡單來講,咱們要儘量地在保證咱們的 App 能正常運行、圖片儘量保證高質量的前提下去壓縮全部用到的文件的體積。好比圖片格式的選擇、去掉咱們代碼中的註釋、空行、無關代碼等。
圖片相關優化
構建工具的使用
壓縮 HTML 文件
能夠把 HTML 的註釋去掉,把行前縮進刪掉,這樣處理的文件能夠明顯減小 HTML 的體積;這樣作幾乎是沒有風險的,除了 pre 標籤不可以去掉行首縮進以外,其餘的都正常。
<script>
標籤位置渲染線程和 JS 引擎線程是互斥的,若是你想首屏渲染的越快,就越不該該在首屏就加載 JS 文件,所以建議將 script 標籤放在 body 標籤底部的緣由。或者使用給 script 標籤添加 defer 或者 async 屬性 。
執行 JS 代碼過長會卡住渲染,對於須要不少時間計算的代碼能夠考慮使用 Webworker。Webworker 可讓咱們另開一個線程執行腳本(這並無改變 JS 單線程的本質,由於新開的線程受控於主線程且不得操做 DOM)而不影響渲染。
懶加載就是將不關鍵的資源延後加載。
懶加載的原理就是隻加載自定義區域(一般是可視區域,但也能夠是即將進入可視區域)內須要加載的東西。對於圖片來講,先設置圖片標籤的src
屬性爲一張佔位圖,將真實的圖片資源放入一個自定義屬性中,當進入自定義區域時,就將自定義屬性替換爲src
屬性,這樣圖片就會去下載資源,實現了圖片懶加載。
懶加載不只能夠用於圖片,也可使用在別的資源上。好比進入可視區域纔開始播放視頻等等。
使用場景好比抽獎動畫展現過程當中預先加載其餘內容,或者電子書閱讀章節的預加載可使切換下一章節時更爲流暢。
執行 JavaScript 的解析和 UI 渲染的兩個瀏覽器線程是互斥的,UI 渲染時 JS 代碼解析終止,反之亦然。
當 頁面佈局和幾何屬性 改變時,就會觸發 迴流。
當 須要更新的只是元素的某些外觀 時,就會觸發 重繪。
使用 CSS 預處理器時注意不要有過多的嵌套,嵌套層次過深會影響瀏覽器查找選擇器的速度,且必定程度上會產生出不少冗餘的字節。
減小 DOM 元素數量,合理利用 :after、:before 等僞類,避免頁面過深的層級嵌套;
優化 JavaScript 性能,減小 DOM 操做次數(或集中操做),能有效規避頁面重繪/重排;
只能說盡量去作優化,如數據分頁、首屏直出、按需加載等
爲觸發頻率較高的函數使用函數節流
SPA:單頁面富應用
動態地重寫頁面的部分與用戶交互而不是加載新的頁面。
優勢:① 先後端分離 ② 頁面之間切換快 ③ 後端只需提供 API
缺點:① 首屏速度慢,由於用戶首次加載 SPA 框架及應用程序的代碼而後才渲染頁面 ② 不利於 SEO
SEO(Search Engine Optimization):搜索引擎優化
經常使用技術:利用 <title> 標籤和 <meta> 標籤的 description
<html> <head> <title>標題內容</title> <meta name="description" content="描述內容"> <meta name="keyword" content="關鍵字1,關鍵字2,—"> </head> </html>
SPA 應用中,一般經過 AJAX 獲取數據,而這裏就難以保證咱們的頁面能被搜索引擎正常收錄到。而且有一些搜索引擎不支持執行 JS 和經過 AJAX 獲取數據,那就更不用提 SEO 了。
對於有些網站而言,SEO 顯得相當重要,例如主要之內容輸出爲主的 Quora、stackoverflow、知乎和豆瓣等等,那如何才能正常使用 SPA 而又不影響 SEO 呢 ?因此有了 SSR
SSR(Server-Side Rendering):服務端渲染
如下內容部分參考《深刻淺出 React 與 Redux》- 程墨
爲了量化網頁性能,咱們定義兩個指標:
在一個 徹底靠瀏覽器端渲染 的應用中,當用戶在瀏覽器中打開一個頁面的時候,最壞狀況下沒有任何緩存,須要等待三個 HTTP 請求才能到達 TTFP 的時間點:
而對於服務器端渲染,由於獲取 HTTP 請求以後就會返回全部有內容的 HTML,因此在一個 HTTP 的週期以後就會提供給瀏覽器有意義的內容,因此首次渲染時間 TTFP 會優於徹底依賴於瀏覽器端渲染的頁面。
除了更短的 TTFP,服務器端渲染還有一個好處就是利於搜索引擎優化,雖然某些搜索引擎已經可以索引瀏覽器端渲染的網頁,可是畢竟不是全部搜索引擎都能作到這一點,讓搜索引擎可以索引到應用頁面的最直接方法就是提供完整 HTML
上面的性能對比只是理論上的分析,實際上,採用服務器端渲染是否能得到更好的 TTFP 有多方面因素。
一、服務器端產生的 HTML 過大是否會影響性能?
由於服務器端渲染返回的是完整的 HTML,那麼下載這個 HTML 的時間也會增加。
二、服務器端渲染的運算消耗是不是服務器可以承擔得起的?
瀏覽器端渲染的方案下,服務器只提供靜態資源,壓力被分攤到了訪問用戶的瀏覽器中;若是使用服務器端渲染,每一個頁面請求都要產生 HTML 頁面,這樣服務器的壓力就會很大。
React 並非給服務器端渲染設計的,若是應用對 TTFP 要求不高,也不但願對 React 頁面進行搜索引擎優化,那麼沒有必要使用「同構」來增長開發難度;若是但願應用的性能能更進一步,並且服務器運算資源充足,那麼能夠嘗試。對 Vue 而言應該也是一樣的道理。
最後咱們來總結下服務端渲染理論上的優缺點:
優勢:
缺點:
一、第三方庫走 cdn
例如:
<script src="//cdn.bootcss.com/vue/2.2.5/vue.min.js"></script> <script src="//cdn.bootcss.com/vue-router/2.3.0/vue-router.min.js"></script> <script src="//cdn.bootcss.com/vuex/2.2.1/vuex.min.js"></script> <script src="//cdn.bootcss.com/axios/0.15.3/axios.min.js"></script>
在 webpack 裏有個 externals 選項,能夠忽略不須要打包的庫
https://webpack.js.org/configuration/externals/#root
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', externals: { 'vue': 'Vue', 'vue-router': 'VueRouter', 'vuex': 'Vuex', 'axios': 'axios' }, output: { ... } }
二、路由懶加載
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ routes: [ { path: '/ebook', component: () => import('./views/ebook/index.vue'), // 路由懶加載,這裏用的是ES6的語法 import()函數是動態加載 import 是靜態加載 children: [ // 動態路由, 能夠傳遞路徑參數 { path: ':fileName', component: () => import('./components/ebook/EbookReader.vue') } ] }, { path: '/store', component: () => import('./views/store/index.vue'), redirect: '/store/shelf', // #/store -> #/store/home ... } ] })
三、使用懶加載插件 Vue-Loader
具體的使用能夠參考 這篇文章 或者去看官方文檔
step1:cnpm install vue-lazyload --save
step2:main.js導入
import VueLazyLoad from 'vue-lazyload' Vue.use(VueLazyload)
step3:<img class="item-pic" v-lazy="newItem.picUrl"/>
vue 文件中將須要懶加載的圖片綁定v-bind:src
修改成v-lazy
這只是圖片懶加載,還有不少其餘可選配置
四、v-if
與v-show
的選擇
通常來講,v-if
有更高的切換開銷,而v-show
有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用v-show
較好;若是在運行時條件不多改變,則使用v-if
較好。
一、單個組件的優化:更改 shouldComponentUpdate 函數的默認實現,根據每一個 React 組件的內在邏輯定製其行爲,減小沒必要要的從新渲染
shouldComponentUpdate(nextProps, nextState) { // 假設影響渲染內容的 prop 只有 completed 和 text,只須要確保 // 這兩個 prop 沒有變化,函數就能夠返回 false return (nextProps.completed !== this.props.completed) || (nextProps.text !== this.props.text) }
二、使用 immutable.js 解決複雜數據 diff、clone 等問題。
immutable.js 實現原理:持久化數據結構,也就是使用舊數據建立新數據時,要保證舊數據同時可用且不變。同時爲了不 deepCopy 把全部節點都複製一遍帶來的性能損耗,Immutable 使用告終構共享,即若是對象樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享。
三、在 constructor() 裏作 this 綁定
當在 render() 裏使用事件處理方法時,提早在構造函數裏把 this 綁定上去(若是須要的話),由於在每次 render 過程當中, 再調用 bind 都會新建一個新的函數,浪費資源.
// bad class App extends React.Component { onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv.bind(this)} />; } } // good class App extends React.Component { constructor(props) { super(props); this.onClickDiv = this.onClickDiv.bind(this); } onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv} />; } }
四、基於路由的代碼分割
使用React.lazy
和React Router
來配置基於路由的代碼分割
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import React, { Suspense, lazy } from 'react'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </Suspense> </Router> );
http://www.javashuo.com/article/p-ueqxqjhb-dz.html
http://www.javashuo.com/article/p-hiiezftc-do.html
http://www.javashuo.com/article/p-maytbjvw-kv.html
https://www.jianshu.com/p/333f390f2e84
https://yuchengkai.cn/docs/frontend/