关于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 相关文章推荐
node.js中的http.response.addTrailers方法使用说明
Dec 14 Javascript
简单谈谈javascript代码复用模式
Jan 28 Javascript
jQuery判断元素上是否绑定了指定事件的方法
Mar 17 Javascript
懒加载实现的分页&amp;&amp;网站footer自适应
Dec 21 Javascript
微信小程序中页面FOR循环和嵌套循环
Jun 21 Javascript
微信小程序中如何使用flyio封装网络请求
Jul 03 Javascript
js如何获取访问IP、地区、当前操作浏览器
Jul 23 Javascript
微信小程序中插入激励视频广告并获取收益(实例代码)
Dec 06 Javascript
小程序跨页面交互的作用与方法详解
Jan 07 Javascript
Nuxt页面级缓存的实现
Mar 09 Javascript
keep-alive不能缓存多层级路由菜单问题解决
Mar 10 Javascript
Handtrack.js库实现实时监测手部运动(推荐)
Feb 08 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
常用的php对象类型判断
2008/08/27 PHP
PHP define函数的使用说明
2008/08/27 PHP
PHP教程之PHP中shell脚本的使用方法分享
2012/02/23 PHP
php标签云的实现代码
2012/10/10 PHP
php版微信公众平台接口参数调试实现判断用户行为的方法
2016/09/23 PHP
PHP中Laravel 关联查询返回错误id的解决方法
2017/04/01 PHP
jQuery调用RESTful WCF示例代码(GET方法/POST方法)
2014/01/26 Javascript
jQuery学习笔记之 Ajax操作篇(一) - 数据加载
2014/06/23 Javascript
js实现两点之间画线的方法
2015/05/12 Javascript
浅谈JS之tagNaem和nodeName
2016/09/13 Javascript
详解Javascript函数声明与递归调用
2016/10/22 Javascript
Bootstrap table使用方法记录
2017/08/23 Javascript
五步轻松实现JavaScript HTML时钟效果
2020/03/25 Javascript
vue2.0.js的多级联动选择器实现方法
2018/02/09 Javascript
详解webpack 打包文件体积过大解决方案(code splitting)
2018/04/10 Javascript
微信小程序云开发 生成带参小程序码流程
2019/05/18 Javascript
Jquery动态列功能完整实例
2019/08/30 jQuery
[01:12]DOTA2次级职业联赛 - Newbee.Y 战队宣传片
2014/12/01 DOTA
Python中static相关知识小结
2018/01/02 Python
python中从str中提取元素到list以及将list转换为str的方法
2018/06/26 Python
python使用pymongo操作mongo的完整步骤
2019/04/13 Python
django数据关系一对多、多对多模型、自关联的建立
2019/07/24 Python
学习和使用python的13个理由
2019/07/30 Python
基于python实现数组格式参数加密计算
2020/04/21 Python
瑞典领先的汽车零部件网上零售商:bildelaronline24.se
2017/01/12 全球购物
介绍一下EJB的体系结构
2012/08/01 面试题
旅游管理专业学生求职信
2013/09/28 职场文书
运动会邀请函范文
2014/01/31 职场文书
祖国在我心中演讲稿300字
2014/05/04 职场文书
终止劳动合同证明书样本
2014/11/19 职场文书
2014年个人思想工作总结
2014/11/27 职场文书
《植树问题》教学反思
2016/03/03 职场文书
入党转正申请自我鉴定
2019/06/25 职场文书
七年级话题作文之执着
2019/11/19 职场文书
详解MySQL事务的隔离级别与MVCC
2021/04/22 MySQL
python入门之算法学习
2021/04/22 Python