Google 的 API javascript 示例中的脚本 async/defer/onload 用法

dan*_*elv 2 javascript google-api

在 Google 为其 API 提供的各种 javascript 示例(例如此处)中,他们使用以下代码从 html 加载脚本:

<script async defer src="https://apis.google.com/js/api.js" 
    onload="this.onload=function(){};handleClientLoad()" 
    onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
Run Code Online (Sandbox Code Playgroud)

我的理解是async/defer告诉浏览器什么时候加载和执行脚本并且彼此有些矛盾。我有几个问题:

  1. 在这种情况下使用async和是什么意思defer
  2. 为什么 Google 选择使用这种技术?它有任何性能或其他好处吗?
  3. onload事件中,为什么他们function(){};在调用之前先为事件分配一个空函数 ( ) handleClientLoad()
  4. 如果我想将整个 javascript 移动到一个单独的 js 文件,加载这两个脚本的最佳方法是什么?由于新的js文件会依赖api.js,不能异步加载?

谢谢。

T.J*_*der 6

WHAT-WG living standard for HTML's section on asyncanddefer很好地涵盖了这一点,其中包括这个方便的图形:

在此处输入图片说明

1. 在这种情况下同时使用 async 和 defer 是什么意思?

如果浏览器支持async,它会忽略defer并执行异步工作。如果不是,但它支持defer,它会执行 defer 。如果两者都不支持,脚本会阻止 DOM 解析,但所有现代浏览器都至少支持一种。

2.谷歌为什么选择使用这种技术?它有任何性能或其他好处吗?

async在不阻塞 DOM 解析和渲染的情况下获取脚本,并在它可用时立即运行它即使 DOM 解析和渲染仍在进行中。defer还将避免阻塞 DOM 解析和渲染,但在解析完成之前不会运行脚本(例如,可能稍后)。

3.在onload事件中,为什么他们先给事件赋值一个空函数(function(){};),然后再调用handleClientLoad()

如果你看一下,这就会变得很清楚onreadystatechanged:基本上它确保handleClientLoadGAPI 只调用一次,而不是潜在的两次(一次onload又一次onreadystatechanged。)

4. 如果我想将整个 javascript 移动到一个单独的 js 文件中,加载这两个脚本的最佳方法是什么?由于新的js文件会依赖api.js,不能异步加载?

好吧,它可以异步加载,您只需要使用api.js. 我可能会:

  1. handleClientLoadscript标签加载上方有一个内联脚本api.js,如下所示:

    var clientLoaded = false;
    function handleClientLoad() {
        if (!clientLoaded &&
            typeof mainScriptLoad !== "undefined" &&
            typeof gapi !== "undefined") {
            clientLoaded = true;
            mainScriptLoad();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. mainScriptLoad在单独的文件中。

  3. 在单独文件的末尾,调用handleClientLoad.

那样:

  • 如果您的脚本首先运行,它会调用handleClientLoadhandleClientLoad会看到 GAPI 尚未加载并且不会执行任何操作;稍后,当 GAPI 加载时,会调用handleClientLoad并且会调用,mainScriptLoad因为一切都准备好了。
  • 如果您的脚本在 GAPI 加载后运行,它会调用handleClientLoadhandleClientLoad会看到您的主脚本尚未加载并且不会尝试调用它。稍后,当您的脚本加载并调用handleClientLoad,handleClientLoad将调用,mainScriptLoad因为一切都已准备就绪。