vue-cli3配置骨架屏方案

vue-cli3配置骨架屏方案

前言

最近在學vue,準備使用vue寫一個移動端項目。考慮到首頁白屏優化,需要實現骨架屏需求。這裏介紹兩種方案,當然都是根據現有的輪子搭的

步驟

  • 安裝vue-skeleton-webpack-plugin插件
npm install --save-dev vue-skeleton-webpack-plugin
  • vue.config.js配置
const path = require('path')
const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin');

module.exports = {
    css: {
         extract: true, // css拆分ExtractTextPlugin插件,默認true - 骨架屏需要爲true
    },
    
    configureWebpack: (config)=>{
        // vue骨架屏插件配置
        config.plugins.push(new SkeletonWebpackPlugin({
          webpackConfig: {
            entry: {
              app: path.join(__dirname, './src/utils/skeleton.js'),
            },
          },
          minimize: true,
          quiet: true,
        }))
  },
}
  • 新建一個skeleton.js文件放在src->utils文件夾下面、
import Vue from 'vue';
import Skeleton from '../components/Skeleton/skeleton-2';

export default new Vue({
  components: {
    Skeleton,
  },
  render: h => h(Skeleton),
});

這個文件是用來注入骨架屏的
  • 新建一個skeleton-2.vue骨架屏組件
<template>
  <div class="skeleton page">
    <div class="skeleton-nav"></div>
    <div class="skeleton-swiper"></div>
    <ul class="skeleton-tabs">
      <li v-for="i in 8" class="skeleton-tabs-item"><span></span></li>
    </ul>
    <div class="skeleton-banner"></div>
    <div v-for="i in 6" class="skeleton-productions"></div>
  </div>
</template>

<style>
  .skeleton {
    position: relative;
    height: 100%;
    overflow: hidden;
    padding: 15px;
    box-sizing: border-box;
    background: #fff;
  }
  .skeleton-nav {
    height: 45px;
    background: #eee;
    margin-bottom: 15px;
  }
  .skeleton-swiper {
    height: 160px;
    background: #eee;
    margin-bottom: 15px;
  }
  .skeleton-tabs {
    list-style: none;
    padding: 0;
    margin: 0 -15px;
    display: flex;
    flex-wrap: wrap;
  }
  .skeleton-tabs-item {
    width: 25%;
    height: 55px;
    box-sizing: border-box;
    text-align: center;
    margin-bottom: 15px;
  }
  .skeleton-tabs-item span {
    display: inline-block;
    width: 55px;
    height: 55px;
    border-radius: 55px;
    background: #eee;
  }
  .skeleton-banner {
    height: 60px;
    background: #eee;
    margin-bottom: 15px;
  }
  .skeleton-productions {
    height: 20px;
    margin-bottom: 15px;
    background: #eee;
  }

  .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);
    }
  }
</style>

  • 新建一個skeleton-1.vue骨架屏組件
<template>
  <div class="skeleton">
    <div class="skeleton-head"></div>
    <div class="skeleton-body">
      <div class="skeleton-title"></div>
      <div class="skeleton-content"></div>
    </div>
  </div>
</template>

<style>
.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 0.5s linear infinite alternate;
}

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

@keyframes skeleton-stretch {
  from {
    transform: scalex(1);
  }
  to {
    transform: scalex(0.3);
  }
}
</style>
  • 修改main.js配置
const app = new Vue({
  store,
  router,
  render: h => h(App)
}).$mount('#app')
  • 重啓項目

思路

  • 將骨架屏也看成路由組件,在構建時使用 Vue 預渲染功能,將骨架屏組件的渲染結果 HTML 片段插入 HTML 頁面模版的掛載點中,將樣式內聯到 head 標籤中。這樣等前端渲染完成時,Vue 將使用客戶端混合,把掛載點中的骨架屏內容替換成真正的頁面內容。

缺點

  • 這種方案實現的是固定死的骨架(可以查看skeleton-2.vue skeleton-1.vue)兩個文件,不能夠自動根據頁面DOM結構生成骨架

優化的方向 (這裏補充第二種方案)

餓了麼團隊 page-skeleton-webpack-plugin開發的自動生成頁面骨架屏方案

  • 安裝 page-skeleton-webpack-plugin html-webpack-plugin
npm install --save-dev page-skeleton-webpack-plugin //這個包有140M 需要注意是否成功下載
npm install --save-dev html-webpack-plugin
  • 配置插件

這裏使用的vue-cli3,故先新建文件vue.config.js


const HtmlWebpackPlugin = require('html-webpack-plugin')
const SkeletonPlugin  = require('page-skeleton-webpack-plugin').SkeletonPlugin
const path = require('path')

module.exports = {
  configureWebpack: {
    plugins: [
      new HtmlWebpackPlugin({
        filename: 'index.html',
        template: 'public/index.html'
      }),
      new SkeletonPlugin({
        pathname: path.resolve(__dirname, `./shell`), // 用來存儲 shell 文件的地址
        staticDir: path.resolve(__dirname, './dist'), // 最好和 `output.path` 相同
        routes: ['/', '/about'] // 將需要生成骨架屏的路由添加到數組中
      })
    ]
  }
}


  • 修改HTML Webpack Plugin 插件的模板
    因爲我們的項目用的是vue-cli腳手架創建的,所以修改public/index.html內容,修改如下:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>vue-skeleton</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue-skeleton doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app">
      <!-- shell -->
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

重點是 <!-- shell -->,因爲插件是根據這個來找到往哪個DOM節點下面動態的插入骨架屏的html文件的。

  • 重啓項目

可以打開控制檯,看到有如下提示說明插件已經正常運行了

image

  • 啓動插件

在開發頁面中通過 Ctrl|Cmd + enter 呼出插件交互界面,或者在在瀏覽器的 JavaScript 控制檯內輸入toggleBar呼出交互界面。點擊交互界面中的按鈕,進行骨架頁面的預覽,這一過程可能會花費 20s 左右時間,當插件準備好骨架頁面後,會自動通過瀏覽器打開預覽頁面,如下圖。

image

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章