1. 为什么有些js需要在body中引入
简单的说,放在 </body>
前面可以保证 HTML 标签先加载和解析,之后才是脚本。
一些脚本涉及到 DOM 操作的,能够有效的前提当然是要有对应的 DOM 对象。当你放在 <head>
标签里,这些脚本在 HTML 标签得以解析之前就执行了,此时没有对应的 DOM 对象可以操作,当然就失效了。
当然,这个问题可以通过监听 DOMContentLoaded
事件,然后把脚本放在事件回调函数里来解决。但是另外一个问题则是 HTTP 请求的优先级问题。脚本(特别是非本地脚本)放在 <head>
标签里,它会优先进行请求,这会阻塞一些 HTML 标签的请求,比如图片等外部资源的下载。这就是为什么我们通常会把脚本放在 load
事件的回调函数里执行的原因。
DOMContentLoaded
和 load
的区别?
前者在文档载入并解析之后触发,但是不包括样式、图片等外部资源;后者则是等待所有的外部资源全部载入之后才触发。因此,你选择监听哪一个事件取决于你的脚本到底要操作什么。
然而不是所有的浏览器都支持 DOMContentLoaded
事件,这就是为什么最佳实践推荐把所有的脚本加载都放在 </body>
标签之前的缘故——保证向后兼容。
从确保内容加载并解析的角度考虑,优先级应该是:HTML > CSS > Javascript,CSS 要生效,前提是 HTML 已经提供了可供操作的选择符;Javascript 要生效,前提是浏览器解析了 HTML 并提供了 DOM 对象以及 CSS 声明已经存在(如果脚本的目的就是处理 HTML & CSS的话)。
2. defer async的区别
先来试个一句话解释仨,当浏览器碰到 script
脚本的时候:
<script src="script.js"></script>
没有defer
或async
,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该script
标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。<script async src="script.js"></script>
有async
,加载和渲染后续文档元素的过程将和script.js
的加载与执行并行进行(异步)。<script defer src="myscript.js"></script>