- loading:
- 在頁面最前面加loading相關的html和css
- 結合html-webpack-plugin插入loading(html+css)
- prerender-spa-plugin(`暫記`)
- 內聯CSS(`不考慮緩存策略的話`)
- 骨架屏<br/>
Q: css文件的加載阻塞骨架屏的渲染<BR/>
A:使用preload,僞代碼以下:
![](https://user-gold-cdn.xitu.io/2019/8/16/16c9923d9d085200?w=758&h=429&f=png&s=205515)
複製代碼
動態polyfill
User-Agent
頭,判斷其支持的特性,返回合適的polyfill。webpack相關優化
Tree Shaking
:
modules: false
; pachage.json
配置sideEffects
。import { capitalize } from 'lodash-es';
複製代碼
import {a} from xx
轉換爲
import {a} from 'xx/a'
複製代碼
// a.js
export function a() {}
// b.js
export function b(){}
// package/index.js
import a from './a'
import b from './b'
export { a, b }
// app.js
import {a} from 'package'
console.log(a)
複製代碼
結果treeShaking後:// a.js
export function a() {}
// b.js 再也不導出 function b(){}
function b() {}
// package/index.js 再也不導出 b 模塊
import a from './a'
import b from './b'
export { a }
// app.js
import {a} from 'package'
console.log(a)
複製代碼
配合 webpack 的 scope hoisting
和 uglify
以後,b 模塊的痕跡會被徹底抹殺掉。// b.js
export function b(v) { reutrn v }
console.log(b(1))
複製代碼
處理後 b 模塊內容變成了:// b.js
console.log(function (v){return v}(1))
複製代碼
注意:b文件中保留了console的代碼。模塊/包
都會被完整的移除。(上面的b文件被移除)shim
或者polyfill
慎用!"sideEffects": [
"*.css",
"src/javascript/base/da.js"
]
複製代碼
splitChunks
import()
懶加載
scroll
事件Intersection Observer
獲取元素可見性placeholder
(提早佔位)閃屏
的現象。三方組件:react-placeholder
、react-hold
(緩存時注意拉取CGI接口的參數處理)
接口動靜分離 & Redis緩存
(node層緩存html)
PWA直出優化
(前端緩存html)
Web App Manifest
,Service Worker
,Push API & Notification API
,App Shell & App Skeleton
promise-based
webworker
)load
事件註冊//只會對topics/下面的路徑進行優化
navigator.serviceWorker.register('/topics/sw.js');
複製代碼
生命週期javascript
指定serviceworkerJS文件的位置,加載解析執行;load事件中註冊css
將指定的靜態資源進行離線緩存html
對舊緩存作刪除等處理;接管控制權前端
SW更新機制
java
背景:
SW
沒有自動更新的邏輯,它須要在頁面加載(一次跳轉)以後纔會去請求sw.js
解決:因爲瀏覽器判斷sw.js
是否更新是經過字節方式,所以修改cacheName
會從新觸發install
並緩存資源。此外,在activate
事件中,咱們須要檢查cacheName
是否變化,若是變化則表示有了新的緩存資源,原有緩存須要刪除。node
SW.js
文件下載,並觸發install
事件。SW
還在工做,新的SW
進入waiting
狀態(此時兩個SW
同時存在,舊的SW
掌管當前頁面)。
二次跳轉
)才能展現最新的頁面。self.skipWaiting()
://caches是全局變量
self.addEventListener('install',e =>{
e.waitUntil(
caches.open(cacheStorageKey)
.then(cache => cache.addAll(cacheList))
.then(() => self.skipWaiting())
)
})
複製代碼
waiting
狀態,因此在waiting
階段,採用必定的策略來進行頁面的刷新:如彈窗提示用戶是否刷新,若刷新則調用wb.messageSW({type: 'SKIP_WAITING'});
觸發message事件。【webWorker】main.js中作相關邏輯處理,經過postMessage傳遞消息控制worker
:self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
// the new v2 Service Worker will immediately kill the old v1 activated Service Worker once the v2 Service Worker installs.
self.skipWaiting();
}
});
複製代碼
activate
事件;能夠在此處對舊緩存作刪除等處理;不重刷,接管控制權:self.clients.claim()
self.addEventListener('activate',function(e){
e.waitUntil(
//獲取全部cache名稱
caches.keys().then(cacheNames => {
return Promise.all(
// 獲取全部不一樣於當前版本名稱cache下的內容
cacheNames.filter(cacheNames => {
return cacheNames !== cacheStorageKey
}).map(cacheNames => {
return caches.delete(cacheNames)
})
)
}).then(() => {
//直接接管當前頁面的權限
return self.clients.claim()
})
)
})
複製代碼
注意:react
sw.js
和manifest.json
靜態資源的緩存策略:不緩存,必須校驗
if('serviceWorker' in navigator) {
fetch('./cas').then(() => {
if(降級) {
//註銷掉全部sw
unregister();
}else {
//註冊
register();
}
})
}
複製代碼
首次啓動優化
背景:首次加載時沒有資源,因此會走線上,等於沒優化
方案:構建時,把整個項目用到的資源輸出到一個list,而後inline到sw.js。當sw install,就會把這個list的資源所有請求進行緩存。這樣作的結果是,不管用戶第一次進入咱們站點的哪一個頁面,都會把整個站點全部的資源都加載回來並緩存。 webpack
涉及內容:
- link相關(rel、media)
- defer、async
- 緩存(4種緩存、緩存策略、ServiceWork
)
- 優化網絡(H2 Push
、Preload/Prefetch
、域名拆分)
-推送JSON
/json內聯
,加速首頁渲染
- 瀏覽器中各資源加載的優先級git
當咱們須要某些網絡資源時,加載和執行每每耦合在一塊兒,下載完當即執行,而加載過程是阻塞式的,延長了onload時間。所以如何在資源執行前預加載資源,減小等待網絡的開銷即是咱們要探討的問題。github
附一張不一樣資源瀏覽器優先級的圖示(來源):
async/defer: 無阻塞加載
DOMContentLoaded
事件觸發前執行DOMContentLoaded
事件觸發前執行,所以最好只包含一個延遲腳本不足:僅限於腳本資源;執行時機不可控或存在執行順序問題
,用於非關鍵資源。
使用ajax加載資源:能夠實現預加載。
不足:優先級較低,沒法對首屏資源提早加載。
Webkit瀏覽器預測解析:chrome的預加載掃描器html-preload-scanner
經過掃描節點中的 "src"
, "link"
等屬性,找到外部鏈接資源後進行預加載,避免了資源加載的等待時間,一樣實現了提早加載以及加載和執行分離。
原始解析作法:
Server Push 圖片來源
Link: <https://example.com/other/styles.css>; rel=preload; as=style;
複製代碼
僅預加載,不推送:
Link: <https://example.com/other/styles.css>; rel=preload; as=style;nopush
複製代碼
目標:減小請求數量和提升頁面加載速度。
特色:多頁面共享push cache(動態數據json除外
)
適用場景:若是不推送這個資源,瀏覽器就會請求這個資源。
須要注意:要確保沒有發起沒必要要的推送,浪費流量。可使用preload標籤代替,或者在HTTP頭中加nopush
屬性。
【若是服務器或者瀏覽器不支持 HTTP/2
,那麼瀏覽器就會按照 preload 來處理這個頭信息,預加載指定的資源文件。】
不足:
Edge和Safari
的支持很差,慎用使用一次
Golomb-compressed sets
算法生成指紋,編碼爲base64,而後存入Cookiepush cache: 只在會話中存在,一旦會話結束就會被釋放
;內存緩存、Service Worker緩存、Disk緩存、Push緩存
)【擴展】
問題1:不只js渲染阻塞,同時js執行後可能獲取一些數據(JSON),才能真正渲染完成,如何解決?
- 用於動態資源的提早推送,注意參數需固定,不帶隨機變量的
- 服務端渲染(直出同構)
- 內聯JSON(目前有些工程使用此方法傳遞同步數據)
另外兩種較常見的渲染方式圖片來源:
fetch
,能夠強制瀏覽器請求資源,同時不阻塞文檔 onload
事件。當前頁面使用,儘早下載,優先級較高;內存緩存
(沒有設置資源的緩存策略時)中。let { relList } = document.createElement('link');
return relList && relList.supports && relList.supports('preload');
複製代碼
//link標籤
<link rel="preload" as="style" herf="./a.css"/>
<link rel="prefetch" as="script" href="./b.js"/>
//動態建立
let preLink = document.createElement('link');
preLink.rel ='prefetch'; //感受動態建立不適合preload
prelink.as = 'script';
preLink.href = './a.js';
複製代碼
不一樣值代表資源類型,對應的優先級不一樣:style
, script
, image
, media
, document
, font
。 問題: 官方說法:不帶 「as」
屬性的 preload 的優先級將會等同於異步請求。 測試:沒有發請求。
as='style'
和as='script'
預加載,會形成二次下載anonymous
匿名,不帶認證信息),一樣會形成二次下載preload字體時即便同域也須要帶crossOrigin,不然一樣會形成二次下載 Requests without credentials use a separate connection
。來源twitter
//head中
<link rel="preload" href="https://abs.twimg.com/k/zh-cn/init.zh-cn.3b38ddbf651139df6007.js" as="script">
//body底部
<script src="https://abs.twimg.com/k/zh-cn/init.zh-cn.3b38ddbf651139df6007.js" async></script>
複製代碼
//若支持preload,異步下載完不會當即執行
<link rel="preload" >
//下載完當即應用到DOM樹
<link rel="stylesheet" >
//異步下載,只有打印的時候纔會應用,不符合則不會應用,所以不會阻塞渲染
<link rel="stylesheet" media='print' >
複製代碼
// 1. 支持preload:
//因爲preload只是獲取樣式,不會當即應用,所以使用onload改變link的rel使其當即生效9)
<link rel="preload" href="style.css" as="style" onload="this.onload = null;this.rel ='stylesheet'">
注:設置onload=null主要是由於有些瀏覽器會在rel改變時再次出發load事件。
// 2. 不支持preload
// 1)獲取所有link
let links = document.getElementsByTagName("link");
// 2)緩存每一個link的media
var finalMedia = link.media || "all";
// 3)改變link的rel和media(異步下載但不會應用)
link.rel = "stylesheet";
link.media = "only x";
// 4)若是綁定onload事件(爲了啓用media)
if( link.addEventListener ){
link.addEventListener( "load", enableStylesheet );
} else if( link.attachEvent ){
link.attachEvent( "onload", enableStylesheet );
}
// 5)爲了應對舊的瀏覽器不支持link的onload事件
setTimeout( enableStylesheet, 3000 );
// 6)enableStylesheet回調,將media恢復,樣式當即應用
link.setAttribute( "onload", null );
link.media = finalMedia;
複製代碼
適用於對於首頁無關的樣式:因爲preload的資源,可以異步加載樣式,所以能夠避免在加載首頁無關樣式時阻塞初始渲染。
對於首頁初始渲染中重要的樣式:
1)內聯 (注意,會將靜態資源的緩存策略與頁面的緩存策略捆綁
)
2)HTTP/2的serverPush
知道了preload和prefetch的用途,那如何結合項目實踐呢?因爲webpack目前基本是項目必備,因此首先介紹結合webpack的使用;而後對quiklink進行簡單介紹。
PreloadWebpackPlugin
經常使用的配置以下:
new PreloadWebpackPlugin({
//preload or prefetch方式
rel: '',
/*
*即<link as='' />中的as,代表資源類型,不一樣的類型決定了不一樣的執行優先級
*好比:script的優先級大於style
*/
as: '',
//排除的html頁面集合,即只關聯要配置的頁面
excludeHtmlNames: [],
//所關聯頁面須要使用preload或prefetch的資源
include: []
})
複製代碼
其中include的兩種使用:
import()
動態導入的模塊。可使用prefetch
方式異步加載模塊;```
include: ['vendor', 'index']
```
複製代碼
注意事項
HtmlWebpackPlugin
插件使用HtmlWebpackPlugin
後面,由於PreloadWebpackPlugin
須要使用其提供的hook鉤子將構造的<link>
插入html中:plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin()
]
複製代碼
使用效果 對某個頁面中include的資源,最終會在對應頁面head中插入link標籤:
<link as="script" href="/common.js" rel="preload">
<link as="script" href="/asyncChunk.js" rel="prefetch">
複製代碼
當真正使用時,因爲已經下載到本地,直接讀取執行,性能獲得較大的提高。
好處:拆分chunk,減小首屏js體積。
若是工程沒有使用HtmlWebpackPlugin,能夠對動態導入的資源作以下處理:
import(/* webpackPrefetch: true */)
import(/* webpackPreload: true */)
複製代碼
【版本限制】需webpack v4.6.0+ 才支持預取和預加載。本地測試後,發現prefetch可用,preload無效(有成功的煩請告知)。
工做原理 經過獲取頁面中a
標籤的href
,試圖更快的加載接下來可能要訪問的頁面。
IntersectionObserver(交叉觀察器): 檢測當前視口的links
let target= document.getElementById('a');
io = new IntersectionObserver(
entries => {},
{
threshold: [1] //交叉區域爲1時會觸發callback
}
);
io.observe(target);
複製代碼
【備註】常規的主要是經過getBoundingClientRect()
獲取元素在視口中的詳細位置,來實現滾動加載以及吸附等功能。
requestIdleCallback:等到瀏覽器空閒時
【備註】注意其和requestAnimationFrame
的區別
檢查當前的網絡環境:navigator.connection.effectiveType //4G、2G...
prefetch緩存的待下載的url
小巧的js庫,使用瞭如上4個特性,每個都值得細細品味。
工做流程:
判斷當前網絡情況,若使用的是2G或者開啓了省流模式(data-saver
),則不作處理
data-saver: The user may enable such preference, if made available by the user agent, due to high data transfer costs, slow connection speeds, or other reasons.
題外話:prefetch有點偷流量的意思,我想看什麼才消耗對應資源產生的流量,而prefetch擅自爲我作主,偷偷下載不少我可能並不須要的資源(在早前流量特貴的時候這麼作,估計會被打死...)。
三種下載資源的方式:fetch
、xhr
、<link rel=prefetch href="" />
使用說明
綜合來看,PreloadWebpackPlugin
更適合對chunk而非html文件的處理;而quikLink
更適合博客類的網站,或者服務端渲染的頁面,這樣才能實現"秒開"的預期效果。
【歡迎留言】本文是否對你有幫助,亦或有所
遺漏筆誤
等,煩請告知。