(注意:本文是针对路由懒加载的项目)css
(1)骨架屏相信大多数人都听讲过,也实现过。(还不清楚骨架屏是什么的能够参考 开发一个简单易懂的webpack骨架屏插件html
但通常状况下都是给首页第一次加载的时候添加骨架屏,而不是给每一个路由页面都加上骨架屏。vue
怎么说呢?webpack
单页面应用第一次加载的时候须要加载大量的js,css资源,若是网络慢的状况,首次打开页面可能会出现十几秒的白屏时间。 如今市场上都是针对首次加载作了骨架屏优化,不多有作路由跳转到其余页面的骨架屏优化。(可能不少人都认为不必作其余路由页面的骨架屏优化,但本文先忽略这个必要性)web
(2)看了vue-skeleton-webpack-plugin的源码,发现该插件的routers也作不了其余路由页面的骨架屏优化。因此想出了本文的方案。vue-router
市场上的单页面应用多数都会作路由懒加载,以vue项目为例: 在注册vue-router的时候,通常像下图作路由分割bash
大概意思就是,当跳转到 /home 页面的时候才加载/home页面对应的js资源。只有当这个js资源加载完成以后页面才会更新。因此若是加载这个js资源的时间用了10几秒,那么咱们将会原地等待10几秒,或者会白屏10几秒。这样的用户体验是很是很差的。网络
若是咱们能够在这10几秒里作一个骨架屏让它先展现出来,等到js资源加载回来后再隐藏它。这样的用户体验不是更加好呢。app
如上图,component对应的value是一个函数,该函数就是加载js资源的Promise, 众所周知,Promise有then方法能够知道js资源何时加载完成。那么咱们能够在import('../views/home/index.vue')以前先让骨架屏显示出来,让<div id="app"></div>
隐藏。而后在import('../views/home/index.vue').then()方法里再将骨架屏隐藏,<div id="app"></div>
显示。svg
(2.1)首先对index.html作修改,以下:
在index.html里添加两个同级得div,一个是骨架屏得div,一个是<div id="app"></div>
,你能够在骨架屏得div里写上本身的样式。再在<head>
里写上简单的显示隐藏的css样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.skeleton-block[data-v-07e00dc6] {
display: flex;
flex-direction: column;
padding: 16px;
}
.body-wrap-show .skeleton-wrap {
display: block !important;
}
.body-wrap-show #app {
display: none !important;
}
.body-wrap-hidden .skeleton-wrap {
display: none !important;
}
.body-wrap-hidden #app {
display: block !important;
}
</style>
</head>
<body>
<div data-server-rendered="true" class="skeleton-wrap" style="display: none;">
<div data-v-07e00dc6>
<section class="skeleton-block" data-v-07e00dc6>
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==" data-v-07e00dc6>
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==" data-v-07e00dc6>
</section>
</div>
</div>
<div id="app"></div>
</script>
</body>
</html
复制代码
(2.2)路由注册文件的修改,以下
大概意思是:每次路由跳转以前先将<div id="app"></div>
隐藏,让骨架怕展现。在then方法里再让<div id="app"></div>
展现,骨架屏隐藏。这样每次路由跳转都会出现骨架屏了。
import Vue from 'vue';
import vueRouter from 'vue-router';
Vue.use(vueRouter);
const router = new vueRouter({
mode: 'history',
routes: [
{
path: '/home',
component: () => {
let body = document.body;
// 每次路由跳转以前先将<div id="app"></div>隐藏
document.getElementById('app').style.display = 'none';
// 移除body上的body-wrap-hidden
body.classList.remove('body-wrap-hidden');
// 在body上的添加class 为body-wrap-show
body.classList.add('body-wrap-show');
return import('../views/home/index.vue').then(res => {
//当js资源加载完成后,将骨架屏隐藏
body.classList.remove('body-wrap-show');
//当js资源加载完成后,将<div id="app"></div>展现
body.classList.add('body-wrap-hidden');
//释放body节点的引用
body = null;
// 注意:then方法里面必须得返回一个值,否则vue-router会报错
return res;
})
}
},
]
});
export default router;
复制代码
(2.4)效果以下:
留意上图,当我点击“我是首页”的时候,是马上出现骨架屏的,再看右边的js资源是正在加载,尚未加载完成。也就是说,它不是使用v-if或者v-show在“我是详情页面”的页面里面作的骨架屏。后者它有个限制,由于后者所谓的骨架屏也是属于“我是详情页面”的代码,必须等待js资源返回后才会显示。而前者则是加载js资源以前就已经显示了。 **
(2.3)结束语:
本文只是个思路,真正想作到每一个路由页面都有各自的骨架屏还得作不少的事情。 (有不当之处还望指出)。