HTML 中的script

208 阅读4分钟

「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战

嗯,没错,就是红宝书第二章第一节。

最近在看到script标签加载脚本的文章, 突然想起来之前碰到的一个问题。 那就是我们的项目需要调用第三方的文档服务来实现在线预览编辑word文档,然后由于是内网私有部署,改成加载一个一个脚本文件,并且他们想动态加载。 动态加载,容易啊,create一个 script元素,然后把它放进dom 就行了。

问题就出在,是放进body还是放进head,还是append到末尾。 一开始是放在末尾的,但是就发现了一个问题。 有可能在调用文档服务的时候,脚本还没加载完毕(什么破服务器这么慢)。

然后同事就找到我了,我啪的一下,一拍大腿,放在head不就行了, 因为浏览器解析dom是按顺序解析的,放head里,body必定会等head里的资源加载完毕才开始解析。 我的想法是对的, 但是,结果就是阻塞了, 因为必须等head里面的资源加载完毕才解析body,所以页面就出现了空白期。不过那个项目本身就比较重,结果直接卡了,没人会关心这个阻塞是一分钟还是两分钟,因为都太久了。 最后他们暂时妥协为静态script标签放在head。我怀疑是angular的问题,但没有证据。我当时也想说用async来着,但是用了之后,不就没办法保证调用的时候一定有了吗。立即下载但是不立即解析,不影响继续解析HTML。 所以,没管,反正这个不是在我手上,不过这个问题还是值得探究一下的。

script标签

首先它是一个html tag。 所以还是符合浏览解析,自上而下解析的规则, 所以它能用createElement方法创建并被追加到dom中。

我们来看看它比较重要且常用的属性

type

就是以前废弃的language, 默认是text/javascript,如果要使用import export 关键字,我们就会把type设为module ,一旦设置为module等于开启了严格模式. 除了text/javascripttext/ecmascriptapplication/javascript, 和application/ecmascript还有 module 之外的其他表识,会让浏览器忽略这个标签。 我们在写glsl时给的x-shader其意义主要还是为了编辑器的高亮,写其他的非法值也是不会被解析的。

async

异步加载,意思就是解析到这的时候,还是立即加载资源,但不会阻止浏览器继续往下解析。 前面提到的那个问题,通过这种方式可以缓解,但是不能保证要用的时候,一定就下载完毕了。

defer

这个属性类似async,也是立即下载,并不会阻塞浏览器的其他动作。 它的不同之处在于推迟执行,意思就是说,加了这个属性之后,这个script标签里面的代码会最晚执行, 如果有多个defer的标签,执行顺序就是他们在html里的顺序。

除了上面几个属性,还有crossorigin intergrity 和跨域以及安全有关。

小的注意点

  • 使用了scr属性的script标签内部的任何东西都会被忽略。

  • script标签内的代码不能出现 </script> ,这个很好理解,如果需要用到,加\转义 <\/script>

  • async 就如同我们所知晓的异步请求那样,没法保证顺序,写在前面的未必就会第一个执行。 因此,页面初始化里面不要依赖 开启了async的script里面的代码。也不要在async的脚本中进行dom操作,因为没法确定它是在DOM加载后才执行,只能确保它在window加载完毕前执行。

小结

对于没有开启async defer属性的script标签,其位置是非常重要的, 浏览器会按照其顺序逐行解析,会造成同步阻塞。初始化没有依赖的脚本适合async和 defer. 一旦使用这两个属性,说明要加载的资源的优先级就比较低。