隨著瀏覽器的日新月異,網(wǎng)頁的性能和速度越來越好倒脓,并且對于用戶體驗來說也越來越重要。
現(xiàn)在有很多優(yōu)化頁面的辦法埃脏,比如:靜態(tài)資源的合并和壓縮,code splitting东羹,DNS預(yù)讀取等等愕难。
本文介紹的是另一種優(yōu)化方法:首屏阻塞css優(yōu)化
原理:
首先我們了解一下頁面的基本渲染流程(參考):
webkit渲染過程:
[圖片上傳失敗...(image-93b228-1543318972487)]
Gecko渲染過程:
[圖片上傳失敗...(image-f8fd83-1543318972487)]
那么,為什么要做這種優(yōu)化呢案疲?上面的流程圖就是原因:首先解析html生成dom樹,同時解析css生成css樹麻养,之后結(jié)合兩者生成渲染樹褐啡,然后渲染到屏幕上。不但如此鳖昌,如果css后面有其他javascript备畦,并且css加載時間過長,也會阻塞后面的js執(zhí)行许昨,因為js可能會操作dom節(jié)點或者css樣式懂盐,所以需要等待render樹完成。那么糕档,如果我們能優(yōu)化css莉恼,那么就能大大減少頁面渲染出來的時間,從而提升pv速那,增加黏性俐银,走向編碼巔峰。端仰。捶惜。
怎么做呢:
目前我知道的比較實用的辦法是webpack集成critical,critical是一個提取關(guān)鍵css荔烧,內(nèi)聯(lián)到html中售躁,并且使用preload和noscript兼容加載非關(guān)鍵css的工具坞淮。
那么,我們開門見山陪捷,直接從webpack配置開始:
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 創(chuàng)建html來服務(wù)你的資源
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 提取css到分離的文件,需要webpack4
const HtmlCriticalWebpackPlugin = require('html-critical-webpack-plugin'); // 集成critical的html-webpack-plugin版本
const path = require('path');
// 用于設(shè)置Chromium诺擅,因為Chromium使用npm或者yarn經(jīng)常有問題
process.env['PUPPETEER_EXECUTABLE_PATH'] =
'你電腦中的Chromium地址';
module.exports = {
mode: 'none',
module: {
rules: [
{
test: /\.css$/,
// 使用MiniCssExtractPlugin.loader代替style-loader
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './index.html' }),
new MiniCssExtractPlugin({}),
new HtmlCriticalWebpackPlugin({
base: path.resolve(__dirname, 'dist'),
src: 'index.html',
dest: 'index.html',
inline: true,
minify: true,
extract: true,
width: 375,
height: 565,
// 確保調(diào)用打包后的JS文件
penthouse: {
blockJSRequests: false
}
})
]
};
然后是html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<div class="div"></div>
<h2>hello world</h2>
<div class="mask">這是一個彈窗</div>
</body>
</html>
接著是css文件:
.div {
width: 200px;
height: 100vh;
background-color: red;
}
h2 {
color: blue;
}
.mask {
width: 500px;
height: 500px;
display: none;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
background-color: yellowgreen;
}
運行webpack后市袖,查看打包后的html文件:
// 省略...
<style>
.div {
width: 200px;
height: 100vh;
background-color: red;
}
.mask {
width: 500px;
height: 500px;
display: none;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
background-color: #9acd32;
}
</style>
<link
href="main.80dc2a9c.css"
rel="preload"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript><link href="main.80dc2a9c.css" rel="stylesheet"/></noscript>
// 省略...
代碼倉庫在此,點擊fork進行實戰(zhàn)練習(xí)
可以看到烁涌,h2標簽的css樣式?jīng)]有出現(xiàn)在內(nèi)聯(lián)style里苍碟,而是出現(xiàn)在main.[hash].css中,因為它不再所設(shè)置首屏范圍內(nèi)撮执,這就是所謂的首屏css優(yōu)化微峰。
相關(guān)內(nèi)容
在上面打包后的html文件里,我們看到了有一個link內(nèi)有rel="preload" as="style"
字段抒钱,緊接著下面就有一個noscript
標簽蜓肆,這兩個是做什么的呢?
-
rel="preload" as="style"
: 用于進行頁面預(yù)加載谋币,rel="preload"
通知瀏覽器開始獲取非關(guān)鍵CSS以供之后用仗扬。其關(guān)鍵在于,preload
不阻塞渲染蕾额,無論資源是否加載完成早芭,瀏覽器都會接著繪制頁面。并且诅蝶,搭配as使用退个,可以指定將要預(yù)加載內(nèi)容的類型,可以讓瀏覽器:- 更精確地優(yōu)化資源加載優(yōu)先級调炬。
- 匹配未來的加載需求语盈,在適當?shù)那闆r下,重復(fù)利用同一資源筐眷。
- 為資源應(yīng)用正確的內(nèi)容安全策略黎烈。
- 為資源設(shè)置正確的 Accept 請求頭。
-
noscript
:如果頁面上的腳本類型不受支持或者當前在瀏覽器中關(guān)閉了腳本匀谣,則在HTML<noscript>
元素中定義腳本未被執(zhí)行時的替代內(nèi)容照棋。換句話說,就是當瀏覽器不支持js腳本或者用戶主動關(guān)閉腳本武翎,那么就會展示noscript
里的內(nèi)容烈炭,而critical則是利用這一點做了向后兼容
總結(jié)
利用critical可以大大提高頁面渲染速度,但是由于其使用puppeteer宝恶,所以下載安裝比較麻煩符隙,上面的webpack中使用設(shè)置env中puppeteer位置的方法解決了這一問題趴捅。
文中如若有不對的地方,還望之處霹疫,共同交流拱绑。