路由跳转前的骨架屏实现

1、前言

(注意:本文是针对路由懒加载的项目)css

(1)骨架屏相信大多数人都听讲过,也实现过。(还不清楚骨架屏是什么的能够参考 开发一个简单易懂的webpack骨架屏插件html

但通常状况下都是给首页第一次加载的时候添加骨架屏,而不是给每一个路由页面都加上骨架屏。vue

怎么说呢?webpack

单页面应用第一次加载的时候须要加载大量的js,css资源,若是网络慢的状况,首次打开页面可能会出现十几秒的白屏时间。 如今市场上都是针对首次加载作了骨架屏优化,不多有作路由跳转到其余页面的骨架屏优化。(可能不少人都认为不必作其余路由页面的骨架屏优化,但本文先忽略这个必要性)web

(2)看了vue-skeleton-webpack-plugin的源码,发现该插件的routers也作不了其余路由页面的骨架屏优化。因此想出了本文的方案。vue-router

2、实现原理

(1)路由懒加载

市场上的单页面应用多数都会作路由懒加载,以vue项目为例: 在注册vue-router的时候,通常像下图作路由分割bash

大概意思就是,当跳转到 /home 页面的时候才加载/home页面对应的js资源。只有当这个js资源加载完成以后页面才会更新。因此若是加载这个js资源的时间用了10几秒,那么咱们将会原地等待10几秒,或者会白屏10几秒。这样的用户体验是很是很差的。网络

若是咱们能够在这10几秒里作一个骨架屏让它先展现出来,等到js资源加载回来后再隐藏它。这样的用户体验不是更加好呢。app

(2)实现原理:

如上图,component对应的value是一个函数,该函数就是加载js资源的Promise, 众所周知,Promise有then方法能够知道js资源何时加载完成。那么咱们能够在import('../views/home/index.vue')以前先让骨架屏显示出来,让<div id="app"></div>隐藏。而后在import('../views/home/index.vue').then()方法里再将骨架屏隐藏,<div id="app"></div>显示。svg

(2.1)首先对index.html作修改,以下:

在index.html里添加两个同级得div,一个是骨架屏得div,一个是<div id="app"></div>,你能够在骨架屏得div里写上本身的样式。再在<head>里写上简单的显示隐藏的css样式

<!DOCTYPE html>
<html lang="en">
  <head>    
    <meta charset="UTF-8">    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">    
    <meta http-equiv="X-UA-Compatible" content="ie=edge">    
    <title>Document</title>
    <style>
      .skeleton-block[data-v-07e00dc6] {  
        display: flex;  
        flex-direction: column;  
        padding: 16px;
      }
      .body-wrap-show .skeleton-wrap {
        display: block !important;
      }
      .body-wrap-show #app {
        display: none !important;
      }
      .body-wrap-hidden .skeleton-wrap {
        display: none !important;
      }
      .body-wrap-hidden #app {
        display: block !important;
      }
    </style>
  </head>
  <body>    
    <div data-server-rendered="true" class="skeleton-wrap" style="display: none;">
      <div data-v-07e00dc6>
        <section class="skeleton-block" data-v-07e00dc6>
          <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==" data-v-07e00dc6> 
          <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==" data-v-07e00dc6>
        </section>
      </div>
    </div>
    <div id="app"></div>    
    </script>
    </body>
    </html

复制代码

(2.2)路由注册文件的修改,以下

大概意思是:每次路由跳转以前先将<div id="app"></div>隐藏,让骨架怕展现。在then方法里再让<div id="app"></div>展现,骨架屏隐藏。这样每次路由跳转都会出现骨架屏了。

import Vue from 'vue';
import vueRouter from 'vue-router';
Vue.use(vueRouter);

const router = new vueRouter({
  mode: 'history',
  routes: [
    {
      path: '/home',
      component: () => {
        let body = document.body;
        // 每次路由跳转以前先将<div id="app"></div>隐藏
        document.getElementById('app').style.display = 'none';
        // 移除body上的body-wrap-hidden
        body.classList.remove('body-wrap-hidden');
        // 在body上的添加class 为body-wrap-show
        body.classList.add('body-wrap-show');

        return import('../views/home/index.vue').then(res => {
            //当js资源加载完成后,将骨架屏隐藏
            body.classList.remove('body-wrap-show');
            //当js资源加载完成后,将<div id="app"></div>展现
            body.classList.add('body-wrap-hidden');
            //释放body节点的引用
            body = null;
            // 注意:then方法里面必须得返回一个值,否则vue-router会报错
            return res;
        })
      }
    },
  ]
});

export default router;

复制代码

(2.4)效果以下:

留意上图,当我点击“我是首页”的时候,是马上出现骨架屏的,再看右边的js资源是正在加载,尚未加载完成。也就是说,它不是使用v-if或者v-show在“我是详情页面”的页面里面作的骨架屏。后者它有个限制,由于后者所谓的骨架屏也是属于“我是详情页面”的代码,必须等待js资源返回后才会显示。而前者则是加载js资源以前就已经显示了。 **

(2.3)结束语:

本文只是个思路,真正想作到每一个路由页面都有各自的骨架屏还得作不少的事情。 (有不当之处还望指出)。

相关文章
相关标签/搜索