spa单页应用-首屏性能优化-进阶版(1s内)

2,178 阅读3分钟

spa单页应用-首屏性能优化-进阶版(1s内)

点赞在看,养成习惯 (~ ̄▽ ̄)~

需先了解一下初级版

初级版(我的另一篇):juejin.cn/post/699685…

初级版存在的问题?

初级版主要是解决了:接口延迟,导致路由渲染延迟,进而导致页面渲染延迟的问题。

但是:在渲染首屏内容之前还解析了很多资源:比如(常见) vue / vue-router / vuex / ui组件库 等 (此处暂时以vue为例,react也是类似的)

其实我们的优化空间还有:

方案一

  • 所有资源都不加载,我先渲染静态部分(比如整个页面的结构,布局,样式,色块等),首屏东西出来后,在加载其他 (方案一的优化思路类似骨架屏

方案二

  • 只需加载一个框架资源vue或react(不加载router等),首屏渲染出来后,在加载其他

两个方案效果对比

两个方法都有尝试过,此处我简单作个结论

  1. 两种方法都可以用,方案一的首屏时间会更快,但是对代码的结构改动较大,因为要写原生代码(写在入口的index.html文件内,其他的script标签都放页尾)(方案一除了类似骨架屏,也类似服务端渲染优化思路)

  2. 方案二会比方案一稍微慢一点,因为多加载了一个框架资源vue或react。但方案二对代码结构改动较小,更容易实现

方案二的实现技巧

背景:有小伙伴问我,组件库好实现延迟加载,但是延迟加载router和store实现不了

所以,以下主要是方案二的实现介绍:

  • 此处以vue2为例(vue3的按需加载比vue2要做的更好,所以vue3实现还要更容易。react也是类似思路)

首先以一个基础项目举例,直接vue-cli出来的简单项目(vue2)cli.vuejs.org/zh/guide/cr…

目录结构,就列几个主要文件

src
  lazyApp.vue
  App.vue
  main.js
vue.config.js

首先 vue.config.js

// vue.config.js
module.exports = { 
  runtimeCompiler: true // 配置这个,目的是为了 支持动态解析template (有兴趣了解原理的话,可以看我另一篇: https://juejin.cn/post/7043991342166310942)
}

以下是main.js ,cli出来的原样,几乎不用动

// main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

以下是app.vue

  • 以下实现了路由vue-router的按需加载 (要安装依赖 npm i -S vue-router@3

  • 原理类似是 在#app里面在套了一个#lazyApp,目的是先渲染 #app的内容,让首屏更快的渲染出来,在加载其他资源和内容(其他内容放在 #lazyApp 内)

<template>
  <div id="app">
    <h1>我是首屏最先出来的内容</h1>
    <h1>我是首屏最先出来的内容</h1>
    <h1>我是首屏最先出来的内容</h1>
    <h1>我是首屏最先出来的内容</h1>

    <div id="lazyApp"></div> // 此处放 懒加载的内容
  </div>
</template>

<script>
import Vue from 'vue'

import lazyApp from './lazyApp.vue'

export default {
  name: 'App',
  mounted () {
    import(/* webpackChunkName: 'vue--router' */'vue-router').then(VueRouter => {
      VueRouter = VueRouter.default
      
      Vue.use(VueRouter)

      const Foo = { template: '<div>foo</div>' }
      const Bar = { template: '<div>bar</div>' }
      const CCC = { template: '<div>ccc</div>' }
      
      const router = new VueRouter({
        routes: [
          { path: '/a', component: Foo },
          { path: '/b', component: Bar },
          { path: '/c', component: CCC },
        ]
      })
      new Vue({
        render: h => h(lazyApp),
        router
      }).$mount('#lazyApp') // 会把 <div id="lazyApp"></div> 这个标签替成 lazyApp.vue
    })
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
a {
  margin: 10px;
}
</style>

以下是 lazyApp.vue (可以理解为嵌套的 #app)

<template>
  <div>
    <span>我是菜单: </span>
    <a href="/#/a">a</a>
    <a href="/#/b">b</a>
    <a href="/#/c">c</a>

    <router-view></router-view>

    <h3>我是lazyApp的内容</h3>
    <h3>我是lazyApp的内容</h3>
    <h3>我是lazyApp的内容</h3>
    <h3>我是lazyApp的内容</h3>
  </div>
</template>

加载的效果

  • 图片内有3个色块,可以按色块和文字备注,以及右侧network面板搭配,来更好的理解优化效果(先加载 首屏 -> 在其他)

首屏性能优化.png

总结

优化方案一

  • 所有资源都不加载,我先渲染静态部分(比如整个页面的结构,布局,样式,色块等),首屏东西出来后,在加载其他 (方案一的优化思路类似骨架屏

优化方案二

  • 只需加载一个框架资源vue或react(不加载router等),首屏渲染出来后,在加载其他

两个方案的对比:

  1. 两种方法都很有效,方案一的首屏时间会更快,但是对代码的结构改动较大,因为要写原生代码(写在入口的index.html文件内,其他的script标签都放页尾)(方案一的优化思路 类似骨架屏,也类似服务端渲染)

  2. 方案二会比方案一稍微慢一点,因为多加载了一个框架资源vue或react。但方案二对代码结构改动较小,更容易实现

优化后的效果:

  1. 首屏正常可以优化到1秒内(正常网速和网络情况)

码字不易,点赞鼓励!