领导需求骨架屏,我等屌丝员工只能上了,调研试用了各类方案,记录一下坑与收获。css
咱们的项目使用的是vant,试用了一把vant自带的骨架屏,与领导需求不符,放弃。html
page-skeleton-webpack-pluginvue
安装webpack
npm install --save-dev page-skeleton-webpack-plugin
npm install --save-dev html-webpack-plugin
复制代码
vue.config.jsgit
const { SkeletonPlugin } = require("page-skeleton-webpack-plugin");
const path = require("path");
...
configureWebpack: config => {
...
config.plugins = [
// 使用骨架屏插件
new SkeletonPlugin({
pathname: path.resolve(__dirname, "./shell"), // 用来存储 shell 文件的地址
staticDir: path.resolve(__dirname, "./dist"), // 最好和 `output.path` 相同
routes: ["/"] // 将须要生成骨架屏的路由添加到数组中
})
];
},
chainWebpack: config => {
if (isProduction) {
// 解决vue-cli3脚手架建立的项目压缩html 干掉<!-- shell -->致使骨架屏不生效
config.plugin("html").tap(opts => {
opts[0].minify.removeComments = false;
return opts;
});
}
}
复制代码
进入项目根目录github
cd /project-a
mkdir shell
复制代码
index.htmlweb
<!--添加shell-->
<div id="app"><!-- shell --></div>
复制代码
从新启动,报错vue-cli
这是由于使用page-skeleton-webpack-plugin
以前须要预加载html-webpack-plugin
,上面的vue.config.js
写法可能致使html-webpack-plugin
没有在骨架屏插件前面加载成功,改变写法:shell
vue.config.jsnpm
...
configureWebpack: {
externals: {
vue: "Vue"
},
plugins: [
new SkeletonPlugin({
pathname: path.resolve(__dirname, "./shell"), // 用来存储 shell 文件的地址
staticDir: path.resolve(__dirname, "./dist"), // 最好和 `output.path` 相同
routes: ["/"] // 将须要生成骨架屏的路由添加到数组中
})
]
},
...
复制代码
从新启动,成功
报错没有这样路径的文件,因为众所周知的缘由我没法使用history方式的路由(饿了么方案必须使用history的方式),加上此方案只能首屏使用,再三考虑,放弃了使用此方案!
这个方案使用下来和下面的vue-skeleton-webpack-plugin感受差很少,感受vue-skeleton-webpack-plugin稍微智能一点,此方案放弃
安装
npm install vue-skeleton-webpack-plugin
复制代码
vue.config.js
configureWebpack: config => {
config.plugins = [
new SkeletonWebpackPlugin({
webpackConfig: {
entry: {
app: path.join(__dirname, "./src/skeleton.js")
}
},
minimize: true,
quiet: true
})
];
},
复制代码
在src下新建js和vue模板
cd src
mkdir skeleton.js
vim skeleton.vue
复制代码
skeleton.js
import Vue from "vue";
import Skeleton from "./Skeleton.vue";
export default new Vue({
components: {
Skeleton
},
render: h => h(Skeleton)
});
复制代码
skeleton.vue
<template>
<div class="skeleton-wrapper">
<section class="skeleton-block">
<!-- eslint-disable vue/max-len -->
<img
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg=="
/>
<img
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg=="
/>
</section>
</div>
</template>
<script>
export default {
name: "Skeleton"
};
</script>
<style scoped>
.skeleton-block {
display: flex;
flex-direction: column;
padding: 16px;
}
</style>
复制代码
从新启动,报错:
vue、vue-server-renderer版本不匹配,执行命令更新npm包
npm update
复制代码
再次从新启动,报错:
此问题,和上面碰见的如出一辙,只是提示的方式不一样而已,修改vue.config.js
configureWebpack: {
...
plugins: [
new SkeletonWebpackPlugin({
webpackConfig: {
entry: {
app: path.join(__dirname, "./src/skeleton.js")
}
},
minimize: true,
quiet: true
})
]
},
复制代码
从新启动,ok,骨架屏已经出来了
不是我想要的效果,修改skeleton.vue
和vue.config.js
skeleton.vue
<template>
<div class="skeleton">
<div class="skeleton-head">
<div class="head-item"></div>
<div class="head-item"></div>
<div class="head-item"></div>
</div>
<div class="skeleton-content"></div>
<div class="skeleton-content"></div>
</div>
</template>
<script>
export default {
name: "skeleton"
};
</script>
<style scoped>
.skeleton {
padding: 10px;
}
.skeleton-head {
width: 100%;
display: flex;
justify-content: space-between;
}
.head-item {
width: 30%;
height: 50px;
}
.skeleton-content {
width: 100%;
height: 200px;
margin-top: 20px;
border-radius: 10px;
}
.skeleton .head-item,
.skeleton .skeleton-content {
background: rgb(194, 207, 214);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%
);
background-size: 20rem 20rem;
animation: skeleton-stripes 1s linear infinite;
}
@keyframes skeleton-stripes {
from {
background-position: 0 0;
}
to {
background-position: 20rem 0;
}
}
</style>
复制代码
vue.config.js
css: {
// 开启CSS样式分离,开发环境下,默认为false,extract默认为false的时候,vue-skeleton-webpack-plugin自定义样式不生效,一片空白的
extract: true,
...
},
复制代码
如今这样的是,全部页面统一使用一个骨架屏,很差,仍是改为单独每一个页面使用单独的骨架屏吧!
src下新建文件夹skeleton
cd src
mkdir skeleton
// 移动骨架屏文件到skeleton文件夹下
mv skeleton.vue ./skeleton
复制代码
修改 skeleton.js
import Vue from "vue";
import skeletonHome from "./skeleton/Skeleton.vue";
export default new Vue({
components: {
skeletonHome
},
template: `
<div>
<skeletonHome id="skeleton-home" style="display:none"/>
</div>
`
});
复制代码
修改vue.config.js
configureWebpack: {
...
plugins: [
// vue-skeleton-webpack-plugin 骨架屏
new SkeletonWebpackPlugin({
webpackConfig: {
entry: {
app: path.join(__dirname, "./src/skeleton.js")
}
},
minimize: true,
quiet: true,
// 根据路由显示骨架屏
router: {
mode: "hash",
routes: [
{
path: "/",
skeletonId: "skeleton-home"
}
]
}
})
]
},
复制代码
从新启动,ok
到这里就告一段落了,若是哪一个页面须要有骨架屏,就在
skeleton
文件夹下新建一个.vue文件,在skeleton.js
中引入,在vue.config.js
中添加对应的路由便可
原本想弄一个彻底自动的骨架屏方案,根据每个页面自动生成一个骨架屏,可是我本人太菜了,暂时对此功能无能为力。市面上的方案就彻底没有一个能全自动的,饿了么说是计划弄一个,遥遥无期;淘宝骨架屏方案,搜索了一下,只是有一些文章介绍,貌似木有开源;腾讯搜索更多的是小程序骨架屏方案;京东貌似有一个dps方案,我去看了下他们的git,讲的挺好,可是demo没有实际参考的意义,有不少疑问,并且仍是新出,本人不是勇士,仍是等等吧!
给项目加一个骨架屏吧
基于vue-cli实现自动生成Skeleton Page,多页skeleton
基于 vue-skeleton-webpack-plugin 的骨架屏实战