移动端之骨架屏(vue示范)
转自知乎作者:小蘑菇小哥https://zhuanlan.zhihu.com/p/48601348 让网页展现的更快,官方说法叫做首屏绘制,First Paint 或者简称 FP。首屏时间 FP 并不要求内容是真实的,有效的,有意义的,可交互的。 大体来说,骨架屏的优势在于:1、在页面加载初期预先渲染内容,提升感官上的体验。2、一般情况骨架屏和实际内容的结构是类...
转自知乎
作者:小蘑菇小哥
https://zhuanlan.zhihu.com/p/48601348
让网页展现的更快,官方说法叫做首屏绘制,First Paint 或者简称 FP。
首屏时间 FP 并不要求内容是真实的,有效的,有意义的,可交互的。
大体来说,骨架屏的优势在于:
1、在页面加载初期预先渲染内容,提升感官上的体验。
2、一般情况骨架屏和实际内容的结构是类似的,因此之后的切换不会过于突兀。这点和传统的 Loading 动图不同,可以认为是其升级版。
3、只需要简单的 CSS 支持 (涉及图片懒加载可能还需要 JS ),不要求 HTTPS 协议,没有额外的学习和维护成本。
4、如果页面采用组件化开发,每个组件可以根据自身状态定义自身的骨架屏及其切换时机,同时维持了组件之间的独立性。
web大致两种渲染模式:
前端渲染,后端渲染。
不论是传统模式还是 SSR,只要是后端渲染,就不需要骨架屏。因为页面的内容直接存在于 HTML,所以并没有骨架屏出场的余地。
实现思路:
大体分为几个步骤:
1.往本应为空的容器节点内部注入骨架屏的 HTML。
骨架屏为了尽快展现,要求快速和简单,所以骨架屏多数使用静态的图片。而且把图片编译成 base64 编码格式可以节省网络请求,使得骨架屏更快展现,更加有效。
<html>
<head>
<style>
.skeleton-wrapper {
// styles
}
</style>
<!-- 声明 meta 或者引入其他 CSS -->
</head>
<body>
<div id="app">
<div class="skeleton-wrapper">
<img src="data:image/svg+xml;base64,XXXXXX">
</div>
</div>
<!-- 引用 JS -->
</body>
</html>
2.在执行 JS 开始真正内容的渲染之前,清空骨架屏 HTML
以 Vue 为例,即在 mount 之前清空内容即可。
let app = new Vue({...})
let container = document.querySelector('#app')
if (container) {
container.innerHTML = ''
}
app.$mount(container)
问题:
1.需要告诉浏览器先渲染骨架屏
使用 <link rel="preload" href="xxxx">,提前把后续要使用的资源先声明一下
<link rel="preload" href="index.css" as="style" onload="this.rel='stylesheet'">
2.兼容性
首先,在 <link> 内部我们使用了 onload,也就是使用了 JS。。为了应对用户的浏览器没有开启脚本功能的情况,我们需要添加一个 fallback。(不过这点对于单页应用来说可能也无所谓,因为如果没有脚本,那页面实际内容也渲染不出来的)
<noscript><link rel="stylesheet" href="index.css"></noscript>
其次,rel="preload" 并不是没有兼容性问题。对于不支持 preload 的浏览器,我们可以添加一些 polyfill 代码(来使所有浏览器获得一致的效果。
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>
3.加载顺序
这里我们需要严格控制 CSS 早于渲染。
<link rel="preload" href="index.css" as="style" onload="this.rel='stylesheet';window.STYLE_READY=true;window.mountApp && window.mountApp()">
JS 对外暴露一个 mountApp 方法用于渲染页面(其实是模拟 Vue 的 mount)
// render.js
function mountApp() {
// 方法内部就是把实际内容添加到 <body> 上面
}
// 本来直接调用方法完成渲染
// mountApp()
// 改成挂到 window 由 CSS 来调用
window.mountApp = mountApp()
// 如果 JS 晚于 CSS 加载完成,那直接执行渲染。
if (window.STYLE_READY) {
mountApp()
}
如果 CSS 更快加载完成,那么通过设置 window.STYLE_READY 允许 JS 加载完成后直接执行;而如果 JS 更快,则先不自己执行,而是把机会留给 CSS 的 onload。
最终的css引用方式
<link rel="preload" href="index.css" as="style" onload="this.onload=null;this.rel='stylesheet';window.STYLE_READY=true;window.mountApp && window.mountApp()">
<!-- 为了方便阅读,折行重复一遍 -->
<!-- this.onload=null -->
<!-- this.rel='stylesheet' -->
<!-- window.STYLE_READY=true -->
<!-- window.mountApp && window.mountApp() -->
多骨架屏的支持:
为了要支持多种骨架屏,我们需要在 index.html 里面进行判断逻辑(独立于主体 JS 之外),具体来说:
1、把所有种类的骨架屏的 HTML 和样式全部写入 index.html
2、在 index.html 底下新增内联的脚本 <script>,根据当前路由判断应该展示哪一个骨架屏
这样会导致 index.html 体积变大一点,但整体感觉依然是收益大于付出,我认为是值得的。
更多推荐
所有评论(0)