首页白屏优化实践

guwufeiyang guwufeiyang     2023-03-31     296

关键词:

前言

自从前端三大框架React、Vue、Angular面世以来,前端开发逐渐趋向规范化、统一化,大多数时候新建前端项目,首先想到使用的技术一定是三大框架之一,框架给前端开发带来了极大的便利和规范,但是由于这三大框架都是JS驱动,在JS没有解析加载完成之前页面无法展示,会处于长时间的白屏,带来了一定的用户体验问题,接下来本篇文章会介绍本人最近在白屏优化时遇到的一些问题和思考

SSR

想到白屏问题,首先想到的解决方案一般都是服务端渲染,在服务端将渲染逻辑处理好,然后将处理好的HTML直接返回给前端展示,这样就可以解决白屏的问题,也可以解决seo的问题,因为不需要动态获取数据了,但是,这和我早期的写后端时的开发模式很像,前端和后端关联在了一起,不利于维护,同时,对于前端工程师来说,要求变高来,需要了解一定的后端知识,虽然有类似Nuxt.js这类的SSR框架帮我们简化了服务端的部分,但是在要做定制或是解决bug时还是无法避免要对服务端部分进行调试、维护,成本颇高,还有需要考虑的服务端渲染会增加服务器压力,要处理并发、运行速度问题等等

预渲染

这个方案是相对简单直接的一个解决办法,尝试成本也比较低,这里介绍如何用prerender-spa-plugin做预渲染,这样就可以在浏览器进行渲染,而不需要将Vue或者React代码部署到服务器上,以vue-cli3的官方demo为例做配置,看具体的配置文件:

const path = require(‘path‘)
const PrerenderSPAPlugin = require(‘prerender-spa-plugin‘)
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
module.exports = 
  configureWebpack: config => 
    let plugins = []
    plugins.push(new PrerenderSPAPlugin(
      staticDir: path.resolve(__dirname, ‘dist‘),
      routes: [‘/‘, ‘/about‘],
      minify: 
        collapseBooleanAttributes: true,
        collapseWhitespace: true,
        decodeEntities: true,
        keepClosingSlash: true,
        sortAttributes: true
      ,
      renderer: new Renderer(
        renderAfterDocumentEvent: ‘custom-render-trigger‘
      )
    ))
    config.plugins = [
      ...config.plugins,
      ...plugins
    ]
  

上面代码是常用prerender-spa-plugin的配置,staticDir预渲染输出的文件地址,routes要做预渲染的路由,minify压缩相关的配置,renderer渲染引擎相关的配置,可以传入自定以的渲染引擎或者直接使用默认的PuppeteerRenderer,renderAfterDocumentEvent是渲染引擎配置中的一个属性,指当某个事件触发时才执行预渲染,这里 有关于渲染引擎的完整属性介绍,这很重要,尤其是对一些特定场景的下的需求,当然简单场景下完全可以不配置renderer渲染引擎选项,直接用默认选项。
接下来执行编译,看看会发生什么?
技术图片
dist目录下会生成路由对应的文件夹,打开index.html

  <div id="app">
   <div id="nav">
    <a href="/" class="router-link-exact-active router-link-active">Home</a> | 
    <a href="/about" class="">About</a>
   </div>
   <div class="home">
    <img alt="Vue logo" src="/img/logo.82b9c7a5.png" />
    <div class="hello" data-v-7b2de9b7="">
     <h1 data-v-7b2de9b7="">Welcome to Your Vue.js App</h1>
     <p data-v-7b2de9b7="">For a guide and recipes on how to configure / customize this project,<br data-v-7b2de9b7="" />check out the <a href="https://cli.vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">vue-cli documentation</a>.</p>
     <h3 data-v-7b2de9b7="">Installed CLI Plugins</h3>
     <ul data-v-7b2de9b7="">
      <li data-v-7b2de9b7=""><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" data-v-7b2de9b7="" rel="noopener" target="_blank">babel</a></li>
      <li data-v-7b2de9b7=""><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" data-v-7b2de9b7="" rel="noopener" target="_blank">eslint</a></li>
      <li data-v-7b2de9b7=""><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest" data-v-7b2de9b7="" rel="noopener" target="_blank">unit-jest</a></li>
     </ul>
     <h3 data-v-7b2de9b7="">Essential Links</h3>
     <ul data-v-7b2de9b7="">
      <li data-v-7b2de9b7=""><a href="https://vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">Core Docs</a></li>
      <li data-v-7b2de9b7=""><a href="https://forum.vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">Forum</a></li>
      <li data-v-7b2de9b7=""><a href="https://chat.vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">Community Chat</a></li>
      <li data-v-7b2de9b7=""><a href="https://twitter.com/vuejs" data-v-7b2de9b7="" rel="noopener" target="_blank">Twitter</a></li>
      <li data-v-7b2de9b7=""><a href="https://news.vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">News</a></li>
     </ul>
     <h3 data-v-7b2de9b7="">Ecosystem</h3>
     <ul data-v-7b2de9b7="">
      <li data-v-7b2de9b7=""><a href="https://router.vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">vue-router</a></li>
      <li data-v-7b2de9b7=""><a href="https://vuex.vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">vuex</a></li>
      <li data-v-7b2de9b7=""><a href="https://github.com/vuejs/vue-devtools#vue-devtools" data-v-7b2de9b7="" rel="noopener" target="_blank">vue-devtools</a></li>
      <li data-v-7b2de9b7=""><a href="https://vue-loader.vuejs.org" data-v-7b2de9b7="" rel="noopener" target="_blank">vue-loader</a></li>
      <li data-v-7b2de9b7=""><a href="https://github.com/vuejs/awesome-vue" data-v-7b2de9b7="" rel="noopener" target="_blank">awesome-vue</a></li>
     </ul>
    </div>
   </div>
  </div>

为了方便,这里只贴了app节点里的代码,以往在没有使用预渲染插件时app节点里面是空的没有内容,从加载index.html文件开始到js文件解析完成之前,由于app节点里面是空的,因此页面会处于白屏状态,但是预渲染插件在编译阶段就将对应的路由编译好插入到app节点,这样就能在js文件解析过程中有内容展示,js解析完成后,Vue会将app节点内的内容替换成Vue渲染好的内容,来看看chrome调试下渲染有什么区别:
常规渲染:
技术图片
预渲染:
技术图片
利用chrome浏览器的加载截屏功能可以看出常规渲染时会有明显的白屏时间,而预渲染则不会产生白屏,那么预渲染有什么缺点呢?

  • 动态数据无法展示,不同的用户看到的都是同样的页面
  • 路由很多时,代码构建时间太长
  • 用户容易误操作,由于预渲染时js还没有加载,因此展示出来的内容没有js的交互逻辑,比如按钮点击,在js加载完成之前用户点击是没有反应的
  • 预加载内容很少,当页面有内容是依赖动态数据加载时,在编译时是无法加载出动态数据的,因此会导致这部分内容编译不出来

骨架屏

骨架屏的实现原理和预加载类似,都是利用了Puppeteer爬取页面的功能,Puppeteer是chrome出的一个headlessChromenode库,提供了API可以抓取SPA并生成预渲染内容,和预加载不太一样的是骨架屏技术会在Puppeteer生成内容后,利用算法将生成的内容进行替换,生成骨架页面,page-skeleton-webpack-plugin是一个用来生成骨架屏的webpack插件,接下来就来看看怎么使用,还是以vue-cli3生成的官方项目为例:

<div id="app"><!-- shell --></div>
const SkeletonPlugin = require(‘page-skeleton-webpack-plugin‘).SkeletonPlugin
const path = require(‘path‘)
module.exports = 
  publicPath: ‘/‘,
  outputDir: ‘dist‘,
  configureWebpack: config => 
    let plugins = []
    plugins.push(new SkeletonPlugin(
      pathname: path.resolve(__dirname, ‘./shell‘), // pathname为来存储 shell 文件的地址
      staticDir: path.resolve(__dirname, ‘./dist‘), // 最好和 `output.path` 相同
      routes: [‘/‘, ‘/about‘], // 将需要生成骨架屏的路由添加到数组中
      port: ‘7890‘
    ))
    config.plugins = [
      ...config.plugins,
      ...plugins
    ]
  ,
  chainWebpack: config => 
    if (process.env.NODE_ENV === ‘production‘) 
      config.plugin(‘html‘).tap(opts => 
        console.log(opts[0])
        opts[0].minify.removeComments = false
        return opts
      )
    
  

上面例子是对page-skeleton-webpack-plugin的简单配置,想要完整的配置可以自行前往github获取,需要注意的是这段代码:

chainWebpack: config => 
    if (process.env.NODE_ENV === ‘production‘) 
      config.plugin(‘html‘).tap(opts => 
        console.log(opts[0])
        opts[0].minify.removeComments = false
        return opts
      )
    
  

这是修改了vue-cli3中集成的html-webpack-plugin的压缩配置,将移除注释去掉了,因为page-skeleton-webpack-plugin在编译时,注入代码依赖<!-- shell -->注释,而vue-cli3中集成的html-webpack-plugin会在编译做压缩,将注释去掉,因此要单独配置一下,否则会在编译时导致生成app节点下没有内容。

还有一个在使用时需要注意的点,如果你是vue-cli3脚手架生成的代码,运行时可能会报这样的错误:
技术图片
如果遇到这个错误,怎么解决呢?github上已经有对应的解决办法了,问题都说完了接下来看看怎么使用,运行项目后,在chrome调试器里执行toggleBar

技术图片
会在页面里显示一个Preview skeleton page按钮,点击后会生成一个新窗口
技术图片
这个窗口显示了当前页面的骨架屏样式和代码,可以修改骨架屏样式,然后点击右上角保存,会将对应路由的骨架屏保存到pathname对应的文件夹下
技术图片
然后执行编译,编译后会在staticDir中生成路由对应的html,这些html中的app节点下都被插入了路由对应骨架屏代码,然后在staticDir下启动服务访问,就能看到骨架屏的效果:
技术图片
从加载过程中可以看到骨架屏的加载

总结

本篇文章简单介绍了个人在白屏优化实践上尝试过的方案,每个方案都个有自己的优劣,需要自己根据实际的业务场景进行取舍,希望对大家在解决此类问题时有所帮助。
如果有错误或不严谨的地方,欢迎批评指正,如果喜欢,欢迎点赞

淘宝首页性能优化实践

上文《一起来看看淘宝首页的个性化》中,带大家看了下弥散着个性化味道的新首页,前端面临着:数据来源多串行请求渲染一个模块运营数据和个性化数据匹配和管理数据兜底容灾等多个问题。本次淘宝首页改版,虽已不再支... 查看详情

淘宝首页性能优化实践

想必很多人都已经看到了新版的淘宝首页,它与以往不太一样,这一版页面中四处弥散着个性化的味道,由于独特的个性化需求,前端也面临各方面的技术挑战:数据来源多串行请求渲染一个模块运营数据和个性化数据匹配和管... 查看详情

vue项目单页应用首页加载速度优化以及解决首页白屏问题(代码片段)

前言最近再接手一个vue项目的时候,公司运营部就说首页加载要10秒以上时间,这谁能忍受,老板也说时间太久,技术部老大说之前的同事优化过一次,时间还是这么久,重担就落在我身上了,于是我... 查看详情

cocoscreator—优化首页打开速度

...这是我比较喜欢CocosCreator的一点原因。其中一个优化点是首页的加载速度,开发组为了加快首页的渲染速度,减少白屏时间,把逻辑代码和首页加载代码做了彻底分离。首次页面加载的只是一个相当于加载器的初始化文件,文件... 查看详情

reactnative启动优化实践

...l复用能力,需要做以下几个事情Native与RN通信频繁是带来白屏的主要原因,那么可以结合一些特定的业务场景进行一些优化参考:ReactNative之启动流程Tree-Shaking性能优化实践-原理篇ReactNative性能 查看详情

vue性能优化1--懒加载(代码片段)

...加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,时间过长,会出啊先长时间的白屏,即使做了loading也是不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以... 查看详情

首页秒开实践指南

前端性能优化一直是衡量一个团队和一个前端的各方面水平,呈现快速的加载,是给人最直观,成就感也最足的一个感受,而且对用户体验是第一重要的概念,所以这个相当重要,现就来结合美团的实践方... 查看详情

vue首页秒开实践指南

一前端性能优化一直是衡量一个团队和一个前端的各方面水平,呈现快速的加载,是给人最直观,成就感也最足的一个感受,而且对用户体验是第一重要的概念,所以这个相当重要,现就来结合美团的实践... 查看详情

前端优化-如何计算白屏和首屏时间

白屏时间白屏时间指的是浏览器开始显示内容的时间。因此我们只需要知道是浏览器开始显示内容的时间点,即页面白屏结束时间点即可获取到页面的白屏时间。计算白屏时间因此,我们通常认为浏览器开始渲染 <body>&nb... 查看详情

cocoscreator视频播放器加载太慢

参考技术A可以优化首页的加载速度。开发组为了加快首页的渲染速度,减少白屏时间,把逻辑代码和首页加载代码做了彻底分离。首次页面加载的只是一个相当于加载器的初始化文件,文件体积特别小,不像某些引擎,一开始... 查看详情

reactnative首次加载白屏优化

  RN首次加载都会有个白屏过程,一般都会有500ms+的白屏时间,原生页面开发同样的页面会能够快速显示而在RN页面中有个明显的等待过程,这个会影响用户体验。 1.使用过度页面简单处理可以在白屏过程中加个过度页面,通... 查看详情

手机端白屏前端优化的方法,5种以上

手机白屏主要是因为页面渲染阻塞导致的,导致的原因有:1:css文件加载需要一定的时间,在加载的过程中页面是空白的解决:将css代码前置或者内联html即使用<style> 2.可能是等待异步加载数据再渲染页面导致白屏,数据... 查看详情

学习笔记网页出现白屏可能的原因与优化方法

问题描述页面白屏,具体情况如下页面白屏的主要原因是页面渲染被阻塞渲染阻塞的原因可能是因为CSS加载出现阻塞,然后又因为CSS在headb标签内,导致只有加载完CSS,才能显示内容,然而CSS出于某种原因加载... 查看详情

学习笔记网页出现白屏可能的原因与优化方法

问题描述页面白屏,具体情况如下页面白屏的主要原因是页面渲染被阻塞渲染阻塞的原因可能是因为CSS加载出现阻塞,然后又因为CSS在headb标签内,导致只有加载完CSS,才能显示内容,然而CSS出于某种原因加载... 查看详情

android应用启动时优化白屏问题

...情况下我们在启动APP的时候,屏幕会出现一段时间的白屏或者黑屏,不同的设备可能白屏黑屏显示的时间长短不同,设备硬件较差的时间都会比较长,这显然影响用户体验。现在我们来分析这个问题产生的原因。... 查看详情

解决优化app启动页白屏黑屏问题(代码片段)

1、需求:Androidapp 启动时总是黑屏或者白屏1秒钟  产品要去改进体验、于是再启动页添加了新的style配置成功解决问题代码如下:manifest中app 启动页:<activityandroid:name=".WelcomeActivity"android:theme="@s... 查看详情

androidmvvm框架使用(十三)ui更新(app启动白屏优化适配android10.0深色模式)(代码片段)

UI更新(App启动白屏优化、适配Android10.0深色模式)前言正文一、启动白屏优化1.样式文件2.修改页面二、适配深色模式1.颜色2.样式3.修改背景4.菜单适配5.BottomNavigationView6.TabLayout7.FloatingActionButton8.深色模式判断9.WebView设置1... 查看详情

解决react首屏加载白屏的问题

...在资源加载请求还未完成的时候,由于阻塞机制,会出现首页白屏的问题,产生很差的用户体验。本文以react为例,提供一个解决方法。解决原理:使用  onreadystatechange 去监听readyState,在资源加载完成之前加载一个只... 查看详情