最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • <script>标签加载知多少?

    正文概述 掘金(阿玉君_)   2021-01-03   396

    前言

    普通script标签加载

    都知道,前端性能优化的一条原则是将script标签放在body底部,为什么呢?因为script标签的加载和执行时会阻塞DOM结构渲染的,若是script标签放在头部,加载时间或者执行时间过长,会影响后续DOM的渲染,造成很长时间的页面白屏,前端体验会变得很差。 那么我们不妨亲自试一试? 为了使例子更加直观,直接用nodejs写一个服务。
    先看一下文件目录:
    <script>标签加载知多少?

    // a.js
    console.log(new Date(Date.now()), "我是外部a.js文件");
    
    // b.js
    console.log(new Date(Date.now()), "我是外部b.js文件");
    
    //server.js
    const fs = require("fs");
    const http = require("http");
    
    const a = fs.readFileSync("./a.js");
    const b = fs.readFileSync("./b.js");
    
    const serverBack = (req, res) => {
      const url = req.url;
      if (url === "/a.js") {
        res.setHeader("Content-Type", "application/javascript; charset=UTF-8");
        setTimeout(() => {
          res.write(a);
          res.end();
        }, 9000);
        return;
      }
      if (url === "/b.js") {
        res.setHeader("Content-Type", "application/javascript; charset=UTF-8");
        setTimeout(() => {
          res.write(b);
          res.end();
        }, 5000);
        return;
      }
    };
    
    http.createServer(serverBack).listen(3003);
    

    a.jsb.js代码中都获取一下当前执行时间的时间戳,在server.js中读取两个文件的代码,若是访问相应的文件,返回即可,a.js延迟9s返回,b.js延迟5s返回,这样时间体验上会更清晰。
    在前端的html文件中直接通过script标签引入

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <script>
          console.log(new Date(Date.now()), "内部script文件", "上");
        </script>
        <script src="http://localhost:3003/a.js"></script>
        <script src="http://localhost:3003/b.js"></script>
        <script>
          console.log(new Date(Date.now()), "内部script文件", "下");
        </script>
      </body>
    </html>
    

    那么,直接运行这个html文件,控制台打印的最终结果为: <script>标签加载知多少? 可以看出来,内部上边的script标签的最先被执行,然后大约9s后基本上是同时执行了外部引入的a.jsb.js和内部下边的script标签。那么它们三个的执行顺序是随机的吗?
    当然不是!不管刷新多少次,它们的执行顺序是永远不会改变的,永远是a.js->b.js->内部下边的script标签。我们在server端设置的b.js文件是5s返回结果,那么b.js文件肯定是先下载完的,可是会永远先执行a.js文件。
    这是因为<script>标签是并发下载,同步执行的,也就是说,不管引入多少个外部script标签,它们都会异步去下载,但是下载完成后,会按照标签的顺序同步执行,这也是为什么<script>阻塞DOM渲染的原因。
    那么外部js文件中是异步代码呢?

    // a.js
    console.log(new Date(Date.now()), "我是外部a.js文件");
    setTimeout(() => {
      console.log(new Date(Date.now()), "我是外部a.js文件", "setTimeout");
    }, 6000);
    
    // b.js
    console.log(new Date(Date.now()), "我是外部b.js文件");
    setTimeout(() => {
      console.log(new Date(Date.now()), "我是外部b.js文件", "setTimeout");
    }, 3000);
    

    我们来看一下运行结果: <script>标签加载知多少? 可以得出结论,a.jsb.js文件依然会按照同步顺序执行,但是对于异步任务,也是遵循eventloop机制,将异步任务放入异步队列中去执行,内部script标签并不会等待异步任务执行完。

    ⚠️defer和async属性只适用于外部引入的js文件

    defer延迟脚本

    现在我们来给外部的两个script标签加上defer属性:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <script>
          console.log(new Date(Date.now()), "内部script文件", "上");
        </script>
        <script src="http://localhost:3003/a.js" defer></script>
        <script src="http://localhost:3003/b.js" defer></script>
        <script>
          console.log(new Date(Date.now()), "内部script文件", "下");
        </script>
      </body>
    </html>
    

    打印结果如下: <script>标签加载知多少? 可以看出来,内部的script标签中的代码并没有等a.jsb.js文件全部执行下载完就执行了,这也就是defer属性的延迟执行功能。虽然是延迟执行,但两个文件执行的先后顺序并不能改变。所以defer属性是并发下载,延迟同步执行,不会阻塞DOM渲染。
    在《javascript高级程序设计》中,原话是这样的:
    HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个执行。在现实中,延迟脚本并不一定会按照顺序执行,因此最好只包含一个延迟脚本
    本文中的浏览器是Chrome,在FirFox和Safari中也尝试了一下,结果并没有发生改变。猜测作者是考虑到某些低版本浏览器没有做到严格的规范,才出此言。

    async异步脚本

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <script>
          console.log(new Date(Date.now()), "内部script文件", "上");
        </script>
        <script src="http://localhost:3003/a.js" async></script>
        <script src="http://localhost:3003/b.js" async></script>
        <script>
          console.log(new Date(Date.now()), "内部script文件", "下");
        </script>
      </body>
    </html>
    

    将所有defer属性全部换成async,我们来看一下结果: <script>标签加载知多少? 可以看出,内部下边的script标签中代码也没有等到a.jsb.js执行完就已经执行了。通过b.js的执行时间可以看出,b.js文件是下载完立即执行的,并没有等待a.js执行完。所以anync属性是并发下载,异步执行,也不会阻塞DOM渲染。

    总结:

    1. script标签加载都是并发加载,这是浏览器提供的功能。但是执行是按照标签写入顺序同步执行,异步代码遵循eventloop。
    2. defer属性是延迟同步执行,也就是等到html文档解析到</html>时才会回过头看script脚本是否全部下载完成?若是下载完成则按照顺序同步执行,否则继续等待所有标签下载完成在执行。
    3. async属性是异步执行,也就是script脚本什么时候下载完,什么时候执行。

    下载网 » <script>标签加载知多少?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元