关于vue.js:vue移动端项目骨架屏的实现

Vue利用加载过程

咱们先来看看vue的入口文件index.html外面的内容,

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

从图里看到vue页面从关上到加载实现是有一段白屏的工夫的,那白屏的加载体验对于首次拜访的用户来说是难以承受的,咱们能够应用尺寸稳固的骨架屏,来辅助实现实在模块占位和霎时加载。

vue利用从关上到页面展现残缺大抵经验了一下几步

1.先渲染index.html的内容
2.动静插入chunk-vendors.js(第三方库)
3.动静插入app.js(业务代码)
4.渲染路由对应的页面

只有当app.js文件渲染实现之后,以后路由对应的vue才会被渲染到页面外面,而app.js是最初才被插入到index.html里的,因而会导致在app.js未加载之前会有一段空白的显示。

这会引出了一个用户体验的问题:用户看到页面内容之前的空白工夫过长,这样页面流失率会大大的减少,这也是咱们不违心看到的。

据统计:加载超过5秒就会有74%的用户来到页面。

骨架屏是什么

在页面齐全渲染实现之前,用户会看到一个款式简略,描述了以后页面的大抵框架,感知到页面正在逐渐加载,最终骨架屏中各个占位局部被齐全替换,体验良好。罕用于文章列表、动静列表页等绝对比拟规定的列表页面。
很多我的项目中都有利用: 饿了么h5版本,知乎,facebook等网站中都有利用。

这个过程中用户会感觉内容正在逐步加载行将出现,升高了用户的烦躁情绪,使得加载过程主观上变得晦涩。

骨架屏相比于传统的loading图会在感官上感觉内容呈现的晦涩而不突兀,体验更加低劣。

所以骨架屏的利用大大提高了用户体验。

上面咱们看看具体的实现办法

骨架屏实际

从vue渲染过程动图中咱们能看到index.html是最快加载的进去的
所以骨架屏须要放到index.html外面
在index.html将骨架屏Dom构造写好,而后在规定好的钩子函数外面去移除这个骨架屏就能够实现骨架屏机制。

然而这种形式有一个问题,如果每个页面的骨架屏的款式都不同,咱们须要手动在index.html写好每个页面对应的构造款式,而后再去解析路由去显示对应的骨架屏。这样很麻烦,不好保护。

呢有没有一个插件帮咱们去干两件事件:
1.将骨架屏vue文件提前渲染到index.html外面
2.渲染路由对应的骨架屏

这样一来就大大解放了双手,代码维护性也大大提高了。

vue-skeleton-webpack-plugin

github地址:https://github.com/lavas-proj…

这是一个基于 Vue 的 webpack 插件,为单页/多页利用生成骨架屏 skeleton,缩小白屏工夫,在页面齐全渲染之前晋升用户感知体验。

将骨架屏也看成路由组件,在构建时应用 Vue 服务端渲染性能,将骨架屏组件的渲染后果 HTML 片段插入 HTML 页面模版的挂载点中,将款式内联到 head 标签中。这样等前端渲染实现时,Vue 将应用客户端混合,把挂载点中的骨架屏内容替换成真正的页面内容。

  • 插件大抵实现思路

    1. 通过vue-server-renderer将骨架屏的Vue 实例渲染为 HTML
    2. Vue webpack 我的项目应用了 HTML Webpack Plugin生成 HTML 文件,通过该插件的html-webpack-plugin-before-html-processing 事件的回调函数插入骨架屏的内容。

插件具体实现可参考作者文章

  • 实现步骤
  1. 新建一个骨架屏的vue文件并将骨架屏的款式写好

  1. 在src文件加下新建一个skeleton.js文件,将方才创立的骨架屏文件引入,注册在components外面,并在template外面写入这个组件,留神:这里的组件肯定要申明ID值
import Vue from 'vue'
import SkeletonIndex from './components/skeleton/index'
export default new Vue({
    components: {
        SkeletonIndex
    },
    template: `
        <div>
            <SkeletonIndex id="skeletonIndex" style="display:none" />
        </div>
    `
})

3.因为我的我的项目是vue-cli3,所以要对vue.config.js操作,这里须要引入skeleton.js,并且能够在routes外面配置路由对应的骨架屏组件。留神extract: true必须要配置,否则骨架屏vue里的款式不会被插入到style外面。~~~~其余配置项能够到作者github上查问。

const path = require('path');
const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin');

module.exports = {
  css: {
    extract: true
  },
  configureWebpack: {
    plugins: [
      new SkeletonWebpackPlugin({
        webpackConfig: {
          entry: {
            app: path.join(__dirname, './src/skeleton.js'),
          },
        },
        router: {
          mode: process.env.NODE_ENV === 'development' ? 'hash' : 'history',
          routes: [
            { path: /.+/, skeletonId: 'skeletonIndex' },
          ]
        }
      }),
    ],
  },
};

配置实现重启利用看一下成果

理论利用遇到的问题

因为这个插件应用的服务端渲染,所以当app.$mount挂载胜利后,骨架屏的内容会被替换掉。
当咱们的业务代码缓缓变多后,页面dom的绘制会变慢,所以会呈现骨架屏隐没后会有短暂白屏的状况。

解决方案:

因为我发现在app.vue外面注册的全局组件会比<router-view></router-view>里的提前渲染,所以我在app.vue外面注册一个骨架屏的全局变量,而后在router.beforeEach外面去依据路由动静渲染对应的骨架屏。

// app.vue
<template>
  <div id="app">
    <router-view></router-view>
    <Skeleton />
  </div>
</template>
<script>
import Skeleton from './components/app/skeleton/index'

export default {
  components: {
    Skeleton
  }
}
</script>

// ./components/app/skeleton/index.vue
<template>
    <div v-if="skeletonName" class="skeleton-wrapper">
        <component :is="skeletonName"></component>
    </div>
</template>
<script>
import { mapState } from 'vuex'
import allTop from './allTop'
import bless_sort from './bless_sort'
import classify_singer from './classify_singer'
import home from './home'
import loading from './loading'
import mv from './mv'
import photo from './photo'
import photov2 from './photov2'
import send_video from './send_video'
export default {
    computed: {
        ...mapState({
            skeletonName: state => state.skeletonName
        })
    },
    components: {
        allTop,
        bless_sort,
        classify_singer,
        home,
        loading,
        mv,
        photo,
        photov2,
        send_video
    }
}
</script>


// main.js
router.beforeEach((to, from, next) => {
    store.commit('SAVE_TOGGLE_SKELETON', false)
    if (util.skeletonCheckRoute(to.path)) {
      store.commit('SAVE_TOGGLE_SKELETON', util.skeletonCheckRoute(to.path, 'getPath'))
    }
    next()
}

这样就解决了短暂白屏的问题,如果有更好的办法能够提出来。

总结

单利用的一个最大的问题就是首屏加载货色太多,加载工夫过长。
而骨架屏的利用只是升高了用户焦虑,咱们还是要优化代码体积。

  • 按需加载
  • 异步组件
  • webpack-bundle-analyzer

绘制骨架屏插件

vue-content-loader
在线绘制骨架屏网站:https://skeletonreact.com/

Vue Content Loader是一个基于Vue.js的SVG占位符加载,可自定义的SVG组件,用于创立占位符加载,例如Facebook加载卡。

Vue Content Loader是react-content-loader的Vue实现。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理