移动端之骨架屏(vue示范)

转自知乎

作者:小蘑菇小哥

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 体积变大一点,但整体感觉依然是收益大于付出,我认为是值得的。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值