詳解Vue如何提取Critical Css

什么是Critical Css

首屏關(guān)鍵css
網(wǎng)頁渲染時漠魏,瀏覽器解析只有在完成 <head> 部分 CSS 樣式的加載倔矾、解析之后才會渲染頁面。這種渲染方式意味著,如果 CSS 文件很大哪自,那么用戶就必須等待很長的時間才能看到渲染結(jié)果丰包。針對這一問題,提出一種非常規(guī)的解決方案壤巷,提高頁面的渲染速度邑彪,這一方案常被稱為 critical rendering path(關(guān)鍵渲染路徑)。我們要做的優(yōu)化就是找出渲染首屏的最小 CSS 集合(Critical CSS)胧华,并把它們寫到 <head> 部分寄症,從而讓瀏覽器接收到 HTML 文件后就盡快渲染出頁面。對于剩余部分的 CSS矩动,我們可以使用異步的方式進行加載有巧。

總結(jié):Critical Css就是渲染首屏的最小CSS集合。

通過criticalcss網(wǎng)站獲取頁面critical css【收費】

Vue-cli客戶端提取Critical css

提取css

vue-cli4為例

css.extract

介紹

  • Type: boolean | Object
  • Default: 生產(chǎn)環(huán)境下是 true悲没,開發(fā)環(huán)境下是 false

是否將組件中的 CSS 提取至一個獨立的 CSS 文件中 (而不是動態(tài)注入到 JavaScript 中的 inline 代碼)篮迎。

同樣當(dāng)構(gòu)建 Web Components 組件時它總是會被禁用 (樣式是 inline 的并注入到了 shadowRoot 中)。

當(dāng)作為一個庫構(gòu)建時示姿,你也可以將其設(shè)置為 false 免得用戶自己導(dǎo)入 CSS甜橱。

提取 CSS 在開發(fā)環(huán)境模式下是默認(rèn)不開啟的,因為它和 CSS 熱重載不兼容峻凫。然而渗鬼,你仍然可以將這個值顯性地設(shè)置為 true 在所有情況下都強制提取。

  • 開發(fā)環(huán)境
    extractfalse荧琼,樣式內(nèi)嵌到 head 中譬胎。
    開發(fā)環(huán)境.png
  • 生產(chǎn)環(huán)境
    extracttrue,樣式分離命锄,外鏈到 head 中堰乔。

形成兩個css文件:

app.[contetHash].css:vue組件抽離出來的css

chunk-vendors.[contentHash].css:第三方庫依賴的css

生產(chǎn)環(huán)境.png

內(nèi)部插件

不足

要么樣式全部內(nèi)嵌在head中導(dǎo)致html文件過大镐侯,要么外鏈,如果出現(xiàn)網(wǎng)絡(luò)問題驶冒,頁面白屏?xí)r間過長苟翻。

針對這一不足,我們看一下在vue客戶端項目中如何提取首屏渲染csscritical css骗污。

提取critical css

使用webpack插件critical-css-webpack-plugin

critical-css-webpack-plugin

項目配置

vue-cli4.x為例

Install
npm install critical-css-webpack-plugin -D
vue.config.js
const criticalCssWebpackPlugin = require('critical-css-webpack-plugin')

/ critical css Options
const criticalCssOptions = {
    // type: boolean/object崇猫,是否inline生成的critical-path css。
    // true:生成html需忿,false:生成css诅炉,object:向`inline-critical`傳遞配置對象
    inline: true,
    // type: string蜡歹,讀取和寫入源的目錄
    base: resolve('dist'),
    // type: string,要操作的html源代碼涕烧,此選項優(yōu)先于src選項
    // html: '',
    // type: array月而,css路徑的數(shù)組
    // css: [],
    // type: string,要操作的html源代碼的位置
    src: 'index.html',
    // type: string议纯,保存操作輸出的位置父款,如果要同時存儲`html`和`css`,使用`html`和`css`的對象
    target: 'index.html',
    // type: integer痹扇,1300铛漓,目標(biāo)視口的寬度
    width: 1300,
    // type: integer,900鲫构,目標(biāo)視口的高度
    height: 900,
    // type: array浓恶,包含寬度和高度的數(shù)組,如果設(shè)置结笨,優(yōu)先于width和height選項
    dimensions: [],
    // type: boolean包晰,是否壓縮生成的critical-path css
    minify: true,
    // type: boolean,小心使用炕吸,從html中刪除inline樣式伐憾,它會基于提取的內(nèi)容生成新的引用,因此可以安全地在多個html文件引用同一樣式文件赫模。刪除每頁的關(guān)鍵css會為每個頁面生成唯一一個異步加載css树肃。意味著你不能跨多頁面緩存
    extract: false,
    // type: boolean,inline圖片
    inlineImages: true,
    // type: array瀑罗,內(nèi)聯(lián)程序開始查詢assets時的目錄/url列表
    assetPaths: [],
    // 設(shè)置base64內(nèi)聯(lián)圖片的最大大行刈臁(以字節(jié)為單位)
    maxImageFileSize: 10240000,
    // type: object/function,critical嘗試相對文檔的assets path
    rebase: undefined,
    // type: array
    ignore: [],
    // type: string斩祭,獲取遠(yuǎn)程src時的用戶代理
    // userAgent: '',
    // type: object,penthouse的配置選項
    penthouse: {
        // propertiesToRemove: ['background'],
        // 強制包含的selector
        forceInclude: ['.card', '.card__list', '.card__main', '.card__img', '.card__article'],
        forceExclude: []
    },
    // type: object劣像,got的配置選項
    request: {},
    // type: string,RFC2617授權(quán):user
    user: undefined,
    // type: string摧玫,RFC2617授權(quán):pass
    pass: undefined,
    // type: boolean耳奕,如果找不到css,則拋出錯誤
    strict: false
};

module.exports = {
    chainWebpack: config => {
        config.plugin('critical')
                .use(criticalCssWebpackPlugin,
                    [
                        criticalCssOptions
                    ]
                ).tap(error => {
                    console.log(error, 'critical css generate error');
                    return error
                })
    }
}

配置完畢后诬像,執(zhí)行npm run build構(gòu)建屋群,查看dist/index.html,可以看到提取的critical css插入到head中坏挠,剩余css仍以外鏈形式引入芍躏。

這樣index.html文件也不會很大,也可以保證在網(wǎng)絡(luò)不穩(wěn)定時癞揉,顯示簡單頁面樣式。

image.png

critical-css-webpack-plugin插件的核心是criticalcritical核心使用penthouse喊熟,接下來再詳解一下criticalpenthouse柏肪。

critical插件

critical插件介紹,使用到核心組件庫penthouse芥牌。

html中提取critical css烦味,并將critical-path內(nèi)聯(lián)到html中

Install
npm i -D critical
Usage

配置文件critical.js

const critical = require('critical');
critical.generate({
  // Inline the generated critical-path CSS
  // - true generates HTML
  // - false generates CSS
  inline: true,
  ...
});

node環(huán)境下執(zhí)行配置文件critical.js即可

node critical.js
Critical Options

摘自https://www.npmjs.com/package/critical

Name Type Default Description
inline boolean/object false Inline critical-path CSS using filamentgroup's loadCSS. Pass an object to configure inline-critical
base string path.dirname(src) or process.cwd() Base directory in which the source and destination are to be written
html string HTML source to be operated against. This option takes precedence over the src option.
css array [] An array of paths to css files, file globs or Vinyl file objects.
src string Location of the HTML source to be operated against
target string or object Location of where to save the output of an operation. Use an object with 'html' and 'css' props if you want to store both
width integer 1300 Width of the target viewport
height integer 900 Height of the target viewport
dimensions array [] An array of objects containing height and width. Takes precedence over width and height if set
minify boolean true Enable minification of generated critical-path CSS
extract boolean false Remove the inlined styles from any stylesheets referenced in the HTML. It generates new references based on extracted content so it's safe to use for multiple HTML files referencing the same stylesheet. Use with caution. Removing the critical CSS per page results in a unique async loaded CSS file for every page. Meaning you can't rely on cache across multiple pages
inlineImages boolean false Inline images
assetPaths array [] List of directories/urls where the inliner should start looking for assets
maxImageFileSize integer 10240 Sets a max file size (in bytes) for base64 inlined images
rebase object or function undefined Critical tries it's best to rebase the asset paths relative to the document. If this doesn't work as expected you can always use this option to control the rebase paths. See postcss-url for details. (https://github.com/pocketjoso/penthouse#usage-1).
ignore array object undefined
userAgent string '' User agent to use when fetching a remote src
penthouse object {} Configuration options for penthouse.
request object {} Configuration options for got.
user string undefined RFC2617 basic authorization: user
pass string undefined RFC2617 basic authorization: pass
strict boolean false Throw an error if no css is found
Global Install And Cli
npm install -g critical
critical test/fixture/index.html --base test/fixture > critical.css

penthouse

penthouse介紹

關(guān)鍵路徑css生成器

Install
npm i -D penthouse
Usage
penthouse({
  url: 'http://google.com',
  cssString: 'body { color: red }'
  ...
})
.then(criticalCss => {
  // use the critical css
  fs.writeFileSync('outfile.css', criticalCss);
})
Options

對應(yīng)critical插件中的penthouseoptions

Name Type Default Description
url string Accessible url. Use file:/// protocol for local html files.
cssString string Original css to extract critical css from
css string Path to original css file on disk (if using instead of cssString)
width integer 1300 Width for critical viewport
height integer 900 Height for critical viewport
screenshots object Configuration for screenshots (not used by default). See Screenshot example
keepLargerMediaQueries boolean false Keep media queries even for width/height values larger than critical viewport.
forceInclude array [] Array of css selectors to keep in critical css, even if not appearing in critical viewport. Strings or regex (f.e. ['.keepMeEvenIfNotSeenInDom', /^.button/])
forceExclude array [] Array of css selectors to remove in critical css, even if appearing in critical viewport. Strings or regex (f.e. ['.doNotKeepMeEvenIfNotSeenInDom', /^.button/])
propertiesToRemove array ['(.)transition(.)', 'cursor', 'pointer-events', '(-webkit-)?tap-highlight-color', '(.*)user-select'] ] Css properties to filter out from critical css
timeout integer 30000 Ms; abort critical CSS generation after this time
puppeteer object Settings for puppeteer. See Custom puppeteer browser example
pageLoadSkipTimeout integer 0 Ms; stop waiting for page load after this time (for sites when page load event is unreliable)
renderWaitTime integer 100 ms; wait time after page load before critical css extraction starts (also before "before" screenshot is taken, if used)
blockJSRequests boolean true set to false to load JS (not recommended)
maxEmbeddedBase64Length integer 1000 characters; strip out inline base64 encoded resources larger than this
maxElementsToCheckPerSelector integer undefined Can be specified to limit nr of elements to inspect per css selector, reducing execution time.
userAgent string 'Penthouse Critical Path CSS Generator' specify which user agent string when loading the page
customPageHeaders object et extra http headers to be sent with the request for the url.
cookies array [] For formatting of each cookie, see Puppeteer setCookie docs
strict boolean false Make Penthouse throw on errors parsing the original CSS. Legacy option, not recommended.
allowedResponseCode number|regex|function Let Penthouse stop if the server response code is not matching this value. number and regex types are tested against the response.status(). A function is also allowed and gets Response as argument. The function should return a boolean.

SSR服務(wù)端提取Critical css

介紹

簡介

SSR服務(wù)端渲染時,管理 CSS 的推薦方法是簡單地使用 *.vue 單個文件組件內(nèi)的<style>壁拉,它提供:

  • 與 HTML 并列同級谬俄,組件作用域 CSS
  • 能夠使用預(yù)處理器(pre-processor)或 PostCSS
  • 開發(fā)過程中熱重載(hot-reload)

更重要的是,vue-style-loadervue-loader 內(nèi)部使用的 loader)弃理,具備一些服務(wù)器端渲染的特殊功能:

  • 客戶端和服務(wù)器端的通用編程體驗溃论。

  • 在使用 bundleRenderer 時,自動注入關(guān)鍵 CSS(critical CSS)痘昌。

  • 如果在服務(wù)器端渲染期間使用钥勋,可以在 HTML 中收集和內(nèi)聯(lián)(使用 template 選項時自動處理)組件的 CSS。在客戶端辆苔,當(dāng)?shù)谝淮问褂迷摻M件時算灸,vue-style-loader 會檢查這個組件是否已經(jīng)具有服務(wù)器內(nèi)聯(lián)(server-inlined)的 CSS - 如果沒有,CSS 將通過 <style> 標(biāo)簽動態(tài)注入驻啤。

demo示例

pages/index.vue為首屏渲染頁面

<template>
    <div class="index">首頁</div>
</template>
<style>
.index {
    color: red;
}
</style>

新建pages/test.vue頁面菲驴,驗證SSR是否會自動注入關(guān)鍵css(critical css

<template>
    <div class="test">測試</div>
</template>
<style>
.test {
    color: red;
}
</style>
  1. index.vue中引入test.vue,服務(wù)啟動后骑冗,test.vue樣式也以style形式內(nèi)嵌在頁面中
  2. 不在index.vue中引入test.vue赊瞬,服務(wù)啟動后,test.vue樣式則沒有內(nèi)嵌在首屏渲染頁面中

總結(jié):SSR服務(wù)端會自動注入首屏渲染關(guān)鍵css沐旨,無需引入其他插件森逮。

提取CSS

nuxt框架配置為例nuxt.config.js

SSR可通過extractCSS將css提取到獨立的文件中,方便緩存磁携。

export default {
    build: {
        extractCSS: true,
    }
}
image.png

總結(jié)

  • 客戶端和服務(wù)端均可通過extract提取css至單獨文件中褒侧,以外鏈形式引入
  • 客戶端只可內(nèi)嵌csshead中,或提取成單獨文件外鏈引入谊迄,不可提取critical css闷供,需要增加額外webpack plugin來提取
  • 服務(wù)端自動注入critical css,不需額外引入其他插件

參考文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末统诺,一起剝皮案震驚了整個濱河市歪脏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粮呢,老刑警劉巖婿失,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钞艇,死亡現(xiàn)場離奇詭異,居然都是意外死亡豪硅,警方通過查閱死者的電腦和手機哩照,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懒浮,“玉大人飘弧,你說我怎么就攤上這事⊙庵” “怎么了次伶?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稽穆。 經(jīng)常有香客問我冠王,道長,這世上最難降的妖魔是什么秧骑? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任版确,我火速辦了婚禮,結(jié)果婚禮上乎折,老公的妹妹穿的比我還像新娘绒疗。我一直安慰自己,他們只是感情好骂澄,可當(dāng)我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布吓蘑。 她就那樣靜靜地躺著,像睡著了一般坟冲。 火紅的嫁衣襯著肌膚如雪磨镶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天健提,我揣著相機與錄音琳猫,去河邊找鬼。 笑死私痹,一個胖子當(dāng)著我的面吹牛脐嫂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播紊遵,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼账千,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暗膜?” 一聲冷哼從身側(cè)響起匀奏,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎学搜,沒想到半個月后娃善,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體论衍,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年聚磺,在試婚紗的時候發(fā)現(xiàn)自己被綠了饲齐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡咧最,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出御雕,到底是詐尸還是另有隱情矢沿,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布酸纲,位于F島的核電站捣鲸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闽坡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一外厂、第九天 我趴在偏房一處隱蔽的房頂上張望代承。 院中可真熱鬧汁蝶,春花似錦、人聲如沸论悴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至察纯,卻和暖如春帕棉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捐寥。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工握恳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乡洼。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像拔稳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巴比,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容