详解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中的var_dump函数实现代码
Sep 07 Javascript
使用jQuery模板来展现json数据的代码
Oct 22 Javascript
基于jQuery的input输入框下拉提示层(自动邮箱后缀名)
Jun 14 Javascript
js 在定义的时候立即执行的函数表达式(function)写法
Jan 16 Javascript
详解基于angular路由的requireJs按需加载js
Jan 20 Javascript
Vue入门之数据绑定(小结)
Jan 08 Javascript
Vue2.0 事件的广播与接收(观察者模式)
Mar 14 Javascript
js 判断当前时间是否处于某个一个时间段内
Sep 19 Javascript
小程序中使用css var变量(使js可以动态设置css样式属性)
Mar 31 Javascript
JS原形与原型链深入详解
May 09 Javascript
分享8个JavaScript库可更好地处理本地存储
Oct 12 Javascript
一篇文章告诉你如何实现Vue前端分页和后端分页
Feb 18 Vue.js
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
Thinkphp中的curd应用实用要点
2015/01/04 PHP
浅谈PHP实现大流量下抢购方案
2017/12/15 PHP
基于php伪静态的实现方法解析
2020/07/31 PHP
form中限制文本字节数js代码
2007/06/10 Javascript
js 未结束的字符串常量错误解决方法
2010/06/13 Javascript
js监听输入框值的即时变化onpropertychange、oninput
2011/07/13 Javascript
Js判断CSS文件加载完毕的具体实现
2014/01/17 Javascript
JS的get和set使用示例
2014/02/20 Javascript
JavaScript 面向对象与原型
2015/04/10 Javascript
JS实现DIV容器赋值的方法
2015/12/14 Javascript
javascript学习指南之回调问题
2016/04/23 Javascript
利用JavaScript实现栈的数据结构示例代码
2017/08/02 Javascript
JavaScript实现的仿新浪微博原生态输入字数即时检查功能【兼容IE6】
2017/09/26 Javascript
Vue.js简易安装和快速入门(第二课)
2017/10/17 Javascript
ES6下子组件调用父组件的方法(推荐)
2018/02/23 Javascript
浅谈Javascript常用正则表达式应用
2019/03/08 Javascript
用原生 JS 实现 innerHTML 功能实例详解
2019/04/03 Javascript
vue element-ui之怎么封装一个自己的组件的详解
2019/05/20 Javascript
详解vuejs中执行npm run dev出现页面cannot GET/问题
2020/04/26 Javascript
[01:12:27]EG vs Secret 2018国际邀请赛淘汰赛BO3 第二场 8.22
2018/08/23 DOTA
Python程序设计入门(5)类的使用简介
2014/06/16 Python
Python中正则表达式详解
2017/05/17 Python
python实现给微信指定好友定时发送消息
2019/04/29 Python
DataFrame 数据合并实现(merge,join,concat)
2020/06/14 Python
python pandas dataframe 去重函数的具体使用
2020/07/20 Python
python开发入门——列表生成式
2020/09/03 Python
网站域名和主机:Domain.com
2019/04/01 全球购物
PHP经典面试题
2016/09/03 面试题
网络公司美工设计工作个人的自我评价
2013/11/03 职场文书
语文教育专业求职信
2014/06/28 职场文书
演讲比赛的活动方案
2014/08/28 职场文书
工作作风建设心得体会
2014/10/22 职场文书
免职通知
2015/04/23 职场文书
《我要的是葫芦》教学反思
2016/02/18 职场文书
vue首次渲染全过程
2021/04/21 Vue.js
微信小程序调用python模型
2022/04/21 Python