Vue页面骨架屏(一)

在开发webapp的时候总是会受到首屏加载时间过长的影响,主流的解决方法是在载入完成之前显示loading图效果,而一些大公司会配置一套服务端渲染的架构来解决这个问题。考虑到ssr所要解决的一系列问题,越来越多的APP采用了“骨架屏”的方式去提升用户体验。

一、分析Vue页面的内容加载过程

vue项目中的入口index.html只有简单的内容:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>Document</title>
</head>
<body><div id="root">        </div><script type="text/javascript" src="bundle.js"></script></body>
</body>
</html>

当js执行完之后,会用vue渲染成的dom将div#root完全替换掉。
我们在div#root中加入模拟骨架屏,在Chrome开发者工具调整网速:

<div id="root">这里是骨架屏
</div>

 

由此可知,将骨架屏内容直接插入div#root中即可实现骨架屏。

二、使用vue-server-renderer来实现骨架屏

我们需要骨架屏也是一个单独的.vue文件,因此我们需要用到vue-server-renderer。对vue服务端渲染有所了解的同学一定知道,这个插件能够将vue项目在node端打包成一个bundle,然后由bundle生成对应的html。
首先是生成项目:

.
├── build
│   ├── webpack.config.client.js
│   └── webpack.config.server.js
├── src
│   └── views
│        ├── index
│        │   └── index.vue
│        ├── skeleton
│        │   └── skeleton.vue
│        ├── app.vue
│        ├── index.js
│        └── skeleton-entry.js
├── index.html
└── skeleton.js
└── package.json

vue的服务端渲染一般会用vue-server-renderer将整个项目在node端打包成一份bundle,而这里我们只要一份有骨架屏的html,所以会有一个单独的骨架屏入口文件skeleton-entry.js,一个骨架屏打包webpack配置webpack.config.server.js,而skeleton.js作用是将webpack打包出来的bundle写入到index.html中。

//skeleton-entry.js
import Vue from 'vue'
import Skeleton from './views/skeleton/skeleton.vue'export default new Vue({components: {Skeleton},template: '<skeleton />'
})
//webpack.config.server.js
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')module.exports = {mode: process.env.NODE_ENV,target: 'node',entry: path.join(__dirname, '../src/skeleton-entry.js'),output: {path: path.join(__dirname, '../server-dist'),filename: 'server.bundle.js',libraryTarget: 'commonjs2'},module: {rules: [{test: /\.vue$/,loader: 'vue-loader'},{test: /\.css$/,use: ['vue-style-loader','css-loader']}    ]},externals: Object.keys(require('../package.json').dependencies),resolve: {alias: {'vue$': 'vue/dist/vue.esm.js'}},plugins: [new VueLoaderPlugin(),new VueSSRServerPlugin({filename: 'skeleton.json'})]
}

其中骨架屏的webpack配置因为是node端,所以需要target: 'node' libraryTarget: 'commonjs2'。在VueSSRServerPlugin中,指定了其输出的json文件名。当执行webpack会在/server-dist目录下生成一个skeleton.json文件,这个文件记载了骨架屏的内容和样式,会提供给vue-server-renderer使用。

//skeleton.js
const fs = require('fs')
const path = require('path')const createBundleRenderer = require('vue-server-renderer').createBundleRenderer// 读取`skeleton.json`,以`index.html`为模板写入内容
const renderer = createBundleRenderer(path.join(__dirname, './server-dist/skeleton.json'), {template: fs.readFileSync(path.join(__dirname, './index.html'), 'utf-8')
})// 把上一步模板完成的内容写入(替换)`index.html`
renderer.renderToString({}, (err, html) => {fs.writeFileSync('index.html', html, 'utf-8')
})
  注意,作为模板的html文件,需要在被写入内容的位置添加<!--vue-ssr-outlet-->占位符,本例子在div#root里写入:<div id="root"><!--vue-ssr-outlet--></div>

最后执行node skeleton就能实现vue的骨架屏。
最终的index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><title>Document</title>
<style data-vue-ssr-id="a7049cb4:0">
.skeleton[data-v-61761ff8] {position: relative;height: 100%;overflow: hidden;padding: 15px;box-sizing: border-box;background: #fff;
}
.skeleton-nav[data-v-61761ff8] {height: 45px;background: #eee;margin-bottom: 15px;
}
.skeleton-swiper[data-v-61761ff8] {height: 160px;background: #eee;margin-bottom: 15px;
}
.skeleton-tabs[data-v-61761ff8] {list-style: none;padding: 0;margin: 0 -15px;display: flex;flex-wrap: wrap;
}
.skeleton-tabs-item[data-v-61761ff8] {width: 25%;height: 55px;box-sizing: border-box;text-align: center;margin-bottom: 15px;
}
.skeleton-tabs-item span[data-v-61761ff8] {display: inline-block;width: 55px;height: 55px;border-radius: 55px;background: #eee;
}
.skeleton-banner[data-v-61761ff8] {height: 60px;background: #eee;margin-bottom: 15px;
}
.skeleton-productions[data-v-61761ff8] {height: 20px;margin-bottom: 15px;background: #eee;
}
</style></head>
<body><div id="root"><div data-server-rendered="true" class="skeleton page" data-v-61761ff8><div class="skeleton-nav" data-v-61761ff8></div> <div class="skeleton-swiper" data-v-61761ff8></div> <ul class="skeleton-tabs" data-v-61761ff8><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li></ul> <div class="skeleton-banner" data-v-61761ff8></div> <div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div></div></div>
</body>
</html>

尾声

文章开头小米商城手机页面就是用的这样的方法,不同的是它的骨架屏是一个base64的图片。

更多关于vue-server-renderer内容请戳vue-ssr
相关demo
转载自https://segmentfault.com/a/1190000014963269

转载于:https://www.cnblogs.com/hongsusu/p/9439152.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql中数据定义和数据控制语言_MySQL 数据定义语言(DDL)

SQL 包含以下 4部分&#xff1a;1数据定义语言(DDL)&#xff1a;DROP、CREATE、ALTER 等语句。2数据操作语言(DML)&#xff1a;INSERT(插入)、UPDATE(修改)、DELETE(删除)语句。3数据查询语言(DQL)&#xff1a;SELECT 语句。4 数据控制语言(DCL): GRANT、REVOKE、COMMIT、ROLLB…

Page directive: illegal to have multiple occurrences of contentType with different values

org.apache.jasper.JasperException: /commons/meta.jsp(1,1) PWC5988: Page directive: illegal to have multiple occurrences of contentType with different values (old: text/html; charsetutf-8, new: text/html;charsetUTF-8) 问题&#xff1a;contentType 有多个值 解…

我们处理了10亿个Java记录的错误-这是导致97%的错误的原因

97&#xff05;的记录错误是由10个唯一错误引起的 在2016年&#xff0c;一件事已经30年没有改变了。 开发和运营团队仍依靠日志文件对应用程序问题进行故障排除。 由于某些未知原因&#xff0c;我们隐式地信任日志文件&#xff0c;因为我们认为事实隐藏在其中。 如果您足够努力…

洛谷 P1939 【模板】矩阵加速(数列)

题目描述 a[1]a[2]a[3]1 a[x]a[x-3]a[x-1] (x>3) 求a数列的第n项对1000000007&#xff08;10^97&#xff09;取余的值。 输入输出格式 输入格式&#xff1a; 第一行一个整数T&#xff0c;表示询问个数。 以下T行&#xff0c;每行一个正整数n。 输出格式&#xff1a; 每行输出…

Java中Map的使用

Map以按键/数值对的形式存储数据&#xff0c;和数组非常相似&#xff0c;在数组中存在的索引&#xff0c;它们本身也是对象。 Map的接口 Map&#xff0d;&#xff0d;&#xff0d;实现Map Map.Entry&#xff0d;&#xff0d;Map的内部类&#xff0c;描述Map中的按键/数值…

java获取10位随机数_Java基础:JAVA中BitSet使用详解

适用场景&#xff1a;整数&#xff0c;无重复&#xff1b;Bitset 基础Bitset&#xff0c;也就是位图&#xff0c;由于可以用非常紧凑的格式来表示给定范围的连续数据而经常出现在各种算法设计中。上面的图来自c库中bitset的一张图。基本原理是&#xff0c;用1位来表示一个数据是…

使用shiro框架,注销问题的解决

在使用shiro框架的时候&#xff0c;有时候会因为登录问题找不到注销的controller。所以会报404的错误&#xff0c;下面是解决办法&#xff1a; 1.首先写一个类SystemLogoutFilter继承LogoutFilter类&#xff0c;具体代码如下&#xff0c;注意要贴Service标签&#xff1a; impor…

介绍“又一个” Cloud Foundry Gradle插件

在与两个同事&#xff08;感谢Mark Alston &#xff0c; Dave Malone &#xff01;&#xff09;一起使用自动Jenkins管道部署Cloud Foundry应用程序的过程中&#xff0c;我决定尝试编写Gradle插件来执行一些通常需要完成的任务使用命令行Cloud Foundry Client完成 。 介绍带有…

卓金武——从数学建模到MATLAB

卓金武——从数学建模到MATLAB 2013-9-4 09:48| 发布者: ilovematlab| 查看: 9647| 评论: 40 摘要: 人物简介——卓金武&#xff08;Steven&#xff09;&#xff0c;MathWorks公司中国区应用工程师。在科学计算、定量优化、数学建模和数据挖掘领域拥有8年经验。曾3次获全国大学…

[Java开发]打印当前路径到控制台

开发时候&#xff0c;遇到了系统找不到配置文件的情况&#xff0c;定位到文件路径的问题之后&#xff0c;选择将当前路径打印到控制台。 File directory new File("");//设定为当前文件夹 try{ System.out.println(directory.getCanonicalPath());//获取标准的路径 …

latex中report目录_LaTeX目录格式控制

章节结构控制章节层次一个文档的最高层章节可以是part&#xff0c;也可以没有part直接是chapter/section。除了part以外&#xff0c;只有在上一层章节存在时&#xff0c;才可以使用下一层章节。章节层次示意图如下&#xff0c;方框代表文档类型&#xff0c;圆圈代表章节层次。对…

诸葛亮给子书

夫君子之行&#xff0c;静以修身&#xff0c;俭以养德&#xff1b;非澹泊无以明志&#xff0c;非宁静无以致远。   夫学须静也&#xff0c;才须学也&#xff1b;非学无以广才&#xff0c;非志无以成学。   怠慢则不能励精&#xff0c;险躁则不能冶性。   年与时驰&…

conda 安装mysql_centos7安装mysql

一、安装YUM Repo1、由于CentOS 的yum源中没有mysql&#xff0c;需要到mysql的官网下载yum repo配置文件。官方网址&#xff1a;https://dev.mysql.com/downloads/repo/yum/下载命令&#xff1a;wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm2、…

智能批处理

我们都有多少次听说“分批处理”会增加延迟&#xff1f; 作为对低延迟系统充满热情的人&#xff0c;这让我感到惊讶。 以我的经验&#xff0c;正确完成批处理不仅可以提高吞吐量&#xff0c;还可以减少平均延迟并保持一致。 那么&#xff0c;批处理如何神奇地减少延迟呢&#x…

cogs 76. [NOIP2007] 统计数字

【问题描述】 某次科研调查时得到了n个自然数&#xff0c;每个数均不超过1500000000(1.5*10^9)。已知不相同的数不超过10000个&#xff0c;现在需要统计这些自然数各自出现的次数&#xff0c;并按照自然数从小到大的顺序输出统计结果。 【输入格式】 输入文件pcount.in包…

javaweb之Filter详解

一、概念&#xff1a;Filter也称之为过滤器&#xff0c;它是Servlet技术中比较激动人心的技术&#xff0c;WEB开发人员通过Filter技术&#xff0c;对web服务器管理的所有web资源&#xff1a;例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截&#xff0c;从而实现一些…

pause容器作用_Kubernetes学习之pause容器

根据代码看到,pause容器运行着一个非常简单的进程,它不执行任何功能,一启动就永远把自己阻塞住了,它的作用就是扮演PID1的角色,并在子进程称为"孤儿进程"的时候,通过调用wait()收割这个子进程,这样就不用担心我们的Pod的PID namespace里会堆满僵尸进程了,这也是为什么…

Spring Integration –轮询文件的创建和修改

1引言 文件支持是Spring Integration与外部系统通信的另一个端点。 在这种情况下&#xff0c;它提供了几个组件来读取&#xff0c;写入和转换文件。 在这篇文章中&#xff0c;我们将编写一个监视目录的应用程序&#xff0c;以便读取其中的所有文件。 具体而言&#xff0c;它执行…

为什么需要消息队列MQ

主要原因&#xff1a;是在高并发情况下&#xff0c;由于来不及同步处理&#xff0c;请求往往会发生堵塞&#xff0c;比如诸多的insert、update之类的请求同时到达mysql&#xff0c;直接导致无数的行锁表锁&#xff0c;甚至最后请求会堆积很多&#xff0c;从而触发大量的too man…

数据库连接池的使用

关于数据库连接池的使用&#xff0c;首先我们要明白我们为什么要用它&#xff0c;对应普通的数据库连接操作&#xff0c;通常会涉及到以下一些操作是比较耗时的&#xff1a; 网络通讯&#xff0c;涉及到网络延时及协议通讯身份验证&#xff0c;涉及安全性检查连接合法性检查&a…