问题: 当在html文件中嵌入script标签时,正确的位置应该放在哪里?
分析
这里面主要涉及的是加载顺序、js是单线程。
由于浏览器解析是自上而下加载解析,如果一个很大的js文件放在head里面,由于js是单线程,那么浏览器会一直解析,而无法加载解析后面的dom tree,以至于页面出现空白,如果放在body最下面,这样dom tree会先加载解析,呈现出页面再加载解析js里面的内容,这样用户体验会更好。
如果js放在head里面,引用了某个dom,但是这时你还没加载dom tree,会报错,可以采用 window.onload = function(){}这样来解决,或者给js设置defer 或者 async来让js异步加载,这样就不会报错了。
当浏览器加载一个含有<script>标签的页面时,会发生以下动作:
1 获取HTML文件,拉取HTML页面(比如:index.html)
2 开始解析html文件
3 当解析器遇到一个<script>标签时,准备去获取<script>标签对应的js文件
4 当解析器获取js文件时,同时中断了页面上其他html的解析
5 一段时间后,js文件解析完毕,页面上其他的html标签继续解析
在第四步的时候,页面上的html解析阻断,给用户带来了不好的体验。
为什么会出现中断html解析?
任何script代码都能改变HTML的结构,通过document.write() 这种方式或者其他方式。 这就导致了HTML解析必须等待<script>全部被下载和执行完,HTML才能解析script标签之后余下的部分。
然而,大部分的Javascript开发者在加载文档过程中,不会通过script操作HTML的DOM结构。然而,他们必须等到<script>全部加载结束,才能看到页面。
废弃的解决方式
之前解决这个问题的方式是将<script>标签放在html文件的<body>标签之后执行
但这种加载方式存在的问题就是只有当所有的html元素加载完成后才能开始加载<script>标签的内容,如果这个加载需要花很长时间的话,那在这段时间内,就无法操作页面,要知道两秒之内不能操作页面的话,这个用户体验是非常差的。
现在的解决方案
现在浏览器<script>标签支持 async 和 defer 属性, 应用这些属性当<script>被下载时,浏览器更安全而且可以并行下载(下载script并不阻断HTML解析)。
async
<script type="text/javascript" src="path/to/script1.js" async></script>
<script type="text/javascript" src="path/to/script2.js" async></script>
async标记的 <script> 异步下载并执行。这意味着<script>下载时不阻塞HTML的解析,并且下载结束<script>马上执行。
上述代码 script2 可能比 script1 先下载完并执行完。
defer
<script type="text/javascript" src="path/to/script1.js" defer></script>
<script type="text/javascript" src="path/to/script2.js" defer></script>
defer标签的script顺序执行,这种方式也不会阻断浏览器解析HTML,同时下载结束时<script> 马上执行
上述代码中 script2 肯定比 script1 后执行完
结论
使用 async 和 defer属性来让你的网页不被block,但是前提要确保你的浏览器支持 这两个属性。