{"action":"created","issue":{"url":"https://api.github.com/repos/FoxDaxian/memory/issues/2","repository_url":"https://api.github.com/repos/FoxDaxian/memory","labels_url":"https://api.github.com/repos/FoxDaxian/memory/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/FoxDaxian/memory/issues/2/comments","events_url":"https://api.github.com/repos/FoxDaxian/memory/issues/2/events","html_url":"https://github.com/FoxDaxian/memory/issues/2","id":384687653,"node_id":"MDU6SXNzdWUzODQ2ODc2NTM=","number":2,"title":"webpack减少阻塞渲染的css（别名首屏阻塞css优化）","user":{"login":"FoxDaxian","id":19708789,"node_id":"MDQ6VXNlcjE5NzA4Nzg5","avatar_url":"https://avatars0.githubusercontent.com/u/19708789?v=4","gravatar_id":"","url":"https://api.github.com/users/FoxDaxian","html_url":"https://github.com/FoxDaxian","followers_url":"https://api.github.com/users/FoxDaxian/followers","following_url":"https://api.github.com/users/FoxDaxian/following{/other_user}","gists_url":"https://api.github.com/users/FoxDaxian/gists{/gist_id}","starred_url":"https://api.github.com/users/FoxDaxian/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/FoxDaxian/subscriptions","organizations_url":"https://api.github.com/users/FoxDaxian/orgs","repos_url":"https://api.github.com/users/FoxDaxian/repos","events_url":"https://api.github.com/users/FoxDaxian/events{/privacy}","received_events_url":"https://api.github.com/users/FoxDaxian/received_events","type":"User","site_admin":false},"labels":[{"id":1134344030,"node_id":"MDU6TGFiZWwxMTM0MzQ0MDMw","url":"https://api.github.com/repos/FoxDaxian/memory/labels/%E5%85%A5%E9%97%A8","name":"入门","color":"a9e26f","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2018-11-27T09:00:42Z","updated_at":"2018-12-04T08:32:10Z","closed_at":null,"author_association":"OWNER","body":"```\r\n随着浏览器的日新月异，网页的性能和速度越来越好，并且对于用户体验来说也越来越重要。\r\n现在有很多优化页面的办法，比如：静态资源的合并和压缩，code splitting，DNS预读取等等。\r\n本文介绍的是另一种优化方法：首屏阻塞css优化\r\n\r\n```\r\n##### 原理：\r\n首先我们了解一下页面的基本渲染流程（[参考](https://juejin.im/post/5b88ddca6fb9a019c7717096)）：\r\nwebkit渲染过程：\r\n![webkit渲染过程](https://user-gold-cdn.xitu.io/2018/9/3/1659db14e773f9cc?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\r\nGecko渲染过程:\r\n![gecko渲染过程](https://user-gold-cdn.xitu.io/2018/9/3/1659db14e7df8a8f?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\r\n那么，为什么要做这种优化呢？上面的流程图就是原因：首先解析html生成dom树，同时解析css生成css树，之后结合两者生成渲染树，然后渲染到屏幕上。不但如此，如果css后面有其他javascript，并且css加载时间过长，也会阻塞后面的js执行，因为js可能会操作dom节点或者css样式，所以需要等待render树完成。那么，如果我们能优化css，那么就能大大减少页面渲染出来的时间，从而提升pv，增加黏性，走向编码巅峰。。。\r\n\r\n* * *\r\n\r\n\r\n##### 怎么做呢：\r\n目前我知道的比较实用的办法是webpack集成[critical](https://github.com/addyosmani/critical)，critical是一个提取关键css，内联到html中，并且使用preload和noscript兼容加载非关键css的工具。\r\n那么，我们开门见山，直接从webpack配置开始：\r\n```javascript\r\nconst HtmlWebpackPlugin = require('html-webpack-plugin'); // 创建html来服务你的资源\r\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 提取css到分离的文件，需要webpack4\r\nconst HtmlCriticalWebpackPlugin = require('html-critical-webpack-plugin'); // 集成critical的html-webpack-plugin版本\r\nconst path = require('path');\r\n\r\n// 用于设置Chromium，因为Chromium使用npm或者yarn经常有问题\r\nprocess.env['PUPPETEER_EXECUTABLE_PATH'] =\r\n    '你电脑中的Chromium地址';\r\n\r\nmodule.exports = {\r\n    mode: 'none',\r\n    module: {\r\n        rules: [\r\n            {\r\n                test: /\\.css$/,\r\n                // 使用MiniCssExtractPlugin.loader代替style-loader\r\n                use: [MiniCssExtractPlugin.loader, 'css-loader']\r\n            },\r\n            {\r\n                test: /\\.js$/,\r\n                use: {\r\n                    loader: 'babel-loader',\r\n                    options: {\r\n                        presets: ['@babel/preset-env']\r\n                    }\r\n                }\r\n            }\r\n        ]\r\n    },\r\n    plugins: [\r\n        new HtmlWebpackPlugin({ template: './index.html' }),\r\n        new MiniCssExtractPlugin({}),\r\n        new HtmlCriticalWebpackPlugin({\r\n            base: path.resolve(__dirname, 'dist'),\r\n            src: 'index.html',\r\n            dest: 'index.html',\r\n            inline: true,\r\n            minify: true,\r\n            extract: true,\r\n            width: 375,\r\n            height: 565,\r\n            // 确保调用打包后的JS文件\r\n            penthouse: {\r\n                blockJSRequests: false\r\n            }\r\n        })\r\n    ]\r\n};\r\n```\r\n然后是html文件：\r\n```html\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n    <head>\r\n        <meta charset=\"UTF-8\" />\r\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\r\n        <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\r\n        <title>Document</title>\r\n    </head>\r\n    <body>\r\n        <div class=\"div\"></div>\r\n\t\t<h2>hello world</h2>\r\n\t\t<div class=\"mask\">这是一个弹窗</div>\r\n    </body>\r\n</html>\r\n```\r\n接着是css文件：\r\n```css\r\n.div {\r\n    width: 200px;\r\n    height: 100vh;\r\n    background-color: red;\r\n}\r\nh2 {\r\n    color: blue;\r\n}\r\n.mask {\r\n    width: 500px;\r\n    height: 500px;\r\n    display: none;\r\n    position: absolute;\r\n    top: 0;\r\n    left: 0;\r\n    bottom: 0;\r\n    right: 0;\r\n    margin: auto;\r\n\tbackground-color: yellowgreen;\r\n}\r\n\r\n```\r\n运行webpack后，查看打包后的html文件：\r\n```html\r\n// 省略...\r\n<style>\r\n    .div {\r\n        width: 200px;\r\n        height: 100vh;\r\n        background-color: red;\r\n    }\r\n    .mask {\r\n        width: 500px;\r\n        height: 500px;\r\n        display: none;\r\n        position: absolute;\r\n        top: 0;\r\n        left: 0;\r\n        bottom: 0;\r\n        right: 0;\r\n        margin: auto;\r\n        background-color: #9acd32;\r\n    }\r\n</style>\r\n<link\r\n    href=\"main.80dc2a9c.css\"\r\n    rel=\"preload\"\r\n    as=\"style\"\r\n    onload=\"this.onload=null;this.rel='stylesheet'\"\r\n/>\r\n<noscript><link href=\"main.80dc2a9c.css\" rel=\"stylesheet\"/></noscript>\r\n// 省略...\r\n```\r\n#### [代码仓库在此，点击fork进行实战练习](https://github.com/FoxDaxian/webpack4)\r\n\r\n可以看到，h2标签的css样式没有出现在内联style里，而是出现在main.[hash].css中，因为它不再所设置首屏范围内，这就是所谓的首屏css优化。\r\n\r\n##### 相关内容\r\n在上面打包后的html文件里，我们看到了有一个link内有`rel=\"preload\" as=\"style\"`字段，紧接着下面就有一个`noscript`标签，这两个是做什么的呢？\r\n- `rel=\"preload\" as=\"style\"`： 用于进行页面预加载，`rel=\"preload\"`通知浏览器开始获取非关键CSS以供之后用。其关键在于，`preload`不阻塞渲染，无论资源是否加载完成，浏览器都会接着绘制页面。并且，搭配as使用，可以指定将要预加载内容的类型，可以让浏览器：\r\n    1. 更精确地优化资源加载优先级。\r\n    2. 匹配未来的加载需求，在适当的情况下，重复利用同一资源。\r\n    3. 为资源应用正确的内容安全策略。\r\n    4. 为资源设置正确的 Accept 请求头。\r\n- `noscript`：如果页面上的脚本类型不受支持或者当前在浏览器中关闭了脚本，则在HTML `<noscript>` 元素中定义脚本未被执行时的替代内容。换句话说，就是当浏览器不支持js脚本或者用户主动关闭脚本，那么就会展示`noscript`里的内容，而[critical](https://github.com/addyosmani/critical)则是利用这一点做了向后兼容\r\n\r\n##### 总结\r\n利用[critical](https://github.com/addyosmani/critical)可以大大提高页面渲染速度，但是由于其使用puppeteer，所以下载安装比较麻烦，上面的webpack中使用设置env中puppeteer位置的方法解决了这一问题。\r\n\r\n文中如若有不对的地方，还望之处，共同交流。\r\n"},"comment":{"url":"https://api.github.com/repos/FoxDaxian/memory/issues/comments/444014389","html_url":"https://github.com/FoxDaxian/memory/issues/2#issuecomment-444014389","issue_url":"https://api.github.com/repos/FoxDaxian/memory/issues/2","id":444014389,"node_id":"MDEyOklzc3VlQ29tbWVudDQ0NDAxNDM4OQ==","user":{"login":"FoxDaxian","id":19708789,"node_id":"MDQ6VXNlcjE5NzA4Nzg5","avatar_url":"https://avatars0.githubusercontent.com/u/19708789?v=4","gravatar_id":"","url":"https://api.github.com/users/FoxDaxian","html_url":"https://github.com/FoxDaxian","followers_url":"https://api.github.com/users/FoxDaxian/followers","following_url":"https://api.github.com/users/FoxDaxian/following{/other_user}","gists_url":"https://api.github.com/users/FoxDaxian/gists{/gist_id}","starred_url":"https://api.github.com/users/FoxDaxian/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/FoxDaxian/subscriptions","organizations_url":"https://api.github.com/users/FoxDaxian/orgs","repos_url":"https://api.github.com/users/FoxDaxian/repos","events_url":"https://api.github.com/users/FoxDaxian/events{/privacy}","received_events_url":"https://api.github.com/users/FoxDaxian/received_events","type":"User","site_admin":false},"created_at":"2018-12-04T08:32:10Z","updated_at":"2018-12-04T08:32:10Z","author_association":"OWNER","body":"[比较全面的方案](https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758)"}}
{"actor":{"display_login":"FoxDaxian"}}