关闭

首页白屏优化实践

时间: 2019-09-15阅读: 964标签: 优化

前言

自从前端三大框架reactvue、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下启动服务访问,就能看到骨架屏的效果:

从加载过程中可以看到骨架屏的加载

原文:https://segmentfault.com/a/1190000020383064

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/5313

关闭

Webpack 打包后代码执行时机分析与优化

代码执行时机将决定着是否能够正常执行,当依赖文件没加载完成就开始执行、使用对应模块,那么将会导致执行异常。这在“ 存在资源加载失败时,加载重试影响原来文件的执行顺序 ”的场景下尤为常见。

js查重去重性能优化心得

今天产品反映有个5000条数据的页面的保存按钮很慢,查看代码看到是因为点击保存按钮之后,进行了查重操作,而查重操作是用2个for循环完成了,时间复杂度是O(n^2)。没办法,只能想办法优化一下了。

从浏览器内部运行机制看性能优化

获取到的HTML/CSS/JS资源经过浏览器内核的处理生成图像呈现在浏览器上,那么下面我们就详细说说浏览器内核在拿到资源之后是经过哪些处理来生成我们最终所看到的页面。

优化Vue.js 单页应用速度

我有一个项目用了 Vue.js 来构建单页应用程序。随着上线日期的临近,性能优化的工作变得越来越重要。在本文中,我收集了有关在加载时间和渲染性能方面提高 Vue.js 应用性能的所有知识。

网站打开速度优化_如何提高网页访问速度技巧方法总结

网站的加载速度不仅影响着用户体验,也会影响搜索引擎的排名,在百度推出“闪电算法”以来,将网站首屏打开速度被列入优化排名行列,作为前端开发的我们需要如果来优化网站的打开速度呢?下面就整理挖掘出很多细节上可以提升性能的东西分享给大家

一次Web端大量图片同时加载卡顿问题的优化之旅

由于业务的需要,笔者最近需要实现一个大量图片同时加载的需求。在实现这个需求的过程中,笔者遇到了很多的坑,也总结了一些优化方案。这里将笔者使用或准备使用的优化方案总结一下。

Node学习笔记:优化crud增删改查

在安装过程中需要特别注意,需要将 Install MongoDB Compass 前默认的勾选取消,可以使用 mongod --version 命令查看 MongoDB 版本,若版本号输出正常则安装完毕

网页首屏性能优化总结

本文总结了系统地进行前端性能优化的整体思路。具体到实施的过程,每一种优化方法都能搜到详细的文章,这里就不展开了。同时,还应该结合具体的业务场景对症下药,最终真正的提高用户体验。符合预期。

Vue项目优化

在日常的Vue项目开发中,随着业务的日渐负责,代码量的日益增加,随之带来的问题就是打包后的vendor.js体积过大,导致加载时空白页时间过长,给用户的体验太差。为此我们需要减少vendor.js的体积,从本质上来解决这种问题

webpack构建速度常规优化方法

使用webpack.DllPlugin优化,其实并没有优化速度,只是把一些不需要经常变更的部分提前构建,之后只需要构建变更的部分就可以了,确实优化了时间,使用external,不构建基础脚本,使用外部脚本引入,确实减少了时间

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!