vue-cli 构建的项目如何加入骨架屏 skeleton

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012878818/article/details/81216272

在使用 Native APP 时我们经常可以看到在实际内容加载出来之前会有一些 占位图片,因为其结构和实际加载内容相似(如新闻页面加载之前也会有一个纵向列表),因此让用户感知到自己的页面正在加载中,体验较好。而传统的 Web 站点因为无法实现这一点,在加载前统一为白屏,我们能优化的仅仅是缩短这个 白屏时间,这就决定了 Web App 相较于 Native APP 在用户体验上的先天劣势。

我们把这个 占位图片 称为 skeleton 。通过 PWA 的缓存机制,我们现在已经有能力让 Skeleton 也出现在 Web App 上取代白屏。在了解具体细节之前,我们先观察一下我的demo效果图。

github地址:https://github.com/hwq888/skeleton  

我们希望在构建时渲染 skeleton 组件,将渲染 DOM 插入 html 的挂载点中,同时将使用的样式通过 style 标签内联。这样在前端 JS 渲染完成之前,用户将看到页面的大致骨架,感知到页面是正在加载的。

我们当然可以选择在开发时直接将页面骨架内容写入 html 模版中,但是这会带来两个问题:

  1. 开发 skeleton 与其他组件体验不一致。
  2. 多页应用中多个页面可能共用同一个 html 模版,而又有独立的 skeleton。

下面我们将看看插件在具体实现中是如何解决这两个问题的:

vue-skeleton-webpack-plugin

github 地址:https://github.com/lavas-project/vue-skeleton-webpack-plugin

具体实现步骤:

1、我们用vue-cli 直接构建一下项目跑起来(具体怎么构建就不说了)

2、进去当前项目,执行命令 : npm install vue-skeleton-webpack-plugin 

3、我们在src目录下创建 Skeleton.vue

<template>
  <div class="skeleton-wrapper">
    <header class="skeleton-header"></header>
    <section class="skeleton-block">
      <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-header {
    height: 40px;
    background: #1976d2;
    padding:0;
    margin: 0;
    width: 100%;
  }
  .skeleton-block {
    display: flex;
    flex-direction: column;
    padding-top: 8px;
  }

</style>

4、创建入口文件:entry-skeleton.js

import Vue from 'vue'
import Skeleton from './Skeleton'
export default new Vue({
  components: {
    Skeleton
  },
  template: '<Skeleton />'
})

5、我们在build 目录下创建 webpack.skeleton.conf.js

'use strict';

const path = require('path')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const nodeExternals = require('webpack-node-externals')

function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = merge(baseWebpackConfig, {
  target: 'node',
  devtool: false,
  entry: {
    app: resolve('../src/entry-skeleton.js')
  },
  output: Object.assign({}, baseWebpackConfig.output, {
    libraryTarget: 'commonjs2'
  }),
  externals: nodeExternals({
    whitelist: /\.css$/
  }),
  plugins: []
})

然后在webpack.dev.conf.js和webpack.prod.conf.js分别加入

const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')
// inject skeleton content(DOM & CSS) into HTML
    new SkeletonWebpackPlugin({
      webpackConfig: require('./webpack.skeleton.conf'),
      quiet: true
    })

然后就完成了。

延伸:

1、vue-skeleton-webpack-plugin 可以 使用多个 骨架屏 ,具体的可以查看官网地址:https://github.com/lavas-project/vue-skeleton-webpack-plugin

2、Skeleton Screen 样式实现

第一种

简单堆砌出元素结构

html

<div class="skeleton">
    <div class="skeleton-head"></div>
    <div class="skeleton-body">
        <div class="skeleton-title"></div>
        <div class="skeleton-content"></div>
    </div>
</div>
  •  

css

.skeleton {
    padding: 10px;
}

.skeleton .skeleton-head,
.skeleton .skeleton-title,
.skeleton .skeleton-content {
    background: rgb(194, 207, 214);
}

.skeleton-head {
    width: 100px;
    height: 100px;
    float: left;
}

.skeleton-body {
    margin-left: 110px;
}

.skeleton-title {
    width: 500px;
    height: 60px;
}

.skeleton-content {
    width: 260px;
    height: 30px;
    margin-top: 10px;
}    
  •  

第二种

背景动画,html结构相同,修改部分css样式

.skeleton .skeleton-head,
.skeleton .skeleton-title,
.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;
    }
}
  •  

第三种

元素结构长度变化 

.skeleton {
  padding: 10px;
}

.skeleton .skeleton-head,
.skeleton .skeleton-title,
.skeleton .skeleton-content {
  background: rgb(194, 207, 214);
}

.skeleton-head {
  width: 100px;
  height: 100px;
  float: left;
}

.skeleton-body {
  margin-left: 110px;
}

.skeleton-title {
  width: 500px;
  height: 60px;
  transform-origin: left;
  animation: skeleton-stretch .5s linear infinite alternate;
}

.skeleton-content {
  width: 260px;
  height: 30px;
  margin-top: 10px;
  transform-origin: left;
  animation: skeleton-stretch .5s -.3s linear infinite alternate;
}

@keyframes skeleton-stretch {
  from {
    transform: scalex(1);
  }
  to {
    transform: scalex(.3);
  }
}

猜你喜欢

转载自blog.csdn.net/u012878818/article/details/81216272