本文將從瀏覽器輸入url到頁面加載完成中經歷的各個階段來探討web前端性能能夠優化的點css
無優化點html
傳送門:https://www.cnblogs.com/chenq...前端
服務器對靜態資源設置瀏覽器緩存信息,瀏覽器在有緩存的狀況下直接從本地讀取資源。vue
地址欄輸入的域名並非最後資源所在的真實位置,域名只是與IP地址的一個映射。網絡服務器的IP地址那麼多,咱們不可能去記一串串的數字,所以域名就產生了,域名解析的過程實際是將域名還原爲IP地址的過程。
順便介紹下,無優化點react
DNS解析後獲得了服務器的ip,接下來就是和服務器創建起鏈接,這經過TCP的三次握手完成。
具體爲:
瀏覽器:你好~你在嗎~我能和你聊會天嗎
服務器:嗯!我在~咱們聊會吧~
瀏覽器:好的,那咱們開始咯webpack
順便介紹下,無優化點,順便再說下四次告別es6
TCP鏈接後數據的傳輸是雙向的,那麼當瀏覽器不想說話了,就會發出停止鏈接的信號標誌,服務器收到後會回一個確認標誌給瀏覽器,這時瀏覽器就不能再向服務器傳輸數據了,可是還能夠發送信號。爲了對服務器的尊重,等服務器把話說完後纔會發一個結束標誌給瀏覽器,這時瀏覽器知道服務器也向本身傳輸完數據了,回一個確認標誌過去,才真正結束此次TCP鏈接。web
完整的HTTP請求包含請求起始行、請求頭部、請求主體三部分。緩存信息是存儲在請求頭中,在階段二上的鏈接有介紹。json
服務器在收到瀏覽器發送的HTTP請求以後,會將收到的HTTP報文封裝成HTTP的Request對象,並經過不一樣的Web服務器進行處理,處理完的結果以HTTP的Response對象返回,主要包括狀態碼,響應頭,響應報文三個部分。響應體爲服務器返回給瀏覽器的信息,主要由HTML,css,js,圖片文件組成。
傳送門: https://www.cnblogs.com/tootw...segmentfault
js是會阻塞頁面渲染的,那麼解決方法有不少,能夠把js放在body的底部,或者是異步加載js。
同步異步加載詳解傳送門:https://blog.csdn.net/qq_3498...
淺談script標籤的defer和async傳送門:https://segmentfault.com/a/11...
前端頁面渲染時會根據DOM結構生成一個DOM樹,而後加上CSS樣式生成渲染樹。若是CSS文件放在<head>標籤中,則CSS RuleTree會先於DOM樹完成構建,以後瀏覽器就能夠邊構建DOM樹邊完成渲染;反之,CSS文件放在全部頁面標籤以後,好比<body/>以前,那麼當DOM樹構建完成了,渲染樹才構建,瀏覽器不得再也不從新渲染整個頁面,這樣形成了資源的浪費。並且頁面還可能會出現閃跳的感受,或者白屏或者佈局混亂或者樣式很醜,直到CSS加載完成,頁面重繪才能恢復正常。所以,通常來說,css標籤應放在標籤之間。但若是css文件較大,會讓首頁白屏時間更長,因此並非說把css都放頂部是一個完美的方法。權衡利弊,應該把必須的css(js)放頂部,把不那麼重要的css(js)放底部。--------------------- 做者:JiajiaAz 來源:CSDN 原文:https://blog.csdn.net/qq_3265...
概念:
訪問頁面時,先把img元素的背景圖片src替換成一張佔位圖,這樣只需請求一次,當圖片出如今瀏覽器的可視區域內時,再設置圖片的真實路徑,顯示圖片。方法:
頁面中的img元素,若沒有src屬性,瀏覽器就不會發出請求去下載圖片,只有經過Javascript設置了圖片路徑,瀏覽器纔會發送請求。
1)懶加載先在頁面中把須要延遲加載的圖片統一使用一張佔位圖進行佔位,把真正的路徑存在元素「data-url」屬性裏。
2)頁面加載完成後,經過scrollTop判斷圖片是否在用戶的視野,若是在,則將 data-url的值取出來存放到src中。
在vue或者react的項目下,怎麼解決頁面渲染問題呢,畢竟初始的html就是一個空白頁面,頁面渲染全靠js的render,若是這個入口js過大,必然會致使頁面白屏時間過長。
若是能夠對入口的js進行代碼分割,把後期纔會用到的js先獨立出來,等用時再引入那麼就能夠大大減小初始js的體積了,首屏頁面渲染起來天然也快。
<button>click to load js</button> <script> document.querySelector('button').onclick = () => { let loadJs = document.createElement('script') loadJs.src = './load.js' document.body.appendChild(loadJs) loadJs.onload = () => { alert("finish loading!") } } </script>
嗯簡單來講原理就是這麼實現的,設置個觸發點,觸發後建立個src爲須要引進js路徑的script Dom,而後設置下加載後的回調函數..
寫在主入口main.js中👇
document.querySelector('button').onclick = () => { require.ensure([], ()=> { // 引入異步加載的js let loadJS = require('./asyncJS') alert(loadJS.flag) }, 'asyncJS') }
webpack的配置文件👇
output: { path: path.resolve(__dirname, './dist'), filename: '[name].bundle.js', publicPath: '../dist/', chunkFilename: 'chunks/[name]-[hash].js' }
webpack 在編譯時,會靜態地解析代碼中的require.ensure(),同時將模塊添加到一個分開的 chunk 當中。這個新的 chunk 會被 webpack 經過 jsonp來按需加載。語法以下:
require.ensure(dependencies: String[], callback: function(require), chunkName: String)
依賴 - dependencies
這是一個字符串數組,經過這個參數,在全部的回調函數的代碼被執行前,咱們能夠將全部須要用到的模塊進行聲明。回調 - callback
當全部的依賴都加載完成後,webpack會執行這個回調函數。require對象的一個實現會做爲一個參數傳遞給這個回調函數。所以,咱們能夠進一步 require() 依賴和其它模塊提供下一步的執行。chunk名稱 - chunkName
chunkName 是提供給這個特定的 require.ensure() 的 chunk的名稱。經過提供 require.ensure() 不一樣執行點相同的名稱,咱們能夠保證全部的依賴都會一塊兒放進相同的 文件束(bundle)。
打包出來的目錄👇
其中chunks文件夾裏的內容就是從main.js中分離出來的,會在被須要時再引入到項目中。
頁面加載時是這樣👇
點了按鈕就是這樣了👇
順便介紹下require.ensure() 的坑點
空數組做爲參數 require.ensure([], function(require){
require('./a.js'); }); 以上代碼保證了拆分點被建立,並且 a.js 被 webpack 分開打包。依賴做爲參數 require.ensure(['./a.js'], function(require) {
require('./b.js'); }); 上面代碼, a.js 和 b.js 都被打包到一塊兒,並且從主文件束中拆分出來。但只有 b.js 的內容被執行。a.js 的內容僅僅是可被使用,但並無被輸出。想去執行 a.js,咱們須要異步地引用它,如 require('./a.js'),讓它的 JavaScritp 被執行。
說了這麼多,繞回來本題中心,這裏以vue爲例子來實現按需異步加載功能。按需加載什麼呢?固然是組件啦!
異步組件 - 官網解釋
在大型應用中,咱們可能須要將應用分割成小一些的代碼塊,而且只在須要的時候才從服務器加載一個模塊。爲了簡化,Vue
容許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue
只有在這個組件須要被渲染的時候纔會觸發該工廠函數,且會把結果緩存起來供將來重渲染。例如:
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { // 向 `resolve` 回調傳遞組件定義 resolve({ template: '<div>I am async!</div>' }) }, 1000) })
如你所見,這個工廠函數會收到一個 resolve 回調,這個回調函數會在你從服務器獲得組件定義的時候被調用。你也能夠調用 reject(reason) 來表示加載失敗。
結合前面講的直接上例子,基於異步組件咱們能夠實現vue的異步路由👇
export default new Router({ routes: [ { path: '/', name: 'HelloWorld', component: resolve => { require.ensure([], () => { resolve(require('@/components/HelloWorld.vue')) }) } }, { path: '/other', name: 'other', component: resolve => { require.ensure([], () => { resolve(require('@/components/Other.vue')) }) } } ] })
固然require.ensure這種寫法比較舊,就是對webpack的兼容性會好點,如今結合es6的寫法會更加簡潔
你也能夠在工廠函數中返回一個 Promise,因此把 webpack 2 和 ES2015 語法加在一塊兒,咱們能夠寫成這樣:
Vue.component( 'async-webpack-example', // 這個 `import` 函數會返回一個 `Promise` 對象。 () => import('./my-async-component') )
export default new Router({ routes: [ { path: '/', name: 'HelloWorld', component: () => import('@/components/HelloWorld.vue') }, { path: '/other', name: 'Other', component: () => import('@/components/Other.vue') } ] })
嗯這樣就好了
順便也提下如何處理加載狀態
這裏的異步組件工廠函數也能夠返回一個以下格式的對象:
const AsyncComponent = () => ({ // 須要加載的組件 (應該是一個 `Promise` 對象) component: import('./MyComponent.vue'), // 異步組件加載時使用的組件 loading: LoadingComponent, // 加載失敗時使用的組件 error: ErrorComponent, // 展現加載時組件的延時時間。默認值是 200 (毫秒) delay: 200, // 若是提供了超時時間且組件加載也超時了, // 則使用加載失敗時使用的組件。默認值是:`Infinity` timeout: 3000 })