关于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;和e.preventDefault();的区别
Jul 11 Javascript
IE中jquery.form中ajax提交没反应解决方法分享
Sep 11 Javascript
js日期范围初始化得到前一个月日期的方法
May 05 Javascript
jQuery Mobile弹出窗、弹出层知识汇总
Jan 05 Javascript
JS闭包与延迟求值用法示例
Dec 22 Javascript
JavaScript函数式编程(Functional Programming)声明式与命令式实例分析
May 21 Javascript
Vue侦测相关api的实现方法
May 22 Javascript
基于JS判断对象是否是数组
Jan 10 Javascript
js实现点赞效果
Mar 16 Javascript
JS轮播图的实现方法2
Aug 25 Javascript
vue项目中微信登录的实现操作
Sep 08 Javascript
解决Vue+SpringBoot+Shiro跨域问题
Jun 09 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+curl 发送图片处理代码分享
2015/07/09 PHP
php基于session实现数据库交互的类实例
2015/08/03 PHP
centos 7.2下搭建LNMP环境教程
2016/11/20 PHP
PHP如何开启Opcache功能提升程序处理效率
2020/04/27 PHP
JavaScript入门教程(3) js面向对象
2009/01/31 Javascript
Dom 是什么的详细说明
2010/10/25 Javascript
js获取元素相对窗口位置的实现代码
2014/09/28 Javascript
原生javascript实现DIV拖拽并计算重复面积
2015/01/02 Javascript
JS模仿编辑器实时改变文本框宽度和高度大小的方法
2015/08/17 Javascript
阿里巴巴技术文章分享 Javascript继承机制的实现
2016/01/14 Javascript
JavaScript实现获取远程的html到当前页面中
2017/03/26 Javascript
node+express+ejs使用模版引擎做的一个示例demo
2017/09/18 Javascript
React如何利用相对于根目录进行引用组件详解
2017/10/09 Javascript
nodejs简单读写excel内容的方法示例
2018/03/16 NodeJs
Vue 指令实现按钮级别权限管理功能
2019/04/23 Javascript
jQuery实现高级检索功能
2019/05/28 jQuery
详解如何在Vue项目中发送jsonp请求
2019/10/25 Javascript
python mysqldb连接数据库
2009/03/16 Python
ssh批量登录并执行命令的python实现代码
2012/05/25 Python
django基础之数据库操作方法(详解)
2017/05/24 Python
pip命令无法使用的解决方法
2018/06/12 Python
selenium+python自动化测试之使用webdriver操作浏览器的方法
2019/01/23 Python
python 使用socket传输图片视频等文件的实现方式
2019/08/07 Python
Django项目中实现使用qq第三方登录功能
2019/08/13 Python
基于Python的微信机器人开发 微信登录和获取好友列表实现解析
2019/08/21 Python
浅析PEP572: 海象运算符
2019/10/15 Python
Python ellipsis 的用法详解
2020/11/20 Python
html5 Canvas绘制线条 closePath()实例代码
2012/05/10 HTML / CSS
海飞丝的广告词
2014/03/20 职场文书
ktv好的活动方案
2014/08/15 职场文书
房屋过户委托书范本
2014/10/07 职场文书
交通事故赔偿协议书
2014/10/16 职场文书
2015年幼儿园班务工作总结
2015/05/12 职场文书
庭外和解协议书
2016/03/23 职场文书
tensorboard 可视化之localhost:6006不显示的解决方案
2021/05/22 Python
Python OpenCV实现图形检测示例详解
2022/04/08 Python