关于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 相关文章推荐
JQuery 学习笔记 选择器之四
Jul 23 Javascript
Jquery each方法跳出循环,并获取返回值(实例讲解)
Dec 12 Javascript
多个js毫秒倒计时同时进行效果
Jan 05 Javascript
JS函数的定义与调用方法推荐
May 12 Javascript
Vue.js bootstrap前端实现分页和排序
Mar 10 Javascript
jQuery选择器之子元素选择器详解
Sep 18 jQuery
原生JS实现的双色球功能示例
Feb 02 Javascript
node实现基于token的身份验证
Apr 09 Javascript
详解Vue2 添加对scss的支持
Jan 02 Javascript
通过jQuery学习js类型判断的技巧
May 27 jQuery
VUE动态生成word的实现
Jul 26 Javascript
IDEA配置jQuery, $符号不再显示黄色波浪线的问题
Oct 09 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
PHP Curl多线程原理实例详解
2013/11/06 PHP
ThinkPHP的URL重写问题
2014/06/22 PHP
windows下配置php5.5开发环境及开发扩展
2014/12/25 PHP
全面解读PHP的人气开发框架Laravel
2015/10/15 PHP
smarty的section嵌套循环用法示例
2016/05/28 PHP
PHP+Session防止表单重复提交的解决方法
2018/04/09 PHP
laravel5.6框架操作数据curd写法(查询构建器)实例分析
2020/01/26 PHP
自动检查并替换文本框内的字符
2006/06/30 Javascript
JS 加入收藏夹的代码(主流浏览器通用)
2013/05/13 Javascript
javascript实现全角半角检测的方法
2015/07/23 Javascript
一个php+js实时显示时间问题
2015/10/12 Javascript
jQuery图片轮播插件——前端开发必看
2016/05/31 Javascript
深入学习Bootstrap表单
2016/12/13 Javascript
JS验证input输入框(字母,数字,符号,中文)
2017/03/23 Javascript
JS实现带导航城市列表以及输入搜索功能
2018/01/04 Javascript
js 实现 list转换成tree的方法示例(数组到树)
2019/08/18 Javascript
vue select 获取value和lable操作
2020/08/28 Javascript
python 判断是否为正小数和正整数的实例
2017/07/23 Python
python获取本机所有IP地址的方法
2018/12/26 Python
pyqt5 禁止窗口最大化和禁止窗口拉伸的方法
2019/06/18 Python
PyQt5实现简单的计算器
2020/05/30 Python
python thrift 实现 单端口多服务的过程
2020/06/08 Python
Django如何实现密码错误报错提醒
2020/09/04 Python
selenium携带cookies模拟登陆CSDN的实现
2021/01/19 Python
公积金转移接收函
2014/01/11 职场文书
采购人员的个人自我评价
2014/01/16 职场文书
求职意向书
2014/07/29 职场文书
辞职信模板(中英文版)
2015/02/27 职场文书
司机个人年终总结
2015/03/03 职场文书
共青团员自我评价
2015/03/10 职场文书
2015年电话客服工作总结
2015/05/18 职场文书
中小学生安全教育观后感
2015/06/17 职场文书
致三级跳运动员加油稿
2015/07/21 职场文书
3招让你摆脱即兴讲话冷场尴尬
2019/08/08 职场文书
ConstraintValidator类如何实现自定义注解校验前端传参
2021/06/18 Java/Android
浅谈Redis的事件驱动模型
2022/05/30 Redis