V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
yuutan
V2EX  ›  分享创造

killblanks 预渲染和骨架屏解决白屏问题

  •  
  •   yuutan · 2021-03-09 15:27:48 +08:00 · 849 次点击
    这是一个创建于 1353 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    白屏一直是 CSR 项目诞生来困扰前端的一大问题,如何在低成本的情况下,增加用户的等待时间,减少跳出率,以及提高页面性能,是前端一直在解决的难题,killblanks 作为其中一种的解决方案,将页面节点直接生成骨架屏,通过预渲染让用户能在等待内容加载时显示内容的轮廓,提供了更好的用户体验,并使内容感觉更快。

    白屏是怎么产生的

    这是一个普通 CSR 项目的Chrome performance截图,可以从快照中看到,白屏的时间大约在 330ms 左右,

    在这段时间里页面的执行逻辑 :

    • 请求 HTML -> 等待 HTML 文档 -> 解析 HTML -> 请求 JS 和 CSS 资源 -> 执行 vendors 和 index.js 逻辑

    330ms 后页面出现大体框架,页面的执行逻辑:

    • vue render 各个组件 -> mounted 页面挂载 -> 调用业务的 API 请求数据 -> 渲染返回的数据 -> 完整的页面

    正是因为 CSR 项目在等待文件加载CSSOM 构建JS 解析等过程中耗费了大量时间,导致了用户会长时间处于不可交互的首屏灰白屏状态,即白屏(blanks)

    详细可以看看寒阳的文章构建时预渲染:网页首帧优化实践

    白屏会影响什么

    通过分析 150 多个网站和 1.5 亿次页面访问的页面放弃数据,戈麦斯发现了游客忠诚度的匮乏,页面响应时间从 2 秒增加到 10 秒,页面跳出率增加了 38%

    简单来说每增加 1s 页面的等待时间,对于 app 来说是跳出率和成交率的下降,对于老板来说就是赔钱,对于你来说就是涨不了工资

    目前常用的白屏解决方案

    方案 预渲染 SSR NSR
    优点 不依赖数据 SEO 友好,构建方便,FMP 比预渲染快 SEO 友好,首屏性能高, FMP 最快
    缺点 SEO 不友好,FMP 慢 成本高,前端负载压力大 成本高,客户端压力大

    SSR 和 NSR 都是非常好的解决白屏的方案,但是缺点也很明显,成本太高,SSR 非常依赖服务的稳定性,对于中小公司而言很少有资源能为前端提供一套稳定的服务器环境,一旦 node 出现故障,损失很大,而 CSR 方案则是对于客户端来说成本很高,有很多不确定性。

    预渲染不是银弹

    基于此,预渲染可能是最简单也最实际的一种方案,但预渲染也并非是银弹,来看看我们团队第一个版本实现的预渲染

    当你高高兴兴的把辛苦做好的预渲染交给视觉视检时,可能换来的是只是一个字:(

    没错,或者说是不友好,感觉网速很慢,这些都是看过第一个版本的同事的反应,为什么会这样?

    通过两个截图对比可以发现

      1. 图片未加载导致页面存在大面积的色块
      1. 后端的请求未返回也造成了页面的空缺甚至是框架的变形

    我们发现问题主要在元素占位上,如果能提前对元素进行占位,预渲染就能渲染出很漂亮的页面,那么有没有方便 快捷 好看的占位元素呢?

    当我搜索解决方案的时候发现,早有人提出了一种占位方案

    一种自动化生成骨架屏的方案

    骨架屏

    是的,正是@Jocs,在 18 年分享了一种自动化生成骨架屏的方案,以及后续开源的page-skeleton-webpack-plugin,提供了一种成本较低的解决骨架屏的方案,为骨架屏实现方式提供一种新的思路,具体方案可以看看他的文章

    比较可惜的是page-skeleton-webpack-plugin三年没更新维护了,虽然是一种很可行的方案,但是还有很多能提高的空间,因此我决定在page-skeleton-webpack-plugin的基础上,提供一种更方便的方案,这就是Killblanks的由来

    Killblanks

    2020 年这个项目正式开始,经过半年的调试和开发,终于成功通过abtest测试,落地上线, 并于 2021 年 2 月开源,感谢每一位对此付出的人。

    原理

    利用Purpeteer能模拟浏览器请求页面的功能,加载谷歌插件@killblanks/skeleton-ext生成骨架屏组件的页面,直出 html 文件

    框架

    目前Killblanks依靠lerna构建,由三个核心功能组成

    使用

    Killblanks 使用非常方便,如果你明白其中原理,三分钟即可上手

    1. 安装

      yarn add @killblanks/prerender -D
    

    2. 配置

    // webpack.config.js
    const prerender = require('@killblanks/prerender')
    
    export default {
      ...
      plugins: [new prerender()]
      ...
    }
    

    3. 使用@killblanks/skeleton-ext

    4. 将生成的骨架屏组件使用在项目中

    • 比如像DEMO中所做的一样
    // index.vue
    <template>
      <div class="container">
        <skeleton :show="!!filterProductList.length">
          <div class="productionList">
            <div v-for="(item, key) in filterProductList" :key="item.goods_id + key" class="production">
              xxx
            </div>
          </div>
        </skeleton>
      </div>
    </template>
    
    <script>
    import skeleton from './skeleton'
    export default {
      components: {
        skeleton
      },
      data: () => {
        return {
          filterProductList: []
        }
      },
      mounted() {
        setTimeout(() => {
          const res = JSON.parse(
            `{"goods_id":"5e7d6d331d41c801b95f594f","name":"skeleton-test","photo":"https://o-static.ihago.net/ikxd/e62403ac0d365c57b4dbc1a0ab7e9cf4/128.png","svga_photo":"","tag":"new","type":1,"type":1805,"real_price":199,"price":299,"discount":8000,"update_time":1594695268}`
          )
          this.filterProductList = Array(10).fill(res)
        }, 3000)
      }
    }
    </script>
    
    // skeleton.vue
      <script>
    import Vue from 'vue'
    const skeletonLoader = {
      name: 'skeletocnLoader',
      functional: true,
      props: {
        show: {
          type: Boolean,
          default: false
        }
      },
      render(h, context) {
        const { show } = context.props
        if (!show || window.__PRERENDER_INJECTED__) {
          const html = `<div>xxx</div>`
          const component = Vue.compile(html)
          return h(component)
        } else {
          return context.children[0]
        }
      }
    }
    export default skeletonLoader
    </script>
    

    5. 在浏览器的console输入PRERENDER_SKELETON

     在 Chrome console 中输入`PRERENDER_SKELETON`启动骨架屏预览
    

    最后效果

    性能

    最后看看abtest测试,Killblanks带来的页面性能数据上的改变

    数据来源:

    利用公司在印度尼西亚上线的活动,进行 abtest 得出相关数据

    数据:

    type total fcp lcp
    @killblanks 1532 536ms 661ms
    Normal 1730 990ms 993ms

    First-contentful-paint(fcp)

    • FCP 平均值对比:536 : 990 @killblanks 能提升454ms, 平均提高45%

    Largest-contentful-paint(lcp)

    • LCP 平均值对比:661 : 993 @killblanks 能提升332ms, 平均提高33.4%

    未来

    killblanks 未来还有很多功能需要完善,目前正在做的

    • 单元测试
    • 优化文档
    • 支持 react && angular
    • 完善 demo

    欢迎各位前端朋友可以在issue留下自己的意见,拒绝白嫖,有时间就为killblanks点下⭐吧。

    3 条回复    2021-03-09 17:06:19 +08:00
    amundsen
        1
    amundsen  
       2021-03-09 16:15:42 +08:00
    好东西
    justin2018
        2
    justin2018  
       2021-03-09 16:40:53 +08:00
    好东西
    iikebug
        3
    iikebug  
       2021-03-09 17:06:19 +08:00
    牛逼
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3616 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 00:53 · PVG 08:53 · LAX 16:53 · JFK 19:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.