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: %;
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: ;
margin: -15px;
display: flex;
flex-wrap: wrap;
}
.skeleton-tabs-item[data-v-61761ff8] {
width: %;
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
Vue页面骨架屏(一)的更多相关文章
- Vue页面骨架屏(二)
实现思路 参考原文中在构建时使用 Vue 预渲染骨架屏一节介绍的思路,我将骨架屏也看成路由组件,在构建时使用 Vue 预渲染功能,将骨架屏组件的渲染结果 HTML 片段插入 HTML 页面模版的挂载点 ...
- Vue 项目骨架屏注入与实践
作为与用户联系最为密切的前端开发者,用户体验是最值得关注的问题.关于页面loading状态的展示,主流的主要有loading图和进度条两种.除此之外,越来越多的APP采用了“骨架屏”的方式去展示未加载 ...
- vue搭建骨架屏步骤配置
1.什么是骨架屏幕? 在页面加载数据之前,有一段空白时间,要么用loading加载,要么就用骨架屏. 在开发webapp的时候总是会受到首屏加载时间过长的影响,主流的解决方法是在载入完成之前显示loa ...
- Vue单页面骨架屏实践
github 地址: VV-UI/VV-UI 演示地址: vv-ui 文档地址:skeleton 关于骨架屏介绍 骨架屏的作用主要是在网络请求较慢时,提供基础占位,当数据加载完成,恢复数据展示.这样给 ...
- Vue项目骨架屏注入实践
相比于早些年前后端代码紧密耦合.后端工程师还得写前端代码的时代,如今已发展到前后端分离,这种开发方式大大提升了前后端项目的可维护性与开发效率,让前后端工程师关注于自己的主业.然而在带来便利的同时,也带 ...
- 骨架屏(page-skeleton-webpack-plugin)初探
作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/2436173500265335 微信公众 ...
- 【vue】饿了么项目-页面骨架开发
1.页面骨架开发 1.1组件拆分 手机浏览器是把页面放在一个虚拟的“窗口”(viewport)中,通常这个虚拟的“窗口”(viewport)比屏幕宽,这样就不用把每个网页挤到很小的窗口中(这样会破坏没 ...
- vue骨架屏以及seo优化
参考文档 vue骨架屏 https://blog.csdn.net/ly124100427/article/details/81168908 vue seo优化 1.SSR服务器渲染: 2.静态化: ...
- 《前端之路》之 前端图片 类型 & 优化 & 预加载 & 懒加载 & 骨架屏
目录 09: 前端图片 类型 & 优化 & 预加载 & 懒加载 & 骨架屏 09: 前端图片 类型 & 优化 & 预加载 & 懒加载 & ...
随机推荐
- Centos 下php安装配置xdebug扩展
2018年05月02日 19:54:42 杨汉松 阅读数:44 1.下载安装xdebug 获取xdebug wget http://www.xdebug.org/files/xdebug-2.3. ...
- linux自动删除30天前的日志文件
linux应用总结: 自动删除n天前的日志文件: . 使用的命令格式如下: find 对应目录 -mtime +天数 -name "文件名" -exec -rm -rf -name ...
- Lightoj 1054 - Efficient Pseudo Code
题目连接: http://www.lightoj.com/volume_showproblem.php?problem=1054 题目大意: 给出n,m,问n^m的所有因子之和是多少? 解题思路: 补 ...
- poj 3164 Command Network (朱刘算法)
题目链接: http://poj.org/problem?id=3164 题目大意: 有n个点(用坐标表示)各点编号分别为1—>n,m条单向路,问能否存在一个花费价值最小的网络,能使从1点到达任 ...
- 用for循环实现的菱形图案
package com.wh.lingxing; import java.util.Scanner; public class LingXing { public static void main(S ...
- Atcoder B - Boxes 玄学 + 数学
http://agc010.contest.atcoder.jp/tasks/agc010_b 预处理出每两个相邻的数的差值,那么首先知道是一共取了sum / ((1 + n) * n / 2)次,因 ...
- Radis
http://www.redis.cn/ http://try.redis.io/ http://www.redisdoc.com/en/latest/ Redis 命令参考¶ 本文档是 Redis ...
- 在reset css后两个input之间还是出现默认间隔的问题。
<div class="search_box fl"> <input type="text" class="search_text& ...
- WPF学习10:基于MVVM Light 制作图形编辑工具(1)
图形编辑器的功能如下图所示: 除了MVVM Light 框架是一个新东西之外,本文所涉及内容之前的WPF学习0-9基本都有相关介绍. 本节中,将搭建编辑器的界面,搭建MVVM Light 框架的使用环 ...
- 微信小程序组件解读和分析:十三、radio单选项目
radio单选项目组件说明: radio:单选项目. radio-group: 单项选择器,内部由多个<radio/>组成. radio单选项目示例代码运行效果如下: 下面是WXML代码: ...