详解Vue如何提取Critical Css

什么是Critical Css

首屏关键css
网页渲染时,浏览器解析只有在完成 <head> 部分 CSS 样式的加载、解析之后才会渲染页面。这种渲染方式意味着,如果 CSS 文件很大,那么用户就必须等待很长的时间才能看到渲染结果。针对这一问题,提出一种非常规的解决方案,提高页面的渲染速度,这一方案常被称为 critical rendering path(关键渲染路径)。我们要做的优化就是找出渲染首屏的最小 CSS 集合(Critical CSS),并把它们写到 <head> 部分,从而让浏览器接收到 HTML 文件后就尽快渲染出页面。对于剩余部分的 CSS,我们可以使用异步的方式进行加载。

总结:Critical Css就是渲染首屏的最小CSS集合。

通过criticalcss网站获取页面critical css【收费】

Vue-cli客户端提取Critical css

提取css

vue-cli4为例

css.extract

介绍

  • Type: boolean | Object
  • Default: 生产环境下是 true,开发环境下是 false

是否将组件中的 CSS 提取至一个独立的 CSS 文件中 (而不是动态注入到 JavaScript 中的 inline 代码)。

同样当构建 Web Components 组件时它总是会被禁用 (样式是 inline 的并注入到了 shadowRoot 中)。

当作为一个库构建时,你也可以将其设置为 false 免得用户自己导入 CSS。

提取 CSS 在开发环境模式下是默认不开启的,因为它和 CSS 热重载不兼容。然而,你仍然可以将这个值显性地设置为 true 在所有情况下都强制提取。

  • 开发环境
    extractfalse,样式内嵌到 head 中。
    开发环境.png
  • 生产环境
    extracttrue,样式分离,外链到 head 中。

形成两个css文件:

app.[contetHash].css:vue组件抽离出来的css

chunk-vendors.[contentHash].css:第三方库依赖的css

生产环境.png

内部插件

不足

要么样式全部内嵌在head中导致html文件过大,要么外链,如果出现网络问题,页面白屏时间过长。

针对这一不足,我们看一下在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源代码,此选项优先于src选项
    // html: '',
    // type: array,css路径的数组
    // css: [],
    // type: string,要操作的html源代码的位置
    src: 'index.html',
    // type: string,保存操作输出的位置,如果要同时存储`html`和`css`,使用`html`和`css`的对象
    target: 'index.html',
    // type: integer,1300,目标视口的宽度
    width: 1300,
    // type: integer,900,目标视口的高度
    height: 900,
    // type: array,包含宽度和高度的数组,如果设置,优先于width和height选项
    dimensions: [],
    // type: boolean,是否压缩生成的critical-path css
    minify: true,
    // type: boolean,小心使用,从html中删除inline样式,它会基于提取的内容生成新的引用,因此可以安全地在多个html文件引用同一样式文件。删除每页的关键css会为每个页面生成唯一一个异步加载css。意味着你不能跨多页面缓存
    extract: false,
    // type: boolean,inline图片
    inlineImages: true,
    // type: array,内联程序开始查询assets时的目录/url列表
    assetPaths: [],
    // 设置base64内联图片的最大大小(以字节为单位)
    maxImageFileSize: 10240000,
    // type: object/function,critical尝试相对文档的assets path
    rebase: undefined,
    // type: array
    ignore: [],
    // type: string,获取远程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授权:user
    user: undefined,
    // type: string,RFC2617授权: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
                })
    }
}

配置完毕后,执行npm run build构建,查看dist/index.html,可以看到提取的critical css插入到head中,剩余css仍以外链形式引入。

这样index.html文件也不会很大,也可以保证在网络不稳定时,显示简单页面样式。

image.png

critical-css-webpack-plugin插件的核心是criticalcritical核心使用penthouse,接下来再详解一下criticalpenthouse

critical插件

critical插件介绍,使用到核心组件库penthouse

html中提取critical css,并将critical-path内联到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环境下执行配置文件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介绍

关键路径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

对应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服务端提取Critical css

介绍

简介

SSR服务端渲染时,管理 CSS 的推荐方法是简单地使用 *.vue 单个文件组件内的<style>,它提供:

  • 与 HTML 并列同级,组件作用域 CSS
  • 能够使用预处理器(pre-processor)或 PostCSS
  • 开发过程中热重载(hot-reload)

更重要的是,vue-style-loadervue-loader 内部使用的 loader),具备一些服务器端渲染的特殊功能:

  • 客户端和服务器端的通用编程体验。

  • 在使用 bundleRenderer 时,自动注入关键 CSS(critical CSS)。

  • 如果在服务器端渲染期间使用,可以在 HTML 中收集和内联(使用 template 选项时自动处理)组件的 CSS。在客户端,当第一次使用该组件时,vue-style-loader 会检查这个组件是否已经具有服务器内联(server-inlined)的 CSS - 如果没有,CSS 将通过 <style> 标签动态注入。

demo示例

pages/index.vue为首屏渲染页面

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

新建pages/test.vue页面,验证SSR是否会自动注入关键css(critical css

<template>
    <div class="test">测试</div>
</template>
<style>
.test {
    color: red;
}
</style>
  1. index.vue中引入test.vue,服务启动后,test.vue样式也以style形式内嵌在页面中
  2. 不在index.vue中引入test.vue,服务启动后,test.vue样式则没有内嵌在首屏渲染页面中

总结:SSR服务端会自动注入首屏渲染关键css,无需引入其他插件。

提取CSS

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

SSR可通过extractCSS将css提取到独立的文件中,方便缓存。

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

总结

  • 客户端和服务端均可通过extract提取css至单独文件中,以外链形式引入
  • 客户端只可内嵌csshead中,或提取成单独文件外链引入,不可提取critical css,需要增加额外webpack plugin来提取
  • 服务端自动注入critical css,不需额外引入其他插件

参考文档

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,312评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,578评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,337评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,134评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,161评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,303评论 1 280
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,761评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,421评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,609评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,450评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,504评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,194评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,760评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,836评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,066评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,612评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,178评论 2 341

推荐阅读更多精彩内容