# Javascript 进阶知识点

Just Do it!

在我们对大型单页应用进行性能优化时,也许会用到按需懒加载的方式,来加载对应的模块,但如果能合理利用 link 标签的 rel 属性值来进行预加载,就能进一步提升渲染速度。

  • dns-prefetch

    当 link 标签的 rel 属性值为“dns-prefetch”时,浏览器会对某个域名预先进行 DNS 解析并缓存。这样,当浏览器在请求同域名资源的时候,能省去从域名查询 IP 的过程,从而减少时间损耗。下图是淘宝网设置的 DNS 预解析。

  • preconnect

    让浏览器在一个 HTTP 请求正式发给服务器前预先执行一些操作,这包括 DNS 解析、TLS 协商、TCP 握手,通过消除往返延迟来为用户节省时间。

  • prefetch/preload

    两个值都是让浏览器预先下载并缓存某个资源,但不同的是,prefetch 可能会在浏览器忙时被忽略,而 preload 则是一定会被预先下载

  • prerender

    浏览器不仅会加载资源,还会解析执行页面,进行预渲染

# OGP(开放图表协议)

OGP 是 Facebook 公司在 2010 年提出的,目的是通过增加文档信息来提升社交网页在被分享时的预览效果。 你只需要在一些分享页面中添加一些 meta 标签及属性,支持 OGP 协议的社交网站就会在解析页面时生成丰富的预览信息,比如站点名称、网页作者、预览图片。 具体预览效果会因各个网站而有所变化。

做如下配置:

<meta property="og:title" content="The Rock" />
<meta property="og:type" content="video.movie" />
<meta property="og:url" content="http://www.imdb.com/title/tt0117500/" />
<meta property="og:image" content="http://ia.media-imdb.com/images/rock.jpg" />

目前百度已宣布支持。可参考文档:https://ogp.me/ (opens new window)

# DOM 操作耗时原因

  • 线程切换

    如果你对浏览器结构有一定了解,就会知道浏览器包含渲染引擎(也称浏览器内核)和 JavaScript 引擎,它们都是单线程运行。单线程的优势是开发方便,避免多线程下的死锁、竞争等问题,劣势是失去了并发能力。

    浏览器为了避免两个引擎同时修改页面而造成渲染结果不一致的情况,增加了另外一个机制,这两个引擎具有互斥性,也就是说在某个时刻只有一个引擎在运行,另一个引擎会被阻塞。操作系统在进行线程切换的时候需要保存上一个线程执行时的状态信息并读取下一个线程的状态信息,俗称上下文切换。而这个操作相对而言是比较耗时的。

    每次 DOM 操作就会引发线程的上下文切换——从 JavaScript 引擎切换到渲染引擎执行对应操作,然后再切换回 JavaScript 引擎继续执行,这就带来了性能损耗。单次切换消耗的时间是非常少的,但是如果频繁的大量切换,那么就会产生性能问题。

    比如下面的测试代码,循环读取一百万次 DOM 中的 body 元素的耗时是读取 JSON 对象耗时的 10 倍。

    // 测试次数:一百万次
    const times = 1000000
    // 缓存body元素
    console.time('object')
    let body = document.body
    // 循环赋值对象作为对照参考
    for(let i=0;i<times;i++) {
      let tmp = body
    }
    console.timeEnd('object')// object: 1.77197265625ms
    
    console.time('dom')
    // 循环读取body元素引发线程切换
    for(let i=0;i<times;i++) {
      let tmp = document.body
    }
    console.timeEnd('dom')// dom: 18.302001953125ms
    
  • 重新渲染

    另一个更加耗时的因素是元素及样式变化引起的再次渲染,在渲染过程中最耗时的两个步骤为重排(Reflow)与重绘(Repaint)。

    浏览器在渲染页面时会将 HTML 和 CSS 分别解析成 DOM 树和 CSSOM 树,然后合并进行排布,再绘制成我们可见的页面。如果在操作 DOM 时涉及到元素、样式的修改,就会引起渲染引擎重新计算样式生成 CSSOM 树,同时还有可能触发对元素的重新排布(简称“重排”)和重新绘制(简称“重绘”)。

    可能会影响到其他元素排布的操作就会引起重排,继而引发重绘,比如:

    • 修改元素边距、大小
    • 添加、删除元素
    • 改变窗口大小

    与之相反的操作则只会引起重绘,比如:

    • 设置背景图片
    • 修改字体颜色
    • 改变 visibility 属性值

更多关于重绘和重排的样式属性,可以参看这个网址 (opens new window)