其他常见事件

资源事件

1 .beforeunload

1 .在窗口,文档,各种资源将要卸载前触发,可以用来防止用户不小心卸载资源,比如一个表格填了很多但是一不小心点击了关闭。
2 .经过测试发现,只有界面中有表单,而且表单已经填写的时候才会触发事件。只有这一种情况需要使用此事件
3 .此事件执行优于unload,也就是说可以阻止掉onload事件的执行
4 .新窗口打开不会执行,只在刷新和关闭时执行
5 .只能用于警告用户有未保存的改变。一但保存之后,事件应该会被清除,这样对页面的性能有影响

2 .unload

1 .窗口新打开,刷新,关闭的时候都会执行

3 .指定以上两个事件的监听函数,浏览器就不会缓存当前页面。所以任何时候都不要指定这些事件

html页面的生命周期

1 .DOMContentLoaded:浏览器已经完全加载了html,dom树已经构建完毕,但是像img和样式表等外部资源并没有下载完毕

1 .DOM加载完毕,js可以访问所有dom节点,初始化界面
2 .由document触发,所以要在这个上面监听
3 .浏览器的自动补全:如果有登录界面,浏览器记住了该页面的用户名和密码,那么在DOMContentLoaded运行的时候浏览器会自动补全表单
4 .async和defer脚本可能还没有执行

2 .load:附件资源已经加载完毕,在此时事件触发可以获得图像的大小,如果没有在html,css中指定

1 .会在所有文件包括样式表,图片和其他资源下载完毕之后触发
2 .一般不会用到这个事件,不需要等那么久

3 .beforeunload/unload:用户正在离开页面,可以询问用户是否保存了更改以及确定是否要离开页面

检测页面的状态

1 .document.readyState:

1 .loading:加载,document任然在加载
2 .interactive:互动。文档已经完成加载,文档已经被解析,但是诸如图像,样式表和框架之类的子资源任然在加载
3 .complete:文档和所有子资源已完成加载,状态表示load事件即将被触发

退出当前页面

1 .一个非常消耗后台运算的请求,在用户退出当前页面的时候,要求前端发送一个请求来杀死任务
2 .场景:

1 .还在本网站,跳到其他路由。从本页面前往别的页面 
2 .刷新,关闭页面的时候。unload事件+beacon api 注意,在页面关闭的时候是无法异步发送信息的,所以使用这个方法

3 .

Page Lifecycle API

1 .为了弥补page visibility api只能在网页对用户不可见时触发,无法知道网页被系统随时丢掉的情况。制定了新的规则,定义了这个事件,允许开发者响应网页状态的各种转换
2 .统一了网页从诞生到卸载的行为模式,并定义了新的事件,允许开发者响应网页状态的各种转换
3 .6种状态

1 .active:网页处于可见状态,且拥有输入焦点
2 .passive阶段:网页可见,但是没有输入检点,无法接受输入。ui更新任然在执行,该阶段只能发生在桌面同时有多个窗口的时候。
3 .hidden:用户的桌面被其他窗口占据,网页不可见,但是尚未冻结,ui更新不在执行(比如一些ui动画)
4 .terminated:用户主动关闭窗口,或者在同一个窗口前往其他页面,导致当前页面开始被浏览器卸载并从内存中清除。注意这个阶段总是在hidden之后发生,也就是说,当用户主动离开当前页面,总是先进入hidden阶段,然后在进入terminated阶段。这个阶段会导致网页卸载,任何新的任务都不会再这个阶段启动,并且如果运行时间太长,正在进行的任务可能也会被终止
5 .Forzen:如果网页处于hidden阶段太久,用户又不关闭网页,浏览器就有可能冻结网页,当用户处于可见状态长时间没有操作,也会进入forzen状态。当处于这个阶段的时候,网页不会再分配CPU计算资源。定时器,回调函数,网络请求,dom操作都不会执行,不过正在运行的任务会执行完毕。浏览器可能会允许forzen阶段的页面,周期性的复苏一小段时间,短暂的变回hidden阶段,允许一小部分任务执行
6 .discarded:处于forzen阶段,用户又不唤醒页面,就会进入discarded阶段,浏览器自动卸载网页,清除该网页的内存占用。这一般是在用户没有介入的情况下,系统强制执行,任何类型的新任务或者js代码,都不会再此阶段执行,因为这个时候通常处在资源限制的情况下.网页被浏览器自动discarded以后。tab窗口还是存在,如果用户重新访问这个tab页面,浏览器将会重新向服务器发出请求,再一次重新加载网页,回到active阶段

4 .常见场景的切换

1 .打开网页->切换到其他app->回到网页(active-hidden-active)
2 .打开网页->切换到其他app->长时间使用后者,系统自动丢弃网页(active-hidden-frozen-discarded)
3 .打开网页->切换到其他app->从任务管理器将浏览器进程清除(active-hidden-terminated)
4 .系统丢弃了某个tab里面的页面后,用户重新打开这个tab(discarded-active)
5 .页面获得焦点时focus(passive-active)
6 .页面失去焦点的时候(active-passive)
7 .用户隐藏页面(切换tab,最小化浏览器),页面由active阶段变成hidden阶段
8 .用户重新访问隐藏的页面(hidden-active)
9 .freeze:网页进入frozen阶段触发 document.addEventListener('forzen',fn) 注意:这个事件的监听函数,最长只能运行500mm,并且只能复用已经打开的网络链接,不能发起新的网络请求
10 .从Frozen阶段进入discarded阶段,不会触发任何事件,无法指定回调函数,只能在进入forzen阶段时指定回调函数
11 .resume:网页离开forzen阶段,变为active.passive,hidden阶段触发
12 .pageshow:用户加载网页时触发。全新的网页加载:e.persisted属性为true,否则为false。会话历史新增一条记录
13 .pagehide:用户离开当前网络,进入另一个网页时触发,这个事件和网页的可见性没有任何关系,主要是看浏览器的history记录变化有关
14 .http://www.imooc.com/article/74160

事件

1 .网页的生命周期事件在所有的frame触发,不管是底层的,还会内嵌的,都会监听到下面的事件
2 .获取当前网页状态

const getState = () => {
  if (document.visibilityState === 'hidden') {
    return 'hidden';
  }
  if (document.hasFocus()) {
    return 'active';
  }
  return 'passive';
};
1 .Forzen状态:监听freeze事件
2 .terminated:监听pagehide事件
3 .document.wasDiscarded:了解当前网页是否被丢弃过

3 .所有页面的生命周期都是相互独立存在的,也就是说一个页面在同一时间点只能存在一个状态,而且通常大多数页面声明周期的状态修改都是通过dom事件监听
4 .浏览器会发送许多事件,但是只有小部分世界表明页面周期状态可能发生变化
5 .unload,beforeunload也会触发事件,但是尽量不要用

Page Visibility API

1 .开发者需要知道,用户正在离开页面,一般是监听以下事件

1 .pagehide
2 .beforeunload
3 .unload

2 .局限性:在手机上面不会触发,页面会直接关闭。比如以下几种场景

1 .用户点了一条系统通知,切换到了另一个app
2 .用户进入任务切换窗口,切换到下一个app
3 .用户点击home键

3 .监听网页的状态,一旦用户不看网页,很多网页行为都可以暂停

1 .对服务器的轮询
2 .网页动画
3 .正在播放的视频或音频

document.visibilityState

1 .表示当前页面状态的值

1 .hidden:页面彻底不可见
2 .visible:页面至少一部分可见
3 .prerender:页面即将或者正在渲染,处于不可见状态

2 .返回hidden的时候

1 .浏览器最小化
2 .当前页面切换成了背景页
3 .浏览器将要卸载页面
4 .操作系统触发锁屏屏幕
5 .

3 .这个属性只针对顶层窗口,内嵌的iframe页面的这个属性由顶层窗口决定。使用css属性影藏iframe页面是不会影响内嵌页面的可见性的
4 .

visibilitychange事件

document.addEventListener('visibilitychange',(e)=>{
            console.log(e)
            if(document.visibilityState==='hidden'){
                document.title='页面不可见'
            }
            if(document.visibilityState==='visible'){
                document.title='可见'
            }
        })

如何正确监听页面卸载

1 .只需要监听上面说的那个事件即可,别的都不可靠

观察页面内的生命状态

let state = getState();

// Accepts a next state and, if there's been a state change, logs the
// change to the console. It also updates the state value defined above.
const logStateChange = (nextState) => {
  const prevState = state;
  if (nextState !== prevState) {
    console.log(State change: ${prevState} >>> ${nextState});    state = nextState;
  }
};

// These lifecycle events can all use the same listener to observe state// changes (they call the getState() function to determine the next state).
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
  window.addEventListener(type, () => logStateChange(getState()), {capture: true});
});

// The next two listeners, on the other hand, can determine the next
// state from the event itself.
window.addEventListener('freeze', () => {
  // In the freeze event, the next state is always frozen.  logStateChange('frozen');
}, {capture: true});

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    // If the event's persisted property is true the page is about
    // to enter the page navigation cache, which is also in the frozen state.    logStateChange('frozen');
  } else {
    // If the event's persisted property is not true the page is
    // about to be unloaded.    logStateChange('terminated');
  }
}, {capture: true});

1 .传入capture:true的原因

1 .不是所有的生命周期事件的target都是一致,pagehide和pageshow事件在window山触发,visibilitychange,freeze以及resume事件在document上触发,而focus和blur事件会在他们各自的dom上触发
2 .这些事件不部分是不会冒泡的,这就意味着不能再公共的祖先上添加非捕获事件监听器监听所有事件
3 .捕获阶段在目标冒泡阶段之前执行,所以在此添加事件监听器能保证他能在其他代码取消他们之前执行

2 .浏览器兼容问题

1 .当切换标签页时有些浏览器没有触发blur事件。这就意味着页面可以直接从active状态进入hidden状态而不会先转为passive状态
2 .个别浏览器实现了页面导航缓存,而页面生命周期api定义了缓存的页面处于冻结状态。由于api完全是新的,所以这些浏览器还未实现freeze和resume事件,尽管这些状态仍然可以通过pagehide,pageshow事件被监听到
3 .ie浏览器没有实现visibility事件
4 .pagehide和visibilitychange事件的发生顺序有所改变。如果在写在页面时,并且页面处于可见状态,早起浏览器会先触发pagehide事件然后在触发visibilitychange事件。新的chrome事件则是先触发visibility事件然后再出发pagehide事件,无论卸载时文档是否为可见状态
5 .pagelifecycle.js规范了跨浏览器的事件触发顺序差异,就可以准确的按照约定触发事件了。

3 .对各种状态的理解

1 .active:对用户来说,active状态是最关键的,这个时候最关键的是响应用户的输入,因此任何可能阻止主线程的非ui工作都应该被停止或者使用web worker来执行

2 .在passive状态时,用户不会与页面互动,但是仍对用户可见。这也就是说ui更新及动画依然会很流畅,但是更新事件就没那么重要了。当页面从active改为passive时,现在是保持未保存的应用程序状态的最好时机

3 .hidden:开发者能最可靠的监听到的最后的状态变化就是页面转化为hidden状态。特别是在移动设备上,因为用户可以关掉tab或者浏览器,此时,beforeunload,pagehide,unload事件都不会触发。在这个时候。该保存所有没有保存的应用状态发送还未发送的所有需要分析的数据。这个时候也应该停止更新ui了,也要关闭所有用户不希望在后台运行的程序

4 .forzen:页面从hidden转为forzen状态时,必须停止任何计时器或拆除任何连接,如果冻结,可能会影响同一源的其他open选项卡标签,或影响浏览器将页面放入页面导航缓存的能力
4.1 .将任何动态的视图状态(无限滚动列表视图的滚动位置)保存到sessionStorage,这样随后页面被抛弃又被重新加载就能恢复到原来的状态
4.2 .关闭所有打开的indexDB连接,关闭打开的BroadcastChange连接,关闭打开的webRTC连接,停止所有网络轮询以及所有打开的web Socket连接,释放所有被保存的web Locks.当页面从forzen转变为hidden的时候,可以重新打开在任何冻结时刻关闭的连接或重启那时被停止的轮询

5 .terminated:值得注意的是,依靠termination事件,beforeunload,pagehide,unload事件是不可靠的,特别是在移动端的时候

6 .discarded:当页面正被抛弃的时候,这个状态是不可能被监听到的,所以要做好在hidden转为forzen的时候页面被抛弃的准备

避免旧版本的生命周期api

1 .unload事件

1 .现代浏览器中千万不要使用unload事件
2 .在很多情况,unload事件并不会触发,不能保证回调,所以是不能作为结束会话的信号来保存状态或者发送数据。
3 .特别是移动设备上,unload事件在一些典型的unload场景中并没有触发,包括从移动设备上标签切换器上关闭标签或者直接关闭浏览器app
4 .现代浏览器总是推荐使用pagehide事件来检测页面是否被卸载而不是使用unload事件

2 .beforeunload事件

1 .不要无条件的添加beforeunload监听器或将其作为会话结束的信号
2 .仅在用户未保存的工作时添加,并在保存工作以后立即将其删除
3 .当这个事件阿生时会阻止浏览器在页面导航缓存中缓存页面
4 .pageliftcycle.js中提供了addUnsavedChanges和removeUnsaveChanges事件来对这种情况做了最佳的实践,所以可以使用这个事件
5 .

常见问题

1 .如何避免页面在hidden的时候不被冻结或者抛弃

1 .比如当app在播放音乐的时候
2 .chrome丢弃页面的时候很保守,只有有把握不会影响用户时才会这样做,例如会检查以下情况,播放音频,使用webRtc,更新表格标题或图标,弹出警告时,发出推送通知
3 .

2 .页面缓存导航

1 .是一种帮助浏览器实现更快前进或者后退按钮的导航优化
2 .当用户离开网页的时候,这些浏览器会冻结该页面的版本,以便用户使用前进或后退按钮导航时能快速恢复。

3 .在frozen或terminated状态不能执行异步api,如何保存数据到indexedDB

1 .在forzen或terminated状态下,在页面任务队列里的冻结任务会被挂起,也就是说不能可靠的调用异步操作或基于api的回调
2 .未来indexDB会有一个commit事件,会保证完成写入数据的操作
3 .现在有两种选择,使用localStprage或者service worker中使用indexDB,在页面被终止或者丢弃时,使用postmessage发送数据到serveice worker

你可能感兴趣的:(其他常见事件)