首页 > 编程语言 >javascript放在head和body的区别以及js文件加载带来的阻塞解决

javascript放在head和body的区别以及js文件加载带来的阻塞解决

时间:2023-02-15 12:46:46浏览次数:58  
标签:body CSSOM head 浏览器 DOM javascript js 构建 css

今天在看到菜鸟教程中的

HTML 中的 Javascript 脚本代码必须位于<script></script> 标签之间。Javascript 脚本代码可被放置在 HTML 页面的 <body><head> 部分中

不禁好奇这两者的区别在哪?
这就要谈到浏览器的执行机制


浏览器的执行机制

Critical Rendering Path(CRP关键渲染路径)
是指浏览器获取到如下图1所示文档到屏幕打印出显示内容的过程

图1:浏览器获取到的HTML文件
(可以通过鼠标右键页面后选择“查看网页源代码得到”)
image


图2:浏览器解析后展示的界面
image


以上从图1到图2的过程浏览器主要经历了一下五个步骤,如下图3所示:

  1. parseHTML (构建DOM树)
  2. parseCSS(构建CSSOM树)
  3. Render Tree(构建渲染树)
  4. layout (布局)
  5. Paint (打印输出)

图3 关键渲染路径步骤
image

注意:parse HTML 和 parse CSS是同时进行的(这非常重要!!!)

接下来详细分解阐述一下每个步骤,
我感觉整个过程是一场“维密秀”,怎么说呢(你且听我慢慢虾扯)

1 parseHTML 构建DOM

由上图1可见拿到的HTML文档是很长的 不可能等解析完了再显示页面(会造成长时间的空白页) 所以浏览器只要拿到一部分就开始构建DOM

构建DOM树的过程如下图4所示:

在网络中传输的都是二进制01代码Bytes,通过指定解析方法(如UTF-8等)得到characters,这些字符集会由Tokenier分成一个个的Tokens,如图5所示,拆分后的Tokens就会变成一个个的结点Nodeds ,最后根据tokens解析的包含嵌套关系生成一棵类似于树状图的DOM树


图4:DOM树的构建过程
image
解析一下图4:
这个DOM树就相当于是一场维密秀,是离不开台前和幕后的准备的,body里的就是台前,观众可以看到的舞台呈现内容,而舞台呈现也离不开幕后head里工作人员的准备工作。
按照浏览器的执行顺序,是先处理head中的内容,也就是先做准备工作,才会保证秀的最终正常展示

图5:Tokenizer解析出Tokens(不同于网络中的令牌token)
image

2 构建CSSOM

(严格来讲这个地方不应该是2,因为是和1同时进行的)

在有了DOM树后相当于舞台的舞美构造和模特选角就有了

模特是可以一个接一个的走出来(避免冷场,DOM的构建是循序渐进的)

但她们的衣服(CSSOM)一定都是幕后工作人员提前全部准备好了的(完整的CSS文件)

文档会有一些默认css样式属性称为“user agent styles”舞台的舞美
如果是外部样式,CSSOM的构建就必须获得一份完整的CSS文件她们的衣服
不同于DOM的构建是一个循序渐进的过程,因为CSS的样式存在覆盖性,会以最终结果为准,如果我们提前构建就会出错
所以从这里可以看出CSS加载的优先级蛮高的,一般通过link放在head中提前加载完成
(就像女生出门试衣服总是会穿身换套,必须以她出门的最后一套为准)

内联样式 or 没有设置样式 --> css的解析就在parse HTML过程中

外部样式 --> css的解析就在parse CSS过程中

图6:解析CSSOM的过程
image

3 Render Tree

image

之前提到过不是所有人都上场,而是只有模特(“可见”)才会走上t台
而幕后人员并不上台(head中的内容)
所以Render Tree的构建过程是
1 浏览器从DOM开始 遍历每一个“可见”结点
2 对于每一个“可见”结点,在CSSOM上找到匹配的样式并引用每个模特有自己对应的服装
3 上面两者对应相结合 生成Render Tree

这个地方会涉及到一个CSS的匹配规则是从右往左的 就是为了页面尽快的渲染(面试)
body .parent .children {color:pink}
考虑如下场景:此时构建了部分DOM,而CSSOM构建是完全完成的,浏览器就会开始构建Render Tree,如果我们找到一条规则从右往左的匹配,我们就只需要逐层观察该节点的父节点是否匹配,而此时的父节点肯定已经在DOM上了,但是反过来我们可能匹配到一个还未出现在DOM上的结点
ps:想到了LeetCode上的一道题另一棵树的子树
DOM树就是那一颗子树,而CSSOM就是一颗完整的树,要把DOM子树和CSSOM树匹配起来

4 layout

render树构建了之后就知道了页面需要挂载哪些元素,以及挂载元素的样式

接下来就需要知道挂载在哪,也就是计算出相对于视窗的位置和大小

引出盒子模型每个盒子摆在哪 怎么摆的问题

模特要怎么走,站定的posing是啥

5 Paint

浏览器将每个节点以像素显示在屏幕上,最终呈现我们看到的页面

阻塞

好了 了解完浏览器的渲染流程后

是不是发现还没出现js的身影?
不要急,因为一开始浏览器就是类似于“电子报纸”的形式,只能阅读html和css写出来的静态页面,但不能交互,后来才出现了与浏览器互动的桥梁——JavaScript
程序媛通过js和浏览器提供的DOMapiBOMapi能够和页面产生一个交互

(我个人会觉得这个背景挺重要的,知其所以然,因为既然js的出现是为了操作页面,一般提到操作可以总结成“增 删 改”这类的词嘛,那对于DOM来讲能改变页面结构,对于CSSOM来讲能改变页面样式。)

JS的用途就决定了它必须是单线程操作,多线程会产生操作冲突,比如在原本的DOM树上,这个节点node是要显示,结果js同时要删掉他,浏览器就会懵逼:你到底要我干嘛,我到底现不现实 (也就是下面这句话的缘由)

"UI渲染线程" 与 "JS引擎" 是 互斥的,当 JS引擎执行时UI线程会被挂起

也就是说,在构建DOM时,HTML解析器若遇到了JavaScript,那么它会暂停构建DOM,将控制权移交给JavaScript引擎,等JavaScript引擎运行完毕,浏览器再从中断的地方恢复DOM构建。

所以如果想首屏渲染的越快,就不建议在首屏就加载 JS 文件资源,这也是很多地方说script 标签放在 body 标签底部的原因。当然也不是说外源script标签必须放在底部,因为可以给script标签添加 defer 或者 async 属性来解决浏览器的阻塞问题,如下图所示:

image

(浏览器只能解析这三种html css js 也就是入门的时候说的前端三件套,所以在vue中我们写的.vue文件 浏览器是不能解析的,必须通过脚手架这一类的工具转成浏览器能认识的语言)

而JavaScript不只是可以改DOM,它还可以更改样式,也就是它可以更改CSSOM。因为不完整的CSSOM是无法使用的模特服装没有准备好 大秀是没办法开的,如果JavaScript想访问CSSOM并更改它,那么在执行JavaScript时,必须要能拿到完整的CSSOM。所以就导致了一个现象,如果浏览器尚未完成CSSOM的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟脚本执行和DOM构建,直至其完成CSSOM的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建CSSOM,然后再执行JavaScript,最后在继续构建DOM。
出现linkstyle标签会造成js的阻塞,如下图所示
image

  1. css 阻塞
    • <style> 标签中的样式
      1. 由parsing html过程进行解析
      2. 不阻塞浏览器渲染
      3. 不阻塞 DOM 解析
    • <link src='index.css'> 引入的外部 css 样式
      (推荐使用 <link>方式引入外部 css,可以避免闪屏现象)
      1. 由parsing css进行解析

      2. 会阻塞浏览器页面渲染(原因:避免闪屏)

      3. 不阻塞 DOM 结构的解析

      4. 会阻塞 js 的执行(但不会阻塞 js 等资源的加载)因为外源css必须拿到完整的文件,所以没有谁能阻塞css的执行
        image

优化方案:(尽可能快的提高 css 加载速度)
使用 CDN 加速
对 css 进行压缩(用打包工具,比如 webpack, gulp 等,也可以通过开启 gzip 压缩)
减少 http 请求数,将多个 css 文件合并

  1. 总结一下js 阻塞
    会阻塞 DOM 解析 因为 js 可能会修改 DOM 树
    会阻塞 页面的渲染 因为 js 代码可能会修改 DOM 树 / CSSOM 树 的结构
    js 会顺序执行,阻塞后续 js 逻辑的执行 (不阻塞 js 等其他资源的加载)
    css 的解析 和 js 的执行 是互斥的 ( css 解析的时候 js 停止执行,js 执行的时候 css 停止解析)
    1 解析html构建dom的时候,遇到js会被阻塞(所以我们经常讲把js代码放在最后)
    2 js执行会被CSSOM建构阻塞,也就是说js必须在CSSOM构建后才能去执行(CSSOM的重要性:必须一次性完成建构 且不会被打断)
    CSSOM构建会阻塞js,js会阻塞DOM构建(加了defer这类除外哈~)
    3 如果使用aysnc异步脚本,脚本的网络请求优先级降低,且网络请求期间不阻塞DOM的构建,直到请求完成才开始执行脚本

参考文章和视频
浏览器的执行机制和渲染机制

写的不错的文章

讲的不错的视频1

讲的不错的视频2

标签:body,CSSOM,head,浏览器,DOM,javascript,js,构建,css
From: https://www.cnblogs.com/erma0415/p/17116576.html

相关文章

  • JavaScript 中 ?. 和 ?? 分别是什么?
    ?.和??是JavaScript中的两个新操作符,分别是可选链操作符(optionalchainingoperator)和空值合并操作符(nullishcoalescingoperator)。?.操作符?.可选链操作符用于......
  • 浏览器前端 JavaScript Worker使用
    主Js文件内容://注意://使用前先判断浏览器是否支持//Worker内的代码不可以操作DOM//不能跨域加载Worker的js文件if(window.Worker!==undefined){//新建Worker......
  • JavaScript模块化方案的演进史
    JavaScript语言诞生至今,模块规范化之路曲曲折折。社区先后出现了各种解决方案,包括AMD、CMD、CommonJS等,而后ECMA组织在JavaScript语言标准层面,增加了模块功能(因为该......
  • javascript 高级编程系列 - Web Workers
    Webworkders的规范让javascript在后台运行脱离了UI线程,从而解决了大量计算阻塞UI线程导致卡死的问题。在Webworkers没有出现之前,我们可以使用window.setTimeout异步方......
  • JavaScript对象属性的特性高级功能
    “usestrict”/创建一个对象最简单的方式:创建一个Object的实例,然后再为它添加属性和方法/varperson=newObject();person.name=“Hongbin”;person.age=21;per......
  • JavaScript 大文件上传下载解决方案
    ​ javaweb上传文件上传文件的jsp中的部分上传文件同样可以使用form表单向后端发请求,也可以使用ajax向后端发请求    1.通过form表单向后端发送请求     ......
  • 从混沌到规范:JavaScript模块化方案的演进史
    前言JavaScript语言诞生至今,模块规范化之路曲曲折折。社区先后出现了各种解决方案,包括AMD、CMD、CommonJS等,而后ECMA组织在JavaScript语言标准层面,增加了模块功能(因......
  • JavaScript-面向对象的理解
    Everythingisobject(万物皆对象)作为开发大家都非常熟悉的一句话。(1)对象是单个事物的抽象。在生活中大的事物圈子,比如一只猫、一本书、一个人,都可以理解为对象(objec......
  • javascript Date日期类
     四、Date日期类迁移时间:2017年5月27日18:43:02Author:Marydon(一)对日期进行格式化(日期转字符串)自定义Date日期类的format()格式化方法方式一:(非原创)//......
  • JavaScript 数字是什么?
    本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!作者|慕课网精英讲师然冬基于IEEE754标准的双精度64位二进制格式的值(-(253-1)到253-1)。——MDN......