关于浏览器架构:深入了解现代网络浏览器第-4-部分
前言本文是进击的大葱对Mario Kosaka写的inside look at modern web browser系列文章的翻译。这里的翻译不是指直译,而是联合集体的了解将作者想表白的意思表达出来,而且会尽量补充一些相干的内容来帮忙大家更好地了解。 达到合成线程的输出这篇文章是探索Chrome外部工作原理的四集系列文章中的最初一篇了。在上一篇文章中,咱们探讨了一下浏览器渲染页面的过程以及学习了一些对于合成线程的常识,在本篇文章中,咱们要看一下当用户在网页上输出内容的时候,合成线程(compositor)做了些什么来保障晦涩的用户体验的。 从浏览器的角度来看输出事件当你听到“输出事件”(input events)的时候,你可能只会想到用户在文本框中输出内容或者对页面进行了点击操作,可是从浏览器的角度来看的话,输出其实代表着来自于用户的任何手势动作(gesture)。所以用户滚动页面,触碰屏幕以及挪动鼠标等操作都能够看作来自于用户的输出事件。 当用户做了一些诸如触碰屏幕的手势动作时,浏览器过程(browser process)是第一个能够接管到这个事件的中央。可是浏览器过程只能晓得用户的手势动作产生在什么中央而不晓得如何解决,这是因为标签内(tab)的内容是由页面的渲染过程(render process)负责的。因而浏览器过程会将事件的类型(如touchstart)以及坐标(coordinates)发送给渲染过程。为了能够正确地解决这个事件,渲染过程会找到事件的指标对象(target)而后运行这个事件绑定的监听函数(listener)。 图 1:点击事件从浏览器过程路由到渲染过程 合成线程接管到输出事件在上一篇文章中,咱们查看了合成线程是如何通过合并页面曾经光栅化好的层来保障晦涩滚动体验(scroll smoothly)的。如果以后页面不存在任何用户事件的监听器(event listener),合成线程齐全不须要主线程的参加就能创立一个新的合成帧来响应事件。可是如果页面有一些事件监听器(event listeners)呢?合成线程是如何判断出这个事件是否须要路由给主线程解决的呢? 图 2:视口悬停在页面图层上 理解非疾速滚动区域 - non-fast scrollable region因为页面的JavaScript脚本是在主线程(main thread)中运行的,所以当一个页面被合成的时候,合成线程会将页面那些注册了事件监听器的区域标记为“非疾速滚动区域”(Non-fast Scrollable Region)。因为晓得了这些信息,当用户事件产生在这些区域时,合成线程会将输出事件发送给主线程来解决。如果输出事件不是产生在非疾速滚动区域,合成线程就毋庸主线程的参加来合成一个新的帧。 图 3:非疾速滚动区域有用户事件产生时的示意图 当你写事件监听器的时候留点心眼Web开发的一个常见的模式是事件委托(event delegation)。因为事件会冒泡,你能够给顶层的元素绑定一个事件监听函数来作为其所有子元素的事件委托者,这样子节点的事件就能够对立被顶层的元素解决了。因而你可能看过或者写过相似于上面的代码: document.body.addEventListener('touchstart', event => { if (event.target === area) { event.preventDefault() }})只用一个事件监听器就能够服务到所有的元素,乍一看这种写法还是挺实惠的。可是,如果你从浏览器的角度去看一下这段代码,你会发现下面给body元素绑定了事件监听器后其实是将整个页面都标记为一个非疾速滚动区域,这就意味着即便你页面的某些区域压根就不在乎是不是有用户输出,当用户输出事件产生时,合成线程每次都会告知主线程并且会期待主线程解决完它才干活。因而这种状况下合成线程就丢失提供晦涩用户体验的能力了(smooth scrolling ability)。 图 4:当整个页面都是非疾速滚动区域时页面的事件处理示意图 为了加重这种状况的产生,您能够为事件监听器传递passive:true选项。 这个选项会通知浏览器您仍要在主线程中侦听事件,可是合成线程也能够持续合成新的帧。 document.body.addEventListener('touchstart', event => { if (event.target === area) { event.preventDefault() } }, {passive: true});passive:false,浏览器执行完回调函数才晓得有没有调用preventDefault,如果没有调用preventDefault,再去执行默认行为,就是滚动,这样就会造成滚动不晦涩。passive:true,就是通知浏览器不会调用preventDefault,浏览器间接执行滚动就行,不必思考回调函数了。这时,即便你在回调函数里调用preventDefault也不会失效。为了防止这一问题,大部分浏览器(Safari 和 Internet Explorer 除外)将文档级节点 Window、Document 和 Document.body 上的 wheel、mousewheel、touchstart 和 touchmove 事件的 passive 默认值更改为 true。如此,事件监听器便不能取消事件,也不会在用户滚动页面时阻止页面出现。还是要揭示大家,在你不须要调用preventDefault的时候,监听scroll或者touchmove,将passive设置为true ...