Vue多页面应用骨架屏添加实践

相关版本信息

"vue": "^2.2.2",
"webpack": "^2.2.1"
"html-webpack-plugin": "^2.28.0"

背景

使用VueWebpack进行**MPA(多页面应用)**的开发,一般会在页面进行数据接口等待时增加Loading动画,为用户提供较好的交互体验。但是会发现,Loading展示的时机是在Vue框架解析后,也就是说需要如下几个条件才能显示:

  1. HTML文件加载完成
  2. JavaScript文件加载完成
  3. window对象中完成webpackJsonp方法的添加

因此:等待的时间=HTML加载时间+JS加载时间+JS全局环境创建的执行时间。若是JS资源文件较大,或者存在过多的图片资源,导致资源速度下载较慢时,用户所能看到的白屏时间便较长。因此添加骨架屏,其优势在于:

  1. 写于HTML文件中,独立于Vue框架,节省了JS加载时间+JS全局环境创建的执行时间的时间
  2. 只在主页面根据页面结构独立编写,预先展示页面结构,进行视觉暂留,提供更好的交互感官
  3. 只在页面结构变化时进行修改,维护成本相对较低

步骤及相关代码

index.html文件中添加骨架屏的HTML和CSS代码

使用Vue进行MPA开发,独立页面都存在index.htmlindex.jsindex.vue三个基本文件。

index.html文件中定义Vue负责渲染的节点元素,index.js中进行Vue的挂载,存在两种写法:

第一种:

...
<body>
    <div id="app"></div>
</body>
...
...
let app = new Vue({
    el: '#app',
    components: {App},
    template: '<App/>'
});
...

第二种:

<body>
...
    <div id="app"><app></app></div>
...
</body>
...
let app = new Vue({
    el: '#app',
    components: {
        app
    }
});
...

注意:需选用第一种方式,如采用第二种方式,则会影响骨架屏的展示

在使用第一种方式时,<div id="app"></div>标签内的任何html结构都会在被Vue动态替换,这也便是进行骨架屏不用主动操作DOM元素的原因。

根据最终页面结构,进行骨架屏开发,并将html和css代码进行压缩混淆后,加入index.html中,示例代码如下:

<head>
    ...
    <style>骨架屏的css代码</style>
</head>
<body>
    ...
    <div id="app">骨架屏的html代码</div>
    ...
</body>

修改JS文件的加载方式

在测试目前已有的插件时发现,由于移动端和PC端浏览器内核对页面渲染的机制*可能(不同的原因尚未找到)*存在不同。在移动端访问页面时,会发现,在JS文件同步加载时会阻塞页面的渲染,使得骨架屏无法在JS文件加载时展示,当JS文件加载完成时便会自动执行,此时Vue会替换掉div节点中的元素,导致骨架屏代码消失,最终结果便是无法展示骨架屏。针对此种情况,需要对JS文件加载的方式进行调整。

HtmlWebpackPlugin插件的配置属性injecttrue时,会自动将JS和CSS文件注入到index.html,根据官方文档提供的选项,可以对JS文件进行sync同步或async异步加载。上文提到同步加载无法满足骨架屏的渲染要求。而且一般进行Webpack打包都会进行代码分割,提取公共代码为独立JS文件,各个页面的文件需要依赖于公共代码文件的优先执行。测试后发现可以通过检测webpackJsonp全局方法是否存在判断公用代码文件加载并执行完成,基本流程如下:

  1. 异步加载公用代码JS文件
  2. 针对页面独立JS文件使用link标签进行预加载
  3. 设置定时器,检测webpackJsonp方法存在,加载页面独立JS文件

Webpack配置文件部分代码如下:

...
webpackConfig.plugins.push(new HtmlWebpackPlugin({
    ...
    template: 'index.html',
    inject: false,
    ...
}));
...

index.html文件注入代码如下:

...
<head>
...
    <!-- 注入CSS样式文件-->
    <% for (var i = 0; i <  htmlWebpackPlugin.files.css.length; i++) { %>
    <link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[i] %>"/>
    <% }%>

    <!-- 注入除公共代码外的JS文件 -->
    <% for (var chunk in htmlWebpackPlugin.files.chunks) {
        if (!/vendor/.test(chunk)&&htmlWebpackPlugin.files.chunks[chunk]){
    %>
    <script>
        (function () {
            const chunkSrc = '<%=htmlWebpackPlugin.files.chunks[chunk].entry %>';
            // 预加载页面独立JS文件
            const preloadLink = document.createElement('link');
            preloadLink.href = chunkSrc;
            preloadLink.rel = 'preload';
            preloadLink.as = 'script';
            document.head.appendChild(preloadLink);
            // 设置定时器,检测公用文件是否执行OK
            const tick = setInterval(() => {
                if (window && window.webpackJsonp) {
                    clearInterval(tick);
                    // 加载页面独立JS文件
                    const preloadedScript = document.createElement('script');
                    preloadedScript.src = chunkSrc;
                    document.body.appendChild(preloadedScript);
                }
            }, 10);
        })();
    </script>
    <%}}%>
...
</head>
<body>
<% if (htmlWebpackPlugin.files.chunks.vendor) { %>
<script src="<%=htmlWebpackPlugin.files.chunks.vendor.entry %>" async></script>
<% } %>
</body>
...

这里使用了插件HtmlWebpackPlugin的模板写法,通过htmlWebpackPlugin对象进行代码chunk的注入,使用方法可参见官方文档

相关材料

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值