关于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 相关文章推荐
浅析return false的正确使用
Nov 04 Javascript
基于jquery自定义的漂亮单选按钮RadioButton
Nov 19 Javascript
jQuery遍历Table应用示例
Apr 09 Javascript
非jQuery实现照片散落桌子上,单击放大的LightBox效果
Nov 28 Javascript
javascript简单实现图片预加载
Dec 03 Javascript
JavaScript前补零操作实例
Mar 11 Javascript
json的结构与遍历方法实例分析
Apr 25 Javascript
vue中实现图片和文件上传的示例代码
Mar 16 Javascript
layui监听下拉选框选中值变化的方法(包含监听普通下拉选框)
Sep 24 Javascript
基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)
Oct 23 Javascript
JS实现压缩上传图片base64长度功能
Dec 03 Javascript
node.js爬虫框架node-crawler初体验
Oct 29 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
2019十大人气国漫
2020/03/13 国漫
DOTA2 1月28日更新:监管系统降临刀塔世界
2021/01/28 DOTA
Notice: Trying to get property of non-object problem(PHP)解决办法
2012/03/11 PHP
Smarty的配置与高级缓存技术分享
2012/06/05 PHP
PHP 使用二进制保存用户状态的实例
2018/01/29 PHP
脚本之家贴图转换+转贴工具用到的js代码超级推荐
2007/04/05 Javascript
Javascript拓展String方法小结
2013/07/08 Javascript
JS获取农历日期具体实例
2013/11/14 Javascript
node.js中的Socket.IO使用实例
2014/11/04 Javascript
基于RequireJS和JQuery的模块化编程——常见问题全面解析
2016/04/14 Javascript
jQuery EasyUI ProgressBar进度条组件
2017/02/28 Javascript
微信小程序中hidden不生效原因的解决办法
2017/04/26 Javascript
Vue动态实现评分效果
2017/05/24 Javascript
React Native 截屏组件的示例代码
2017/12/06 Javascript
vue单个组件实现无限层级多选菜单功能
2018/04/10 Javascript
vue.config.js常用配置详解
2019/11/14 Javascript
[49:20]2014 DOTA2国际邀请赛中国区预选赛5.21 CIS VS TongFu
2014/05/22 DOTA
python+opencv实现霍夫变换检测直线
2020/10/23 Python
Python实现统计英文文章词频的方法分析
2019/01/28 Python
python 中值滤波,椒盐去噪,图片增强实例
2019/12/18 Python
Python selenium环境搭建实现过程解析
2020/09/08 Python
Python3获取cookie常用三种方案
2020/10/05 Python
纯CSS实现预加载动画效果
2017/09/06 HTML / CSS
css3翻牌翻数字的示例代码
2020/02/07 HTML / CSS
TripAdvisor台湾:全球最大旅游网站
2018/08/26 全球购物
企业管理专业个人求职信范文
2013/09/24 职场文书
宣传策划类求职信范文
2014/01/31 职场文书
公务员政审个人鉴定
2014/02/25 职场文书
《毛主席在花山》教学反思
2014/04/20 职场文书
音乐剧猫观后感
2015/06/04 职场文书
电影红河谷观后感
2015/06/11 职场文书
2016优秀教师先进个人事迹材料
2016/02/25 职场文书
2019年浪漫婚礼证婚词
2019/06/27 职场文书
导游词之包公祠
2019/11/25 职场文书
Python中可变和不可变对象的深入讲解
2021/08/02 Python
关于PostgreSQL JSONB的匹配和交集问题
2021/09/14 PostgreSQL