关于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 相关文章推荐
Flash对联广告的关闭按钮讨论
Jan 30 Javascript
情人节专属 纯js脚本1k大小的3D玫瑰效果
Feb 11 Javascript
jQuery easyui datagrid动态查询数据实例讲解
Feb 26 Javascript
javascript实现促销倒计时+fixed固定在底部
Sep 18 Javascript
jQuery中验证表单提交方式及序列化表单内容的实现
Jan 06 Javascript
jQuery文件上传插件Uploadify使用指南
Jun 05 Javascript
jQuery制作简单柱状图实例
Jan 28 Javascript
jQuery 判断图片是否加载完成方法汇总
Aug 10 Javascript
javascript实现表单验证
Jan 29 Javascript
EasyUI在Panel上动态添加LinkButton按钮
Aug 11 Javascript
Vue2几种常见开局方式详解
Sep 09 Javascript
jQuery实现简单评论功能
Aug 19 jQuery
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
PHP5 操作MySQL数据库基础代码
2009/09/29 PHP
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
PHP中使用json数据格式定义字面量对象的方法
2014/08/20 PHP
php将textarea数据提交到mysql出现很多空格的解决方法
2014/12/19 PHP
PHP依赖注入(DI)和控制反转(IoC)详解
2017/06/12 PHP
解决php用mysql方式连接数据库出现Deprecated报错问题
2019/12/25 PHP
使用JavaScript switch case 另类写法
2010/03/14 Javascript
javascript 获取iframe里页面中元素值的方法
2014/02/17 Javascript
如何书写高质量jQuery代码(使用jquery性能问题)
2014/06/30 Javascript
JavaScript数据绑定实现一个简单的 MVVM 库
2016/04/08 Javascript
基于jquery实现ajax无刷新评论
2020/08/19 Javascript
Bootstrap组件系列之福利篇几款好用的组件(推荐)
2016/06/23 Javascript
jquery控制页面的展开和隐藏实现方法(推荐)
2016/10/15 Javascript
详解angular2封装material2对话框组件
2017/03/03 Javascript
JS完成画圆圈的小球
2017/03/07 Javascript
web前端vue filter 过滤器
2018/01/12 Javascript
微信公众平台 发送模板消息(Java接口开发)
2019/04/17 Javascript
vue路由中前进后退的一些事儿
2019/05/18 Javascript
vue 地图可视化 maptalks 篇实例代码详解
2019/05/21 Javascript
设计模式中的原型模式在Python程序中的应用示例
2016/03/02 Python
Python实现列表删除重复元素的三种常用方法分析
2017/11/24 Python
python生成lmdb格式的文件实例
2018/11/08 Python
python爬取内容存入Excel实例
2019/02/20 Python
简单了解python 邮件模块的使用方法
2019/07/24 Python
解决python中导入win32com.client出错的问题
2019/07/26 Python
Python使用windows设置定时执行脚本
2020/11/12 Python
Ticketmaster德国票务网站:购买音乐会和体育等门票
2016/11/14 全球购物
说出一些常用的类,包,接口
2014/09/22 面试题
.NET remoting中对象激活的两种方式
2015/06/08 面试题
金融专业大学生自我评价
2014/01/09 职场文书
社区工作者先进事迹
2014/01/18 职场文书
大学生个人自荐信样本
2014/03/02 职场文书
工作检讨书500字
2014/10/19 职场文书
幼儿园综治宣传月活动总结
2015/05/07 职场文书
高中班主任培训心得体会
2016/01/07 职场文书
安全生产协议书
2016/03/22 职场文书