# js-css-block-dom **Repository Path**: cckevincyh/js-css-block-dom ## Basic Information - **Project Name**: js-css-block-dom - **Description**: 浅谈JS和CSS是如何阻塞DOM - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-07-31 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 后端Express ``` let express = require('express'); let app = new express(); let filter = (req, res, next) => { console.log(req.query); let {sleep} = req.query; if(sleep){ sleepFun(sleep).then(()=> next()); }else{ next(); } }; app.use(filter); app.use('/static', express.static('public')); app.listen(3000,"127.0.0.1"); function sleepFun(time) { return new Promise(function(res) { setTimeout(() => { res() }, time); }) } ``` 根据传入sleep的参数设置返回文件的时间。 ## CSS 阻塞页面渲染 ``` hello
``` ``` hello
``` 我们把common.css放在html中的任何地方。 common.css中的内容是把背景设置为蓝色 ``` div { background: lightblue; } ``` 第一种情况如果common.css是放在前面,那么最后渲染出来的是绿色,而且页面是等待3秒之后才出现绿色。 第二种情况如果是common.css是放在后面,那么最后渲染出来的是蓝色,而且页面也是等待3秒之后才出现蓝色。 > 如果CSS 不会阻塞页面阻塞渲染,那么CSS文件下载之前,浏览器就会渲染出一个浅绿色的div,之后再变成浅蓝色,或者从浅蓝色变成浅绿色。浏览器的这个策略其实很明智的,想象一下,如果没有这个策略,页面首先会呈现出一个原始的模样,待CSS下载完之后又突然变了一个模样。用户体验可谓极差,而且渲染是有成本的。因此,基于性能与用户体验的考虑,浏览器会尽量减少渲染的次数,CSS顺理成章地阻塞页面渲染。 ## CSS 不会阻塞 DOM 的解析 关于CSS,``````标签放在头部性能会高一点,如果``````,JS文件的内容是: ``` hello
``` - console.js ``` console.log(document.querySelector('div')); ``` 这里简单解释下defer模式和async模式 > 1.默认引用 script: 当浏览器遇到 script 标签时,文档的解析将停止,并立即下载并执行脚本,脚本执行完毕后将继续解析文档。 > 2.async模式 当浏览器遇到 script 标签时,文档的解析不会停止,其他线程将下载脚本,脚本下载完成后开始执行脚本,脚本执行的过程中文档将停止解析,直到脚本执行完毕。 > 3.defer模式 当浏览器遇到 script 标签时,文档的解析不会停止,其他线程将下载脚本,待到文档解析完成,脚本才会执行。 defer属性是用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。设置这个属性,能保证DOM解析后马上打印出div。 可以看到是首先打印出div这个DOM节点,过3s左右之后才渲染出一个浅绿色的div。这就证明了CSS 是不会阻塞 DOM 的解析的,尽管CSS下载需要3s,但这个过程中,浏览器不会傻等着CSS下载完,而是会解析DOM的。 ### 什么是DOMContentLoaded? DOMContentLoaded事件触发表示当前的HTML文档已被完全加载和解析, DOMContentLoaded事件本身不会等待CSS文件、图片、iframe加载完成。具体的定义可以参考:https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event ### DOMContentLoaded什么时候触发? ``` DOMContentLoaded什么时候触发?
``` - date.js ``` console.log(new Date()); ``` - common.css ``` div { background: lightblue; } ``` 在代码中我们监听了DOMContentLoaded事件,然后在下面引入了date.js外部js文件,然后在最下面还引入了外部的css样式,而且下载时间需要3秒。 然后我们访问页面可以发现,date.js先打印出来时间,然后触发了DOMContentLoaded,此时页面还是空白的,过了3秒之后才出现浅蓝色的div。 通过这段代码,我们可以知道DOMContentLoaded事件本身不会等待CSS文件加载。 所以总结一下: DOMContentLoaded事件本身不会等待CSS文件、图片、iframe加载完成。 1. 如果页面中没有script标签,DOMContentLoaded事件并没有等待CSS文件、图片加载完成。 2. 如果页面中静态的写有script标签,DOMContentLoaded事件需要等待JS执行完才触发。而且script标签中的JS需要等待位于其前面的CSS的加载完成。 总的来说,当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件;如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等 CSSOM 构建完成才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成。 ### defer 与 DOMContentLoaded > 如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。 所以这意味着什么呢?HTML 文档解析不受影响,等 DOM 构建完成之后 defer 脚本执行,但脚本执行之前需要等待 CSSOM 构建完成。在 DOM、CSSOM 构建完毕,defer 脚本执行完成之后,DOMContentLoaded 事件触发。 ### async 与 DOMContentLoaded > 如果 script 标签中包含 async,则 HTML 文档构建不受影响,解析完毕后,DOMContentLoaded 触发,而不需要等待 async 脚本执行、样式表加载等等。 引用:[你不知道的 DOMContentLoaded](https://zhuanlan.zhihu.com/p/25876048) ### 浏览器渲染原理 HTML 文档被加载和解析完成之前,浏览器做了哪些事情呢? > 浏览器向服务器请求到了 HTML 文档后便开始解析,产物是 DOM(文档对象模型),到这里 HTML 文档就被加载和解析完成了。如果有 CSS 的会根据 CSS 生成 CSSOM(CSS 对象模型),然后再由 DOM 和 CSSOM 合并产生渲染树。有了渲染树,知道了所有节点的样式,下面便根据这些节点以及样式计算它们在浏览器中确切的大小和位置,这就是布局阶段。有了以上这些信息,下面就把节点绘制到浏览器上。所有的过程如下图所示: ## CSS 阻止JS执行 我们来看看下面这段的代码,和上面不同的是这里的``` CSS 阻止JS执行
``` 可以看到页面等待了3秒,但此过程中不会打印任何东西,之后呈现出一个浅绿色的div,再打印出null。 看起来好像是CSS阻塞了DOM的解析。但是前面我们已经了解了CSS是不会阻塞DOM的,那么阻塞DOM的到底是什么?其实阻塞页面解析的其实是JS,因为JS在等待CSS的下载,等待CSS完成之后才执行JS。这是为什么呢? > 仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等CSS控制的属性,浏览器是需要计算的,也就是依赖于CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行JS > 所以,为何 ``` 我们可以在浏览器上访问,可以看到页面先是显示浅绿色的div,然后在现实浅灰色,最后才是浅蓝色。 由此可见,每次碰到```