JavaScript 页面生命周期主要事件

  1. DOMContentLoaded
  2. load
  3. beforeunload/unload

一、DOMContentLoaded

DOMContentLoaded事件发生在document对象上,需用addEventListener来捕获它,此时浏览器已完全加载 HTML,并构建了 DOM 树。
document.addEventListener("DOMContentLoaded", ready)

1.1 DOMContentLoaded 和脚本
当浏览器处理一个 HTML 文档,并在文档中遇到 <script> 标签时,就会在继续构建 DOM 之前运行它。这是一种防范措施,因为脚本可能想要修改 DOM,甚至对其执行 document.write 操作,所以 DOMContentLoaded 必须等待脚本执行结束。

不会阻塞 ​DOMContentLoaded​ 的脚本:
具有 ​async​ 特性(attribute)的脚本不会阻塞 ​DOMContentLoaded​,稍后 我们会讲到。
使用 ​document.createElement('script')​ 动态生成并添加到网页的脚本也不会阻塞 ​DOMContentLoaded​。

1.2 DOMContentLoaded 和样式

外部样式表不会影响 DOM,因此 DOMContentLoaded 不会等待它们。但这里有一个陷阱。如果在样式后面有一个脚本,那么该脚本必须等待样式表加载完成:原因是,脚本可能想要获取元素的坐标和其他与样式相关的属性

1.3 浏览器内建的自动填充
Firefox,Chrome 和 Opera 都会在 DOMContentLoaded 中自动填充表单
如果 DOMContentLoaded 被需要加载很长时间的脚本延迟触发,那么自动填充也会等待

二、load

当整个页面,包括样式、图片和其他资源被加载完成时,会触发 window 对象上的 load 事件。可以通过 onload 属性获取此事件

三、readyState

document.readyState​ 是文档的当前状态,可以在 ​readystatechange​ 事件中跟踪状态更改
  1. loading —— 文档正在被加载。
  2. interactive​ —— 文档已被解析完成,与 ​DOMContentLoaded​ 几乎同时发生
  3. complete —— 文档和资源均已加载完成,与 ​window.onload​ 几乎同时发生,但是在 ​window.onload​ 之前发生。

JavaScript脚本

当浏览器加载 HTML 时遇到 <script>...</script> 标签,浏览器就不能继续构建 DOM。它必须立刻执行此脚本。对于外部脚本 <script src="..."></script> 也是一样的:浏览器必须等脚本下载完,并执行结束,之后才能继续处理剩余的页面
--- 有什么问题?
    1.脚本不能访问到位于它们下面的 DOM 元素
    2.脚本会阻塞页面
--- 有什么解决办法?
    1.把脚本放在页面底部【浏览器只有在下载了完整的HTML文档之后才会开始下载脚本。对于长的 HTML文档来说,这样可能会造成明显的延迟】
    2.defer,告诉浏览器不要等待脚本。浏览器将继续处理 HTML,构建 DOM。脚本会“在后台”下载,然后等 DOM 构建完成后,脚本才会执行【适用于外部脚本】
        具有 ​defer​ 特性的脚本不会阻塞页面
        具有 ​defer​ 特性的脚本总是要等到 DOM 解析完毕,但在 ​DOMContentLoaded​ 事件之前执行
        具有 defer 特性的脚本保持其相对顺序,就像常规脚本一样
    3.async【脚本是完全独立的】
        async 脚本会在后台加载,并在加载就绪时运行
    4.动态脚本
        let script = document.createElement('script');
        script.src = "/article/script-async-defer/long.js";
        document.body.append(script); // (*)
        当脚本被附加到文档 (*) 时,脚本就会立即开始加载。
        默认情况下,动态脚本的行为是“异步”的。
        function loadScript(src) {
              let script = document.createElement('script');
              script.src = src;
              script.async = false;
              document.body.append(script);
        }
        // long.js 先执行,因为代码中设置了 async=false
        loadScript("/article/script-async-defer/long.js");
        loadScript("/article/script-async-defer/small.js");
        
    

JavaScript资源加载 onload,onerror

浏览器允许我们跟踪外部资源的加载 —— 脚本,iframe,图片等

跨源策略
一个源(域/端口/协议三者)无法获取另一个源(origin)的内容
window.error无法捕获其他源的脚本的错误信息
--- 如何跨源?

有三个级别的跨源访问
    1.无 ​crossorigin​ 特性 —— 禁止访问
    2.crossorigin="anonymous"​ —— 如果服务器的响应带有包含 ​*​ 或我们的源(origin)的 header ​Access-Control-Allow-Origin​,则允许访问。浏览器不会将授权信息和 cookie 发送到远程服务器。
    3.crossorigin="use-credentials"​ —— 如果服务器发送回带有我们的源的 header ​Access-Control-Allow-Origin​ 和 ​Access-Control-Allow-Credentials: true​,则允许访问。浏览器会将授权信息和 cookie 发送到远程服务器。
    

JavaScript跨窗口通信

“同源(Same Origin)”策略限制了窗口(window)和 frame 之间的相互访问
一个 <iframe> 标签承载了一个单独的嵌入的窗口,它具有自己的 document 和 window
iframe.contentWindow​ 来获取 ​<iframe>​ 中的 window
iframe.contentDocument​ 来获取 ​<iframe>​ 中的 document,是 ​iframe.contentWindow.document​ 的简写形式

如果尝试对来自另一个源的 <iframe> 进行读取和写入

1.可以通过 ​iframe.contentWindow​ 获取对内部 window 的引用
2.可以对 ​location​ 进行写入

一个 iframe 内可能嵌套了其他的 iframe。相应的 window 对象会形成一个层次结构(hierarchy)。可以通过以下方式获取:

​window.frames​ —— “子”窗口的集合(用于嵌套的 iframe)。
​window.parent​ —— 对“父”(外部)窗口的引用。
​window.top​ —— 对最顶级父窗口的引用。

跨窗口通信
postMessage​ 接口允许窗口之间相互通信,无论它们来自什么源

想要发送消息的窗口需要调用接收窗口的 postMessage 方法。
win.postMessage(data, targetOrigin)
参数:
​data​
要发送的数据。可以是任何对象,IE 浏览器只支持字符串,因此我们需要对复杂的对象调用 ​JSON.stringify​ 方法进行处理,以支持该浏览器。

​targetOrigin​
指定目标窗口的源,以便只有来自给定的源的窗口才能获得该消息。
<iframe src="http://example.com" rel="external nofollow"  rel="external nofollow"  name="example">
<script>
  let win = window.frames.example;
  win.postMessage("message", "http://example.com");
</script>

onmessage
为了接收消息,目标窗口应该在 message 事件上有一个处理程序。当 postMessage 被调用时触发该事件(并且 targetOrigin 检查成功)
event 对象具有特殊属性:
​data​
从 ​postMessage​ 传递来的数据。

​origin​
发送方的源,例如 ​http://javascript.info​。

​source​
对发送方窗口的引用。如果我们想,我们可以立即 ​source.postMessage(...)​ 回去。
window.addEventListener("message", function(event) {
  if (event.origin != 'http://javascript.info') {
    // 来自未知的源的内容,我们忽略它
    return;
  }
  alert( "received: " + event.data );
  // 可以使用 event.source.postMessage(...) 向回发送消息
});

有李
108 声望12 粉丝

通过注意生活中细节性的风景


« 上一篇
PWA实践
下一篇 »
域名