关于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 相关文章推荐
JS实现打开本地文件或文件夹
Mar 09 Javascript
ajax 文件上传应用简单实现
Mar 03 Javascript
AJAX的跨域与JSONP(为文章自动添加短址的功能)
Jan 17 Javascript
js防止DIV布局滚动时闪动的解决方法
Oct 30 Javascript
jQuery实现的调整表格行tr上下顺序
Jan 10 Javascript
JS实现六边形3D拖拽翻转效果的方法
Sep 11 Javascript
js通过classname来获取元素的方法
Nov 24 Javascript
javascript函数的节流[throttle]与防抖[debounce]
Nov 15 Javascript
浅谈es6 javascript的map数据结构
Dec 14 Javascript
Node Puppeteer图像识别实现百度指数爬虫的示例
Feb 22 Javascript
JS在if中的强制类型转换方式
Jul 15 Javascript
Vue中ref和$refs的介绍以及使用方法示例
Jan 11 Vue.js
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中利用str_pad函数生成数字递增形式的产品编号
2013/09/30 PHP
PHP中$_SERVER的详细参数与说明介绍
2013/10/26 PHP
PHP对表单提交特殊字符的过滤和处理方法汇总
2014/02/18 PHP
php5.3以后的版本连接sqlserver2000的方法
2014/07/28 PHP
简单理解PHP的面向对象编程方式
2016/05/17 PHP
15款优秀的jQuery导航菜单插件分享
2011/07/19 Javascript
基于JQuery实现的类似购物商城的购物车
2011/12/06 Javascript
JavaScript组合模式学习要点
2016/08/26 Javascript
JavaScript中定时控制Throttle、Debounce和Immediate详解
2016/11/17 Javascript
vue-router:嵌套路由的使用方法
2017/02/21 Javascript
VUE中v-model和v-for指令详解
2017/06/23 Javascript
JavaScript中的垃圾回收与内存泄漏示例详解
2019/05/02 Javascript
使用vue-cli4.0快速搭建一个项目的方法步骤
2019/12/04 Javascript
[00:18]天涯墨客三技能展示
2018/08/25 DOTA
[43:32]Winstrike vs VGJ.S 2018国际邀请赛淘汰赛BO3 第一场 8.23
2018/08/24 DOTA
使用Python下的XSLT API进行web开发的简单教程
2015/04/15 Python
Python实现SSH远程登陆,并执行命令的方法(分享)
2017/05/08 Python
Python实现利用163邮箱远程关电脑脚本
2018/02/22 Python
Flask框架信号用法实例分析
2018/07/24 Python
Python使用matplotlib绘制随机漫步图
2018/08/27 Python
浅谈django的render函数的参数问题
2018/10/16 Python
python实现批量注册网站用户的示例
2019/02/22 Python
python实现抠图给证件照换背景源码
2019/08/20 Python
Python PyInstaller安装和使用教程详解
2020/01/08 Python
TensorFlow梯度求解tf.gradients实例
2020/02/04 Python
Python基于数列实现购物车程序过程详解
2020/06/09 Python
为中国消费者甄选天下优品:网易严选
2016/08/11 全球购物
XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式?
2016/01/12 面试题
如何做好总经理助理
2013/11/12 职场文书
成功的酒店创业计划书
2013/12/27 职场文书
美德好少年主要事迹
2014/01/29 职场文书
四风问题自查报告剖析材料
2014/02/08 职场文书
电子工程专业毕业生求职信
2014/03/14 职场文书
群众路线党员个人剖析材料
2014/10/08 职场文书
怎么写工作检讨书
2014/11/16 职场文书
辩护意见书
2015/06/04 职场文书