Vue页面骨架屏的实现方法


Posted in Javascript onMay 22, 2018

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

小米商城:

Vue页面骨架屏的实现方法

一、分析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>

Vue页面骨架屏的实现方法

由此可知,将骨架屏内容直接插入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>

看下效果:

Vue页面骨架屏的实现方法

效果还是阔以的。

尾声

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

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

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

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

Javascript 相关文章推荐
JavaScript 异步调用框架 (Part 2 - 用例设计)
Aug 03 Javascript
Jquery Ajax学习实例4 向WebService发出请求,返回实体对象的异步调用
Mar 16 Javascript
简单谈谈javascript代码复用模式
Jan 28 Javascript
初步认识JavaScript函数库jQuery
Jun 18 Javascript
JavaScript中的this关键字使用详解
Aug 14 Javascript
JavaScript驾驭网页-CSS与DOM
Mar 24 Javascript
Highcharts 多个Y轴动态刷新数据的实现代码
May 28 Javascript
Radio 单选JS动态添加的选项onchange事件无效的解决方法
Dec 12 Javascript
JS中对数组元素进行增删改移的方法总结
Dec 15 Javascript
jQuery Ajax前后端使用JSON进行交互示例
Mar 17 Javascript
用p5.js制作烟花特效的示例代码
Mar 21 Javascript
vue构建动态表单的方法示例
Sep 22 Javascript
Angular网络请求的封装方法
May 22 #Javascript
vue input输入框模糊查询的示例代码
May 22 #Javascript
vue 中swiper的使用教程
May 22 #Javascript
vue配置多页面的实现方法
May 22 #Javascript
vue.js使用v-model指令实现的数据双向绑定功能示例
May 22 #Javascript
详解js类型判断
May 22 #Javascript
vue.js过滤器+ajax实现事件监听及后台php数据交互实例
May 22 #Javascript
You might like
PHP parse_url 一个好用的函数
2009/10/03 PHP
Laravel框架使用技巧之使用url()全局函数返回前一个页面的地址方法详解
2020/04/06 PHP
JavaScript 继承机制的实现(待续)
2010/05/18 Javascript
基于jquery的仿百度的鼠标移入图片抖动效果
2010/09/17 Javascript
jQuery学习笔记之2个小技巧
2015/01/19 Javascript
js小数运算出现多位小数如何解决
2015/10/08 Javascript
javaScript给元素添加多个class的简单实现
2016/07/20 Javascript
深入浅出ES6新特性之函数默认参数和箭头函数
2016/08/01 Javascript
基于JS如何实现给字符加千分符(65,541,694,158)
2016/08/03 Javascript
BootStrap日期控件在模态框中选择时间下拉菜单无效的原因及解决办法(火狐下不能点击)
2016/08/18 Javascript
微信小程序 后台https域名绑定和免费的https证书申请详解
2016/11/10 Javascript
angular2路由切换改变页面title的示例代码
2017/08/23 Javascript
WebGL学习教程之Three.js学习笔记(第一篇)
2019/04/25 Javascript
基于javascript处理nginx请求过程详解
2020/07/07 Javascript
JavaScript的垃圾回收机制与内存管理
2020/08/06 Javascript
js 数据类型判断的方法
2020/12/03 Javascript
vue表单验证之禁止input输入框输入空格
2020/12/03 Vue.js
python 提取文件的小程序
2009/07/29 Python
python的正则表达式re模块的常用方法
2013/03/09 Python
解决Shell执行python文件,传参空格引起的问题
2018/10/30 Python
Python中利用aiohttp制作异步爬虫及简单应用
2018/11/29 Python
python实现的接收邮件功能示例【基于网易POP3服务器】
2019/09/11 Python
Python加密模块的hashlib,hmac模块使用解析
2020/01/02 Python
python爬虫开发之urllib模块详细使用方法与实例全解
2020/03/09 Python
python利用Excel读取和存储测试数据完成接口自动化教程
2020/04/30 Python
解决Django Haystack全文检索为空的问题
2020/05/19 Python
pycharm配置QtDesigner的超详细方法
2021/01/25 Python
日本即尚网:JSHOPPERS.com(支持中文)
2019/12/03 全球购物
英文翻译的自我评价语句
2013/10/04 职场文书
茶叶生产计划书
2014/01/10 职场文书
小学家长会邀请函
2014/01/23 职场文书
文化建设工作方案
2014/05/12 职场文书
音乐幼师求职信
2014/07/09 职场文书
高中校园广播稿3篇
2014/09/29 职场文书
春节晚会开场白
2015/05/29 职场文书
python单向链表实例详解
2022/05/25 Python