详解VUE单页应用骨架屏方案


Posted in Javascript onJanuary 17, 2019

什么是骨架屏?

简单的说,骨架屏就是在页面未渲染完成的时候,先用一些简单的图形大致勾勒出页面的基本轮廓,给用户造成页面正在加载的错觉,待页面渲染完成之后再用页面替换掉骨架屏,从而减少页面白屏的时间,给用户带来更好的体验。

详解VUE单页应用骨架屏方案

分析VUE渲染过程

使用vue-cli3.0创建项目: vue create project

详解VUE单页应用骨架屏方案 

详解VUE单页应用骨架屏方案 

详解VUE单页应用骨架屏方案 

详解VUE单页应用骨架屏方案 

详解VUE单页应用骨架屏方案 

详解VUE单页应用骨架屏方案 


详解VUE单页应用骨架屏方案 

详解VUE单页应用骨架屏方案

在生成的项目文件夹下的public文件夹下的index.html文件代码如下:

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width,initial-scale=1.0">
 <link rel="icon" href="<%= BASE_URL %>favicon.ico" rel="external nofollow" >
 <title>project</title>
 </head>
 <body>
 <noscript>
  <strong>We're sorry but project doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
 </noscript>
 <div id="app"></div>
 <!-- built files will be auto injected -->
 </body>
</html>

可以看到,DOM里面有一个div#app,当js被执行完成之后,此div#app会被整个替换掉,因此,如何在Vue页面实现骨架屏,已经有了一个很清晰的思路——在div#app内直接插入骨架屏相关内容即可。

实现方案

手动在div#app里面写入骨架屏内容是不科学的,因此需要一个扩展性强且自动化的易维护方案。既然是在Vue项目里,所以所谓的骨架屏也是一个.vue文件,它能够在构建时由工具自动注入到div#app里面。 首先,我们在/src目录下新建一个Skeleton.vue文件,其内容如下:

<template>
 <div class="skeleton page">
 <div class="skeleton-nav"></div>
 <div class="skeleton-swiper">
  <div class="skeleton-swiper-item item-one"></div>
  <div class="skeleton-swiper-item item-two"></div>
 </div>
 </div>
</template>

<style>
html,body,div{
 margin:0;
 padding:0;
}
.skeleton {
 height: 100%;
 overflow: hidden;
 box-sizing: border-box;
 background: #fff;
}
.skeleton-nav {
 height: 54px;
 background: #eee;
 margin-bottom: 20px;
}
.skeleton-swiper {
 min-height:600px;
 max-width:1280px;
 margin:0 auto;
}
.skeleton-swiper-item{
 min-height: 600px;
 height:100%;
 background:#eee;
 border-radius:5px;
}
.item-one{
 width:20%;
 float:left;
}
.item-two{
 width:78%;
 float:right;
}
</style>

接下来,在/src目录再新建一个skeleton.entry.js入口文件:

import Vue from 'vue';
import Skeleton from './Skeleton.vue';

export default new Vue({
 components: {
  Skeleton,
 },
 template: '<skeleton />',
});

在完成了骨架屏的准备之后,我们需要一个关键插件vue-server-renderer。该插件本用于服务端渲染,但是在这里,我们主要利用它能够把.vue文件处理成html和css字符串的功能,来完成骨架屏的注入。

骨架屏注入

首先在public文件夹下新建一个template.html文件,并且其代码和index.html文件代码相同,但是需要在div#app中添加 <!--vue-ssr-outlet--> 占位符:

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width,initial-scale=1.0">
 <link rel="icon" href="./favicon.ico" rel="external nofollow" >
 <title>医生工作台</title>
 </head>
 <body>
 <noscript>
  <strong>We're sorry but yz_doctors doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
 </noscript>
 <div id="app"><!--vue-ssr-outlet--></div>
 <!-- built files will be auto injected -->
 </body>
</html>

然后,我们还需要在根目录新建一个webpack.skeleton.conf.js文件,以专门用来进行骨架屏的构建。

const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
 target: 'node',
 entry: {
  skeleton: './src/skeleton.entry.js',
 },
 output: {
  path: path.resolve(__dirname, './dist'),
  publicPath: '/dist/',
  filename: '[name].js',
  libraryTarget: 'commonjs2',
 },
 module: {
  rules: [
   {
    test: /\.css$/,
    use: [
     'vue-style-loader',
     'css-loader',
    ],
   },
   {
    test: /\.vue$/,
    loader: 'vue-loader',
   },
  ],
 },
 externals: nodeExternals({
  whitelist: /\.css$/,
 }),
 resolve: {
  alias: {
   'vue$': 'vue/dist/vue.esm.js',
  },
  extensions: ['*', '.js', '.vue', '.json'],
 },
 plugins: [
  new VueLoaderPlugin(),
  new VueSSRServerPlugin({
   filename: 'skeleton.json',
  }),
 ],
};

可以看到,该配置文件和普通的配置文件基本完全一致,主要的区别在于其target: 'node',配置了externals,以及在plugins里面加入了VueSSRServerPlugin。在VueSSRServerPlugin中,指定了其输出的json文件名。然后通过运行下列指令,在/dist目录下生成一个skeleton.json文件: webpack --config ./webpack.skeleton.conf.js 接下来,在根目录下新建一个skeleton.js,该文件即将被用于往index.html内插入骨架屏:

const fs = require('fs');
const { resolve } = require('path');
const htmlMinifier = require('html-minifier');
const createBundleRenderer = require('vue-server-renderer').createBundleRenderer;

// 先把vue的模板文件index.html置换成标准的模板,防止骨架屏污染
let tempData = fs.readFileSync(resolve(__dirname, './public/template.html'), 'utf-8');
fs.writeFileSync('./public/index.html', tempData, 'utf-8');
console.log('模板注入完成');
// 读取`skeleton.json`,以`index.html`为模板写入内容
const renderer = createBundleRenderer(resolve(__dirname, './dist/skeleton.json'), {
 template: fs.readFileSync(resolve(__dirname, './public/index.html'), 'utf-8'),
});

// 把上一步模板完成的内容写入(替换)`index.html`
renderer.renderToString({}, (err, html) => {
 if (err) {
  console.log(err);
  return;
 }
 html = htmlMinifier.minify(html, {
  collapseWhitespace: true,
  minifyCSS: true,
 });
 fs.writeFileSync('./public/index.html', html, 'utf-8');
});
console.log('骨架屏注入完成');

接下来,只要运行 node skeleton.js ,就可以完成骨架屏的注入。 为了在 npm run serve 的时候自动完成骨架屏的注入,避免运行多次命令,需要在 package.json 中增加一条命令 "preserve": "webpack --config ./webpack.skeleton.conf.js && node skeleton.js" ,放在 "serve" 命令之前。

总结

新建template.html文件的目的是为了保存模板文件的干净,因为每次完成骨架屏的注入后index.html文件中的 <!--vue-ssr-outlet--> 占位符已经被骨架屏代码所替换,再次修改骨架屏后就无法完成骨架屏的注入啦,所以在注入骨架屏时先用template.html文件中的内容替换index.html文件,避免了每次修改骨架屏时还要手动修改index.html文件,运行一条命令实现骨架屏的自动注入。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 继承详解(三)
Jul 13 Javascript
对 lightbox JS 图片控件进行了一下改造, 使其他支持复杂的图片说明
Mar 20 Javascript
js调试系列 控制台命令行API使用方法
Jun 18 Javascript
JQuery中extend的用法实例分析
Feb 08 Javascript
JavaScript中的Math.LOG2E属性使用详解
Jun 14 Javascript
jQuery插件支持同一页面被多次调用
Feb 14 Javascript
微信小程序 动态绑定数据及动态事件处理
Mar 14 Javascript
JavaScript表单验证实现代码
May 22 Javascript
JavaScript 中的 this 简单规则
Sep 19 Javascript
使用vue-cli webpack 快速搭建项目的代码
Nov 21 Javascript
使用jquery模拟a标签的click事件无法实现跳转的解决
Dec 04 jQuery
解决Echarts 显示隐藏后宽度高度变小的问题
Jul 19 Javascript
Jquery获取radio选中值实例总结
Jan 17 #jQuery
js中call()和apply()改变指针问题的讲解
Jan 17 #Javascript
js中怎么判断两个字符串相等的实例
Jan 17 #Javascript
js中null与空字符串&quot;&quot;的区别讲解
Jan 17 #Javascript
vue中$nextTick的用法讲解
Jan 17 #Javascript
vue项目打包之后背景样式丢失的解决方案
Jan 17 #Javascript
js中Array对象的常用遍历方法详解
Jan 17 #Javascript
You might like
php5.2.0内存管理改进
2007/01/22 PHP
thinkphp中memcache的用法实例
2014/11/29 PHP
php静态文件生成类实例分析
2015/01/03 PHP
php获取Google机器人访问足迹的方法
2015/04/15 PHP
用jscript实现新建和保存一个word文档
2007/06/15 Javascript
jquery实现个人中心导航菜单效果和美观都非常不错
2014/09/02 Javascript
jquery制作 随机弹跳的小球特效
2015/02/01 Javascript
JavaScript中使用Math.floor()方法对数字取整
2015/06/15 Javascript
AngularJS 模型详细介绍及实例代码
2016/07/27 Javascript
原生JS实现-星级评分系统的简单实例
2016/08/21 Javascript
预防网页挂马的方法总结
2016/11/03 Javascript
动态加载css方法实现和深入解析
2017/01/18 Javascript
Javascript中for循环语句的几种写法总结对比
2017/01/23 Javascript
基于JS实现翻书效果的页面切换样式
2017/02/16 Javascript
javascript简写常用的12个技巧(可以大大减少你的js代码量)
2020/03/28 Javascript
关于react-router/react-router-dom v4 history不能访问问题的解决
2018/01/08 Javascript
angularjs 页面自适应高度的方法
2018/01/17 Javascript
angular写一个列表的选择全选交互组件的示例
2018/01/22 Javascript
微信小程序利用canvas 绘制幸运大转盘功能
2018/07/06 Javascript
JS实现指定区域的全屏显示功能示例
2019/04/25 Javascript
Vue的编码技巧与规范使用详解
2019/08/28 Javascript
[49:27]LGD vs OG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
[58:11]守擂赛第二周擂主赛 DeMonsTer vs Leopard
2020/04/28 DOTA
python在控制台输出进度条的方法
2015/06/20 Python
python验证码识别教程之利用滴水算法分割图片
2018/06/05 Python
Python画图实现同一结点多个柱状图的示例
2019/07/07 Python
Python实现实时数据采集新型冠状病毒数据实例
2020/02/04 Python
HTML5之SVG 2D入门8—文档结构及相关元素总结
2013/01/30 HTML / CSS
说一下Linux下有关用户和组管理的命令
2014/08/18 面试题
Java如何读取CLOB字段
2013/10/10 面试题
建筑专业自荐信范文
2014/01/05 职场文书
外国人聘用意向书
2014/04/01 职场文书
人民调解员培训方案
2014/06/05 职场文书
农村优秀教师事迹材料
2014/08/27 职场文书
2019年预备党员的思想汇报:加深对党的认知
2019/09/25 职场文书
游戏《铁拳》动画化!2022年年内播出
2022/03/21 日漫