Vue页面骨架屏的实现方法

 更新时间:2018年05月22日 09:59:00   作者:RThong  
在开发webapp的时候总是会受到首屏加载时间过长的影响,越来越多的APP采用了“骨架屏”的方式去提升用户体验。这篇文章主要介绍了Vue页面骨架屏的实现方法,感兴趣的小伙伴们可以参考一下

在开发webapp的时候总是会受到首屏加载时间过长的影响,主流的解决方法是在载入完成之前显示loading图效果,而一些大公司会配置一套服务端渲染的架构来解决这个问题。考虑到ssr所要解决的一系列问题,越来越多的APP采用了“骨架屏”的方式去提升用户体验。

小米商城:

一、分析Vue页面的内容加载过程

vue项目中的入口index.html只有简单的内容:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="root">    
  </div>
  <script type="text/javascript" src="bundle.js"></script></body>
</body>
</html>

当js执行完之后,会用vue渲染成的dom将div#root完全替换掉。

我们在div#root中加入模拟骨架屏,在Chrome开发者工具调整网速:

<div id="root">
  这里是骨架屏
</div>


由此可知,将骨架屏内容直接插入div#root中即可实现骨架屏。

二、使用vue-server-renderer来实现骨架屏

我们需要骨架屏也是一个单独的.vue文件,因此我们需要用到vue-server-renderer。对vue服务端渲染有所了解的同学一定知道,这个插件能够将vue项目在node端打包成一个bundle,然后由bundle生成对应的html。
首先是生成项目:

.
├── build
│  ├── webpack.config.client.js
│  └── webpack.config.server.js
├── src
│  └── views
│    ├── index
│    │  └── index.vue
│    ├── skeleton
│    │  └── skeleton.vue
│    ├── app.vue
│    ├── index.js
│    └── skeleton-entry.js
├── index.html
└── skeleton.js
└── package.json

vue的服务端渲染一般会用vue-server-renderer将整个项目在node端打包成一份bundle,而这里我们只要一份有骨架屏的html,所以会有一个单独的骨架屏入口文件skeleton-entry.js,一个骨架屏打包webpack配置webpack.config.server.js,而skeleton.js作用是将webpack打包出来的bundle写入到index.html中。

//skeleton-entry.js
import Vue from 'vue'
import Skeleton from './views/skeleton/skeleton.vue'

export default new Vue({
 components: {
  Skeleton
 },
 template: '<skeleton />'
})
//webpack.config.server.js
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

module.exports = {
 mode: process.env.NODE_ENV,
 target: 'node',
 entry: path.join(__dirname, '../src/skeleton-entry.js'),
 output: {
  path: path.join(__dirname, '../server-dist'),
  filename: 'server.bundle.js',
  libraryTarget: 'commonjs2'
 },
 module: {
  rules: [
   {
    test: /\.vue$/,
    loader: 'vue-loader'
   },
   {
    test: /\.css$/,
    use: [
     'vue-style-loader',
     'css-loader'
    ]
   }  
  ]
 },
 externals: Object.keys(require('../package.json').dependencies),
 resolve: {
  alias: {
   'vue$': 'vue/dist/vue.esm.js'
  }
 },
 plugins: [
  new VueLoaderPlugin(),
  new VueSSRServerPlugin({
   filename: 'skeleton.json'
  })
 ]
}

其中骨架屏的webpack配置因为是node端,所以需要target: 'node' libraryTarget: 'commonjs2'。在VueSSRServerPlugin中,指定了其输出的json文件名。当执行webpack会在/server-dist目录下生成一个skeleton.json文件,这个文件记载了骨架屏的内容和样式,会提供给vue-server-renderer使用。

//skeleton.js
const fs = require('fs')
const path = require('path')

const createBundleRenderer = require('vue-server-renderer').createBundleRenderer

// 读取`skeleton.json`,以`index.html`为模板写入内容
const renderer = createBundleRenderer(path.join(__dirname, './server-dist/skeleton.json'), {
 template: fs.readFileSync(path.join(__dirname, './index.html'), 'utf-8')
})

// 把上一步模板完成的内容写入(替换)`index.html`
renderer.renderToString({}, (err, html) => {
 fs.writeFileSync('index.html', html, 'utf-8')
})

注意,作为模板的html文件,需要在被写入内容的位置添加<!--vue-ssr-outlet-->占位符,本例子在div#root里写入:

<div id="root">
<!--vue-ssr-outlet-->
</div>

最后执行node skeleton就能实现vue的骨架屏。

最终的index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <title>Document</title>
<style data-vue-ssr-id="a7049cb4:0">
.skeleton[data-v-61761ff8] {
 position: relative;
 height: 100%;
 overflow: hidden;
 padding: 15px;
 box-sizing: border-box;
 background: #fff;
}
.skeleton-nav[data-v-61761ff8] {
 height: 45px;
 background: #eee;
 margin-bottom: 15px;
}
.skeleton-swiper[data-v-61761ff8] {
 height: 160px;
 background: #eee;
 margin-bottom: 15px;
}
.skeleton-tabs[data-v-61761ff8] {
 list-style: none;
 padding: 0;
 margin: 0 -15px;
 display: flex;
 flex-wrap: wrap;
}
.skeleton-tabs-item[data-v-61761ff8] {
 width: 25%;
 height: 55px;
 box-sizing: border-box;
 text-align: center;
 margin-bottom: 15px;
}
.skeleton-tabs-item span[data-v-61761ff8] {
 display: inline-block;
 width: 55px;
 height: 55px;
 border-radius: 55px;
 background: #eee;
}
.skeleton-banner[data-v-61761ff8] {
 height: 60px;
 background: #eee;
 margin-bottom: 15px;
}
.skeleton-productions[data-v-61761ff8] {
 height: 20px;
 margin-bottom: 15px;
 background: #eee;
}
</style></head>
<body>
  <div id="root">
    <div data-server-rendered="true" class="skeleton page" data-v-61761ff8><div class="skeleton-nav" data-v-61761ff8></div> <div class="skeleton-swiper" data-v-61761ff8></div> <ul class="skeleton-tabs" data-v-61761ff8><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li></ul> <div class="skeleton-banner" data-v-61761ff8></div> <div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div></div>
  </div>
</body>
</html>

看下效果:


效果还是阔以的。

尾声

文章开头小米商城手机页面就是用的这样的方法,不同的是它的骨架屏是一个base64的图片。

更多关于vue-server-renderer内容请戳vue-ssr

文章相关代码已经同步到Github,欢迎查阅~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • vue通过滚动行为实现从列表到详情,返回列表原位置的方法

    vue通过滚动行为实现从列表到详情,返回列表原位置的方法

    今天小编就为大家分享一篇vue通过滚动行为实现从列表到详情,返回列表原位置的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • vue前端传空值、空字符串的问题及解决

    vue前端传空值、空字符串的问题及解决

    这篇文章主要介绍了vue前端传空值、空字符串的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Vue3使用el-table组件实现分页、多选以及回显功能

    Vue3使用el-table组件实现分页、多选以及回显功能

    这篇文章主要介绍了Vue3使用el-table组件实现分页、多选以及回显功能,文中通过代码示例介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-09-09
  • VUE侧边导航栏实现筛选过滤的示例代码

    VUE侧边导航栏实现筛选过滤的示例代码

    本文主要介绍了VUE侧边导航栏实现筛选过滤的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • 分享12个Vue开发中的性能优化小技巧(实用!)

    分享12个Vue开发中的性能优化小技巧(实用!)

    一般来说,你不需要太关心vue的运行时性能,它在运行时非常快,但付出的代价是初始化时相对较慢,下面这篇文章主要给大家分享介绍了十二个Vue开发中的性能优化小技巧,需要的朋友可以参考下
    2022-02-02
  • vue+echarts定时重新绘制以达到刷新的动效问题

    vue+echarts定时重新绘制以达到刷新的动效问题

    这篇文章主要介绍了vue+echarts定时重新绘制以达到刷新的动效问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Vue使用axios post方式将表单中的数据以json格式提交给后端接收操作实例

    Vue使用axios post方式将表单中的数据以json格式提交给后端接收操作实例

    这篇文章主要介绍了Vue使用axios post方式将表单中的数据以json格式提交给后端接收操作,结合实例形式分析了vue基于axios库post传送表单json格式数据相关操作实现技巧与注意事项,需要的朋友可以参考下
    2023-06-06
  • 基于Vue实现电商SKU组合算法问题

    基于Vue实现电商SKU组合算法问题

    这篇文章主要介绍了基于Vue实现电商SKU组合算法问题 ,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-05-05
  • vue3.0中友好使用antdv示例详解

    vue3.0中友好使用antdv示例详解

    这篇文章主要给大家介绍了关于在vue3.0中如何友好使用antdv的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • 如何在Vue中实现登录验证功能(代码示例)

    如何在Vue中实现登录验证功能(代码示例)

    Vue是一种流行的JavaScript框架,可以帮助开发者建立高效的Web应用程序,本文将为您介绍如何在Vue中实现登录验证功能,并为您提供具体的代码示例,感兴趣的朋友一起看看吧
    2023-11-11

最新评论