详解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中最常用的继承模式 组合继承
Aug 12 Javascript
jQuery :nth-child前有无空格的区别分析
Jul 11 Javascript
jquery设置text的值示例(设置文本框 DIV 表单值)
Jan 06 Javascript
Node.js与PHP、Python的字符处理性能对比
Jul 06 Javascript
JavaScript实现模仿桌面窗口的方法
Jul 18 Javascript
JS实现弹出居中的模式窗口示例
Jun 20 Javascript
node.js 和HTML5开发本地桌面应用程序
Dec 13 Javascript
Angular 1.x个人使用的经验小结
Jul 19 Javascript
jQuery读取本地的json文件(实例讲解)
Oct 31 jQuery
Bootstrap Table 搜索框和查询功能
Nov 30 Javascript
vue-music 使用better-scroll遇到轮播图不能自动轮播问题
Dec 03 Javascript
Vuex的实战使用详解
Oct 31 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
WINDOWS服务器安装多套PHP的另类解决方案
2006/10/09 PHP
JavaScript控制各种浏览器全屏模式的方法、属性和事件介绍
2014/04/03 Javascript
javascript获取四位数字或者字母的随机数
2015/01/09 Javascript
每天一篇javascript学习小结(String对象)
2015/11/18 Javascript
jQuery树形插件jquery.simpleTree.js用法分析
2016/09/05 Javascript
node.js实现的装饰者模式示例
2017/09/06 Javascript
js 发布订阅模式的实例讲解
2017/09/10 Javascript
vue elementUI table 自定义表头和行合并的实例代码
2019/05/22 Javascript
如何在微信小程序中存setStorage
2019/12/13 Javascript
小程序跳转到的H5页面再跳转回跳小程序的方法
2020/03/06 Javascript
JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】
2020/05/12 Javascript
在vue中实现禁止回退上一步,路由不存历史记录
2020/07/22 Javascript
[02:40]DOTA2英雄基础教程 炼金术士
2013/12/23 DOTA
[01:04:49]KG vs LGD 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
python3 pandas 读取MySQL数据和插入的实例
2018/04/20 Python
Python读取指定日期邮件的实例
2019/02/01 Python
python flask 如何修改默认端口号的方法步骤
2019/07/12 Python
使用WingPro 7 设置Python路径的方法
2019/07/24 Python
Python csv模块使用方法代码实例
2019/08/29 Python
python实现秒杀商品的微信自动提醒功能(代码详解)
2020/04/27 Python
Python xlrd模块导入过程及常用操作
2020/06/10 Python
Python selenium模块实现定位过程解析
2020/07/09 Python
Python3以GitHub为例来实现模拟登录和爬取的实例讲解
2020/07/30 Python
python uuid生成唯一id或str的最简单案例
2021/01/13 Python
使paramiko库执行命令时在给定的时间强制退出功能的实现
2021/03/03 Python
电大物流学生的自我评价
2013/10/25 职场文书
药学专业大专生的自我评价
2013/12/12 职场文书
十岁生日同学答谢词
2014/01/19 职场文书
中学生国旗下讲话稿
2014/04/26 职场文书
孝敬父母的活动方案
2014/08/28 职场文书
2014年实习生工作总结
2014/11/27 职场文书
2015年校长新年寄语
2014/12/08 职场文书
2015元旦联欢晚会结束语
2014/12/14 职场文书
和谐拯救危机观后感
2015/06/15 职场文书
年终奖金发放管理制度,中小企业适用,拿去救急吧!
2019/07/12 职场文书
Redis全局ID生成器的实现
2022/06/05 Redis