實際項目中使用骨架屏,試用各類方案(基於vue-cli 3.0)

前言

領導需求骨架屏,我等屌絲員工只能上了,調研試用了各類方案,記錄一下坑與收穫。css

UI庫自帶的骨架屏

咱們的項目使用的是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-server-renderer方案

這個方案使用下來和下面的vue-skeleton-webpack-plugin感受差很少,感受vue-skeleton-webpack-plugin稍微智能一點,此方案放棄

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.vuevue.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 的骨架屏實戰

相關文章
相關標籤/搜索