什么是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 在所有情况下都强制提取。
-
开发环境
extract
为false
,样式内嵌到head
中。
-
生产环境
extract
为true
,样式分离,外链到head
中。
形成两个css文件:
app.[contetHash].css:vue组件抽离出来的css
chunk-vendors.[contentHash].css:第三方库依赖的css
内部插件
- mini-css-extract-plugin:CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载
- @intervolga/optimize-cssnano-plugin:优化和压缩css
不足
要么样式全部内嵌在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
文件也不会很大,也可以保证在网络不稳定时,显示简单页面样式。
critical-css-webpack-plugin
插件的核心是critical
,critical
核心使用penthouse
,接下来再详解一下critical
和penthouse
。
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
关键路径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插件
中的penthouse
options
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-loader
(vue-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>
- 在
index.vue
中引入test.vue
,服务启动后,test.vue
样式也以style
形式内嵌在页面中 - 不在
index.vue
中引入test.vue
,服务启动后,test.vue
样式则没有内嵌在首屏渲染页面中
总结:SSR服务端会自动注入首屏渲染关键css,无需引入其他插件。
提取CSS
以nuxt
框架配置为例nuxt.config.js
SSR可通过extractCSS
将css提取到独立的文件中,方便缓存。
export default {
build: {
extractCSS: true,
}
}
总结
- 客户端和服务端均可通过
extract
提取css
至单独文件中,以外链形式引入 - 客户端只可内嵌
css
至head
中,或提取成单独文件外链引入,不可提取critical css
,需要增加额外webpack plugin来提取 - 服务端自动注入
critical css
,不需额外引入其他插件