浏览器资源加载优先级

前端资源加载优先级

我们经常会思考一个经典问题,”一个页面从输入URL到页面加载显示完成,这个过程都发生什么?”, 此问题涉及的范围可谓非常之广,今天我们只关注浏览器加载资源的过程。

资源加载优先级

首先,浏览器会根据资源重要性,给资源设定优先级(实际上会更复杂,浏览器会根据资源类型进行猜测,例如先加载css然后再加载js和img),我们可以通过Chrome Devtools查看资源加载优先级,如果你的Network面板没有Priority选项,可以在表头上点击右键显示,下图是我们生产环境一个页面的JS 和 CSS加载优先级

可以看到浏览器将资源优先级分为以下5类

Highest 最高
Hight 高
Medium 中等
Low 低
Lowest 最低

这五类规则见下图

img

手动控制加载优先级

虽然浏览器会自动判断资源重要性,但通常情况下,浏览器会因为获取的信息不足,导致做出错误的判断

那么如何手动控制浏览器加载优先级呢?

主要有3种指令:

  • preload 预加载
  • preconnect 预连接
  • prefetch 预获取

preload

link rel="preload" 告知浏览器当前导航需要某个资源,应尽快开始提取

可以注意到link上使用了属性“as”, 该属性允许你告知浏览器您将加载的资源类型,以便浏览器可以正确处理该资源

link rel="preload" 是强制浏览器执行的指令;与我们将探讨的其他资源提示不同,它是浏览器必须执行的指令,而不只是可选提示.

preconnect

link rel="preconnect" 告知浏览器您的页面打算与另一个起点建立连接,以及您希望尽快启动该过程。

预连接的作用是提前进行DNS查找,TCP连接,TLS协商(https)

但请注意,preconnet虽然成本很低,但依然会占用CPU时间,而且如果10s内没有使用此连接,情况尤为糟糕,因为当浏览器关闭连接时,所有已完成的连接都将遭到浪费。

除此之外,还有另外一个标签可用于回退方案,而且被浏览器厂商广泛支持

link rel="dns-prefetch" href="//s04.cdn.ipalfish.com"

prefetch

link rel="prefetch" 与以上2个指令不同,他不会发生资源抢占,而是在带宽空闲(idle)时发生

此指令通常用于此资源用于未来,例如其他页面跳转,或者用户交互后才会出现的行为,通常此资源的优先级会标记为 Lowest

prefetch 不会降低现有资源的优先级,即如果此资源很快会被加载,则尽量不要使用prefetch,否则会导致资源会被加载多次。

项目实践

绘本项目使用vue-cli 3搭建,已在生产环境实践使用prefetch,preload加载资源

Vue-cli3 默认会进行以上优化,方案是:

  1. 根据入口文件依赖添加PreloadWebpackPlugin ,以便初始化渲染

  2. 根据async chunk生成的文件 添加PrefetchWebpackPlugin 插件

开学季活动页面举例

路由规则如下:

routes: [
    {
      path: '/',
      name: 'Index',
      component: Index
    },
    {
      path: '/record',
      name: 'Record',
      component: Record
    },
    {
      path: '/rule',
      name: 'Rule',
      component: Rule
    },
    {
      path: '*',
      redirect: '/'
    }
  ]

根据以上对prefetch的描述,我们知道首屏加载资源使用prefetch很可能会导致资源会被多次加载,对此我们可以使用2种方案:

  1. 首页(首屏)加载资源不进行async处理
  2. 有选择的进行prefetch

现在项目中使用的是第二种方案,先在webpack中去掉prefetch插件

Object.keys(pages).forEach((page) => {
    // 去掉prefetch 业务自己决定 https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
    config.plugins.delete(`prefetch-${pages[page].path}`)
  })

然后业务方根据自己需要添加prefetch指令

const Index = () => import(
  /* webpackChunkName: "school-season-index" */
  './index')

const Record = () => import(
  /* webpackChunkName: "school-season-record" */
  /* webpackPrefetch: true */
  './record')

const Rule = () => import(
  /* webpackChunkName: "school-season-rule" */
  /* webpackPrefetch: true */
  './rule')

以上可通过开学季活动页面查看

除此之外还有prerender 可进行页面预渲染,但支持的浏览器厂商较少,暂不做考虑

参考

https://github.com/joshbuchea/HEAD

https://www.w3.org/TR/resource-hints

https://developers.google.com/web/fundamentals/performance/resource-prioritization

https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf