关于Vue单页面骨架屏实践记录


Posted in Javascript onDecember 13, 2017

关于骨架屏介绍

骨架屏的作用主要是在网络请求较慢时,提供基础占位,当数据加载完成,恢复数据展示。这样给用户一种很自然的过渡,不会造成页面长时间白屏或者闪烁等情况。 常见的骨架屏实现方案有ssr服务端渲染和prerender两种解决方案。

这里主要通过代码为大家展示如何一步步做出这样一个骨架屏:

关于Vue单页面骨架屏实践记录

prerender 渲染骨架屏

本组件库骨架屏的实现也是基于预渲染去实现的,有关于预渲染更详细的介绍请参考这篇文章:处理 Vue 单页面 Meta SEO的另一种思路 下面我们主要介绍其实现步骤,首先我们也是需要配置webpack-plugin,不过已经有实现好的prerender-spa-plugin可用

var path = require('path')
var PrerenderSpaPlugin = require('prerender-spa-plugin')
module.exports = {
 // ...
 plugins: [
 new PrerenderSpaPlugin(
 // Absolute path to compiled SPA
 path.join(__dirname, '../dist'),
 // List of routes to prerender
 ['/']
 )
 ]
}

然后写好我们的骨架屏文件main.skeleton.vue

<template>
 <div class="main-skeleton">
 <w-skeleton height="80px"></w-skeleton>
 <div>
 <div class="skeleton-container">
 <div class="skeleton">
  <w-skeleton height="300px"></w-skeleton>
 </div>
 <w-skeleton height="45px"></w-skeleton>
 </div>
 <div class="skeleton-bottom">
 <w-skeleton height="45px"></w-skeleton>
 </div>
 </div>
 </div>
</template>

当初次进入页面的时候我们需要显示骨架屏,数据加载完,我们需要移除骨架屏:

<template>
 <div id="app">
 <mainSkeleton v-if="!init"></mainSkeleton>
 <div v-else>
 <div class="body"></div>
 </div>
 </div>
</template>
<script>
 import mainSkeleton from './main.skeleton.vue'
 export default {
 name: 'app',
 data () {
 return {
 init: false
 }
 },
 mounted () {
 // 这里模拟数据请求
 setTimeout(() => {
 this.init = true
 }, 250)
 },
 components: {
 mainSkeleton
 }
 }
</script>

ssr 渲染骨架屏

下面我用我灵魂画师的笔法,画出了大致的过程:

关于Vue单页面骨架屏实践记录

首先创建我们的skeleton.entry.js

import Vue from 'vue';
import Skeleton from './skeleton.vue';
export default new Vue({
 components: {
 Skeleton
 },
 template: '<skeleton />'
});

当然这里的skeleton.vue使我们事先写好的骨架屏组件,看起来可能是这样:

<template>
 <div class="skeleton-wrapper">
 <header class="skeleton-header"></header>
 <div class="skeleton-block"></div>
 </div>
</template>

然后我们需要的是能把skeleton.entry.js编译成服务端渲染可用的bundle文件,所以我们需要有个编译骨架屏的webpack.ssr.conf.js文件:

const path = require('path');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const nodeExternals = require('webpack-node-externals');
function resolve(dir) {
 return path.join(__dirname, dir);
}
module.exports = merge(baseWebpackConfig, {
 target: 'node',
 devtool: false,
 entry: {
 app: resolve('./src/skeleton.entry.js')
 },
 output: Object.assign({}, baseWebpackConfig.output, {
 libraryTarget: 'commonjs2'
 }),
 externals: nodeExternals({
 whitelist: /\.css$/
 }),
 plugins: []
});

接下来最终的步骤,就是编写我们的webpackPlugin,我们期望我们的webpackPlugin可以帮我们把入口文件编译成bundle,然后再通过vue-server-renderer来render bundle,最终产出响应的html片段和css片段,这里贴出核心代码:

// webpack start to work
 var serverCompiler = webpack(serverWebpackConfig);
 var mfs = new MFS();
 // output to mfs
 serverCompiler.outputFileSystem = mfs;
 serverCompiler.watch({}, function (err, stats) {

 if (err) {
  reject(err);
  return;
 }
 stats = stats.toJson();
 stats.errors.forEach(function (err) {
  console.error(err);
 });
 stats.warnings.forEach(function (err) {
  console.warn(err);
 });
 var bundle = mfs.readFileSync(outputPath, 'utf-8');
 var skeletonCss = mfs.readFileSync(outputCssPath, 'utf-8');
 // create renderer with bundle
 var renderer = createBundleRenderer(bundle);
 // use vue ssr to render skeleton
 renderer.renderToString({}, function (err, skeletonHtml) {
  if (err) {
  reject(err);
  }
  else {
  resolve({skeletonHtml: skeletonHtml, skeletonCss: skeletonCss});
  }
 });
 });

最后一步,我们对产出的html片段, css片段进行组装,产出最终的html,所以我们需要监听webpack 的编译挂载之前的事件:

compiler.plugin('compilation', function (compilation) {
 // add listener for html-webpack-plugin
 compilation.plugin('html-webpack-plugin-before-html-processing', function (htmlPluginData, callback) {
 ssr(webpackConfig).then(function (ref) {
  var skeletonHtml = ref.skeletonHtml;
  var skeletonCss = ref.skeletonCss;
  // insert inlined styles into html
  var headTagEndPos = htmlPluginData.html.lastIndexOf('</head>');
  htmlPluginData.html = insertAt(htmlPluginData.html, ("<style>" + skeletonCss + "</style>"), headTagEndPos);

  // replace mounted point with ssr result in html
  var appPos = htmlPluginData.html.lastIndexOf(insertAfter) + insertAfter.length;
  htmlPluginData.html = insertAt(htmlPluginData.html, skeletonHtml, appPos);
  callback(null, htmlPluginData);
 });
 });
 });

github 地址: VV-UI/VV-UI

演示地址: vv-ui

文档地址:skeleton

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript onmouseout 解决办法
Jul 17 Javascript
Jquery数独游戏解析(一)-页面布局
Nov 05 Javascript
extjs实现选择多表自定义查询功能 前台部分(ext源码)
Dec 20 Javascript
node.js实现逐行读取文件内容的代码
Jun 27 Javascript
JS常用字符串方法(推荐)
Jan 15 Javascript
angularJs中datatable实现代码
Jun 03 Javascript
Node.js+jade+mongodb+mongoose实现爬虫分离入库与生成静态文件的方法
Sep 20 Javascript
JS+HTML+CSS实现轮播效果
Nov 28 Javascript
jackson解析json字符串,首字母大写会自动转为小写的方法
Dec 22 Javascript
Vue渲染过程浅析
Mar 14 Javascript
vue实现扫码功能
Jan 17 Javascript
原生js实现日历效果
Mar 02 Javascript
JS实现利用两个队列表示一个栈的方法
Dec 13 #Javascript
node vue项目开发之前后端分离实战记录
Dec 13 #Javascript
详解vue-cli快速构建vue应用并实现webpack打包
Dec 13 #Javascript
Angularjs过滤器实现动态搜索与排序功能示例
Dec 13 #Javascript
Angular4编程之表单响应功能示例
Dec 13 #Javascript
详解webpack require.ensure与require AMD的区别
Dec 13 #Javascript
vue登录路由验证的实现
Dec 13 #Javascript
You might like
PHP一些有意思的小区别
2006/12/06 PHP
PHP数组实例总结与说明
2011/08/23 PHP
php实现12306余票查询、价格查询示例
2014/04/17 PHP
php实现mysql事务处理的方法
2014/12/25 PHP
PHP将HTML转换成文本的实现代码
2015/01/21 PHP
php使用lua+redis实现限流,计数器模式,令牌桶模式
2019/04/04 PHP
jQuery 遍历-nextUntil()方法以及prevUntil()方法的使用介绍
2013/04/26 Javascript
JS+CSS实现带小三角指引的滑动门效果
2015/09/22 Javascript
js实现的奥运倒计时时钟效果代码
2015/12/09 Javascript
js实现获取鼠标当前的位置
2016/12/14 Javascript
vue + socket.io实现一个简易聊天室示例代码
2017/03/06 Javascript
vue按需加载组件webpack require.ensure的方法
2017/12/13 Javascript
javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】
2018/08/01 Javascript
弱类型语言javascript中 a,b 的运算实例小结
2019/08/07 Javascript
[52:06]FNATIC vs NIP 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
书单|人生苦短,你还不用python!
2017/12/29 Python
python实现excel读写数据
2021/03/02 Python
numpy中实现ndarray数组返回符合特定条件的索引方法
2018/04/17 Python
PyTorch上搭建简单神经网络实现回归和分类的示例
2018/04/28 Python
pandas 数据归一化以及行删除例程的方法
2018/11/10 Python
使用Python 正则匹配两个特定字符之间的字符方法
2018/12/24 Python
django页面跳转问题及注意事项
2019/07/18 Python
pytorch的batch normalize使用详解
2020/01/15 Python
详解django使用include无法跳转的解决方法
2020/03/19 Python
pyecharts调整图例与各板块的位置间距实例
2020/05/16 Python
Win10下用Anaconda安装TensorFlow(图文教程)
2020/06/18 Python
详解Python直接赋值,深拷贝和浅拷贝
2020/07/09 Python
加拿大花店:1800Flowers.ca
2016/11/16 全球购物
业务经理的岗位职责
2013/11/16 职场文书
园林技术个人的自我评价
2014/01/08 职场文书
2014信息技术专业毕业生自我评价
2014/01/17 职场文书
行政人事经理职位说明书
2014/03/05 职场文书
2014年秋季新学期寄语
2014/08/02 职场文书
秋收起义观后感
2015/06/11 职场文书
Pandas加速代码之避免使用for循环
2021/05/30 Python
SpringBoot 拦截器妙用你真的了解吗
2021/07/01 Java/Android