现代浏览器内部工作原理(附详细流程图)

发表于:2019-3-12 16:31

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:caihaihong    来源:掘金

    渲染管道最重要的事情:每个步骤,都是前一个操作的结果用于后一个操作的数据。如果为元素设置动画,每一帧都要进行相同的处理操作,大多数浏览器的1帧/秒如果浏览器丢失了中间部分帧,就会让人觉得卡顿。
  时间轴上的动画帧
  真正绘制一个页面
  光栅化
  简单光栅处理示意图
  现在知道了文档的结构,CSS,布局树,绘制顺序。就是将数据转化为物理设备上的像素了。这个过程成为光栅化。
  合成
  合成处理是将页面的各个部分光栅化,并且合成线程进行图层移动合成。
  分层
  为了分清哪些元素位于什么图层,主线程遍历布局树创建图层树,如果某些部分是单独图层(例如划入式侧面菜单栏),但没有拆分出来,可以用CSS属性:will-change提示浏览器。
  主线程的光栅化和合成
  一旦创建了图层树和确定了绘制顺序,主线程将会把信息传递给合成线程,接着,合成线程会光栅化每个图层,一个图层可能跟页面一样大,合成线程将其分块后发送给光栅线程。光栅线程光栅化每个小块后将他们存储在显存中。
  光栅线程创建分块的位图并发送到 GPU
  合成线程会不同的光栅化线程设置优先级,以便视图或者附近区域的画面可以先光栅化显示。图层还具有不同的分辨率的块,可以放大显示。
  一旦块被光栅化,合成线程会收集这些块的信息(称为绘制四边形),创建合成帧。
  绘制四边形:包含块在内存的位置,以及合成时块在页面中的位置等信息。
  合成帧:一个绘制四边形的集合,代表一个页面的一帧。
  接着,合成帧通过IPC提交给浏览器进程,此时,可以在UI线程或者其他插件的渲染进程添加一个合成帧,这些合成器帧被送到GPU然后在屏幕上显示。如果收到滚动事件,合成帧会创建另一个合成帧到GPU。
  合成线程创建合成帧,将其发送到浏览器进程,再接着发送到 GPU
  用户输入行为与合成器
  用户的任何行为,对于浏览器来说都是输入行为。包括点击,滚动,触摸屏幕,滑动鼠标。
  例如用户触摸屏幕时,浏览器进程率先捕捉行为,浏览器进程所掌握的信息仅限于行为发生的区域,因为标签内的内容都由渲染进程处理。浏览器进程会将点击行为以及坐标传达给渲染进程。渲染进程在进行相应的处理。
  合成器接收输入事件
  悬于页面图层的视图窗口
  理解非立即可滚动区
  运行JS是渲染进程的主线程的工作,页面合成之后,注册了事件的区域叫做“非立即可滚动区”,合成器线程会通知渲染进程的主线程处理。没有输入事件没有发生在事件注册区域,合成器进程则不需要等待主线程,可以继续合成帧。
  设置时间处理应该注意
   document.body.addEventListener('touchstart',
  event => {
  if (event.target === area) {
  event.preventDefault();
  }
  });
   事件代理是浏览器常用的事件处理模式,在顶层元素添加一个事件。
  这样带来的问题就是整个页面都被标识为非立即滚动区域,合成器进程需要每次都询问主线程是否需要处理事件并且等待反馈。流畅的合成器处理模式就失效了。
  你可以给事件监听添加一个 passive:true 选项 ,将这种负面效果最小化。这会提示浏览器你想继续在主线程中监听事件,但合成器不必停滞等候,可接着创建新的合成帧。
   document.body.addEventListener('touchstart', event => {
  if (event.target === area) {
  event.preventDefault()
  }
  }, {passive: true});
  不过上述写法可能又会带来另外一个问题,假设某个区域你只想要水平滚动,使用 passive: true 可以实现平滑滚动,但是垂直方向的滚动可能会先于event.preventDefault()发生,此时可以通过 event.cancelable 来防止这种情况。
   document.body.addEventListener('pointermove', event => {
  if (event.cancelable) {
  event.preventDefault(); // 阻止默认的滚动行为
  /*
  *  这里设置程序执行任务
  */
  }
  }, {passive:: true});
  也可以使用css属性 touch-action 来完全消除事件处理器的影响,如:#area{touch-action:pan-x;}
  查找事件对象
  当组合器线程发送输入事件给主线程时,主线程首先会进行命中测试(hit test),来查找对应的时间目标,命中测试会基于渲染过程中生成的绘制记录(paint records)查找事件发生坐标下寻找的元素。
  主线程检查绘制记录查询坐标 x、y 处绘制内容
  #####事件的优化
  一般我们屏幕的刷新速率为 60fps,但是某些事件的触发量会不止这个值,出于优化的目的,Chrome 会合并连续的事件(如 wheel, mousewheel, mousemove, pointermove, touchmove ),并延迟到下一帧渲染时候执行 。而如 keydown, keyup, mouseup, mousedown, touchstart, 和 touchend 等非连续性事件则会立即被触发。
  使用 getCoalescedEvents 获取帧内事件
  事件合并可帮助大多数 web 应用构建良好的用户体验。然而,如果你开发的是一个绘图类应用,需要基于 touchmove 事件的坐标绘制线路,那么在你试图画下一根光滑的线条时,区间内的一些坐标点也可能会因事件合并而丢失。这时,你可以使用目标事件的  getCoalescedEvents 方法获取事件合并后的信息。
   window.addEventListener('pointermove', event => {
  const events = event.getCoalescedEvents();
  for (let event of events) {
  const x = event.pageX;
  const y = event.pageY;
  // 使用 x、y 坐标画线
  }
  });
 
      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
33/3<123
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号