详解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 相关文章推荐
jquery下组织javascript代码(js函数化)
Aug 25 Javascript
js的hasownproperty使用示例
Mar 02 Javascript
Javascript WebSocket使用实例介绍(简明入门教程)
Apr 16 Javascript
Javascript基础教程之if条件语句
Jan 18 Javascript
js数组常用操作方法小结(增加,删除,合并,分割等)
Aug 02 Javascript
bootstrap基础知识学习笔记
Nov 02 Javascript
BootStrap 图标icon符号图标glyphicons不正常显示的快速解决办法
Dec 08 Javascript
JS定时检测任务任务完成后执行下一步的解决办法
Dec 22 Javascript
js+html5实现页面可刷新的倒计时效果
Jul 15 Javascript
vue中使用cropperjs的方法
Mar 01 Javascript
使用Sonarqube扫描Javascript代码的示例
Dec 26 Javascript
详解基于Vue的支持数据双向绑定的select组件
Sep 02 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
Yii实现多按钮保存与提交的方法
2014/12/03 PHP
PHP实现仿Google分页效果的分页函数
2015/07/29 PHP
PHP入门教程之会话控制技巧(cookie与session)
2016/09/11 PHP
Yii框架引用插件和ckeditor中body与P标签去除的方法
2017/01/19 PHP
jquery 1.3.2 IE8中的一点点的小问题解决方法
2009/07/10 Javascript
IE 上下滚动展示模仿Marquee机制
2009/12/20 Javascript
JavaScript 继承机制的实现(待续)
2010/05/18 Javascript
基于Jquery的动态创建DOM元素的代码
2010/12/28 Javascript
javascript 函数及作用域总结介绍
2013/11/12 Javascript
JS+CSS实现DIV层的展开、收缩效果
2016/01/28 Javascript
jQuery插件FusionCharts实现的2D柱状图效果示例【附demo源码下载】
2017/03/06 Javascript
vue 挂载路由到头部导航的方法
2017/11/13 Javascript
mockjs+vue页面直接展示数据的方法
2018/12/19 Javascript
javascript实现固定侧边栏
2021/02/09 Javascript
python错误:AttributeError: 'module' object has no attribute 'setdefaultencoding'问题的解决方法
2014/08/22 Python
python实现基于两张图片生成圆角图标效果的方法
2015/03/26 Python
Python中函数参数设置及使用的学习笔记
2016/05/03 Python
利用python3随机生成中文字符的实现方法
2017/11/24 Python
python中的常量和变量代码详解
2018/07/25 Python
Python数据可视化:箱线图多种库画法
2019/11/06 Python
解决TensorFlow GPU版出现OOM错误的问题
2020/02/03 Python
PyCharm GUI界面开发和exe文件生成的实现
2020/03/04 Python
Python动态导入模块:__import__、importlib、动态导入的使用场景实例分析
2020/03/30 Python
Python在字符串中处理html和xml的方法
2020/07/31 Python
详解Python之Scrapy爬虫教程NBA球员数据存放到Mysql数据库
2021/01/24 Python
英国领先的NHS批准的在线药店:Pharmacy2U
2017/01/06 全球购物
北卡罗来纳州豪华家具和家居装饰店:Carolina Rustica
2018/10/30 全球购物
毕业生就业自荐信
2013/12/04 职场文书
内勤岗位职责
2015/02/10 职场文书
员工拾金不昧表扬稿
2015/05/05 职场文书
2015暑期社会实践调查报告
2015/07/14 职场文书
小学中队委竞选稿
2015/11/20 职场文书
《画家和牧童》教学反思
2016/02/17 职场文书
创业计划书之酒店
2019/08/30 职场文书
Python爬虫进阶之Beautiful Soup库详解
2021/04/29 Python
CSS SandBox应用场景及常见问题
2022/06/25 HTML / CSS