Vue頁面顯示骨架屏
Vue頁面顯示骨架屏
1.什麼是骨架螢幕?
在頁面載入資料之前,有一段空白時間,要麼用loading載入,要麼就用骨架屏。
2.如何快速用Vue實現骨架屏效果?
#①直接把下面的CSS程式碼複製,貼上到vue專案的index.html 的 <head>...</head>裡面
<style> .skeleton { position: fixed; height: 100%; overflow: hidden; padding: 15px; box-sizing: border-box; background: #fff; width: 98%; top: 0; } .skeleton-nav { height: 110px; background: #eee; margin-bottom: 15px; } .skeleton-swiper { height: 440px; background: #eee; margin-bottom: 15px; } .skeleton-tabs { list-style: none; padding: 0; margin: 0 -15px; display: flex; flex-wrap: wrap; } .skeleton-tabs-item { width: 22%; height: 80px; box-sizing: border-box; text-align: center; margin-bottom: 15px; } .skeleton-tabs-item span { display: inline-block; width: 80px; height: 80px; border-radius: 55px; background: #eee; } .skeleton-banner { height: 250px; background: #eee; margin-bottom: 15px; } .skeleton-productions { height: 50px; margin-bottom: 15px; background: #eee; } </style>
#②把下面的html程式碼複製,貼上到vue專案的index.html 的 <div id=app>.....</div>裡面
1 <div data-server-rendered=true class="skeleton page"> 2 <div class=skeleton-nav></div> 3 <div class=skeleton-swiper></div> 4 <ul class=skeleton-tabs> 5 <li class=skeleton-tabs-item><span></span></li> 6 <li class=skeleton-tabs-item><span></span></li> 7 <li class=skeleton-tabs-item><span></span></li> 8 <li class=skeleton-tabs-item><span></span></li> 9 <li class=skeleton-tabs-item><span></span></li> 10 <li class=skeleton-tabs-item><span></span></li> 11 <li class=skeleton-tabs-item><span></span></li> 12 <li class=skeleton-tabs-item><span></span></li> 13 </ul> 14 <div class=skeleton-banner></div> 15 <div class=skeleton-productions></div> 16 <div class=skeleton-productions></div> 17 <div class=skeleton-productions></div> 18 <div class=skeleton-productions></div> 19 <ul class=skeleton-tabs> 20 <li class=skeleton-tabs-item><span></span></li> 21 <li class=skeleton-tabs-item><span></span></li> 22 <li class=skeleton-tabs-item><span></span></li> 23 <li class=skeleton-tabs-item><span></span></li> 24 <li class=skeleton-tabs-item><span></span></li> 25 <li class=skeleton-tabs-item><span></span></li> 26 <li class=skeleton-tabs-item><span></span></li> 27 <li class=skeleton-tabs-item><span></span></li> 28 </ul> 29 <div class=skeleton-swiper></div> 30 <div class=skeleton-nav></div> 31 </div>
#③如果你是vue-cli腳手架搭的,直接就可以看到效果了,然後npm run build。就成了。
下圖是我做的效果。
3.既然來到了這裡,我們肯定要知道原理的。
感謝作者的分享: 附上鍊接 https://segmentfault.com/a/1190000014832185
原理:
DOM裡面有且僅有一個div#app
,當js被執行完成之後,此div#app
會被整個替換掉,也就是“測試測試測試測試測試測試測試測試”這部分內容,會被替換掉。
所以:我們寫在裡面的html程式碼首屏時會顯示,但DOM經過render渲染結束後,裡面就被替換了。
如下程式碼:是index.html裡面程式碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>vue-skeleton</title> </head> <body> <div id="app"> 測試測試測試測試測試測試測試測試 </div> <script src="/dist/build.js"></script> </body> </html>
4.如何從無到有些一個骨架屏
①通過vue-cli安裝一個簡易版的vue專案
vue init webpack-simple vue-skeleton
②安裝第一次外掛:
npm install vue-server-renderer --save //安裝服務端渲染外掛,利用它能夠把.vue檔案處理成html和css字串的功能,來完成骨架屏的注入 npm install webpack-node-externals --save-dev //安裝外部外掛,在專案中給css設定白名單
npm install html-webpack-plugin --save-dev //打包的時候可以將index.html一起打包
③在/src
目錄下新建一個Skeleton.vue
檔案,樣式都可以自己定義的,為了方便,可以直接複製貼上。
1 <template> 2 <div class="skeleton page"> 3 <div class="skeleton-nav"></div> 4 <div class="skeleton-swiper"></div> 5 <ul class="skeleton-tabs"> 6 <li v-for="i in 8" class="skeleton-tabs-item"><span></span></li> 7 </ul> 8 <div class="skeleton-banner"></div> 9 <div v-for="i in 4" class="skeleton-productions"></div> 10 <ul class="skeleton-tabs"> 11 <li v-for="i in 8" class="skeleton-tabs-item"><span></span></li> 12 </ul> 13 <div class="skeleton-swiper"></div> 14 <div class="skeleton-nav"></div> 15 </div> 16 </template> 17 18 <style> 19 .skeleton { 20 position: fixed; 21 height: 100%; 22 overflow: hidden; 23 padding: 15px; 24 box-sizing: border-box; 25 background: #fff; 26 width: 98%; 27 top: 0; 28 } 29 .skeleton-nav { 30 height: 110px; 31 background: #eee; 32 margin-bottom: 15px; 33 } 34 .skeleton-swiper { 35 height: 440px; 36 background: #eee; 37 margin-bottom: 15px; 38 } 39 .skeleton-tabs { 40 list-style: none; 41 padding: 0; 42 margin: 0 -15px; 43 display: flex; 44 flex-wrap: wrap; 45 } 46 .skeleton-tabs-item { 47 width: 22%; 48 height: 80px; 49 box-sizing: border-box; 50 text-align: center; 51 margin-bottom: 15px; 52 } 53 .skeleton-tabs-item span { 54 display: inline-block; 55 width: 80px; 56 height: 80px; 57 border-radius: 55px; 58 background: #eee; 59 } 60 .skeleton-banner { 61 height: 250px; 62 background: #eee; 63 margin-bottom: 15px; 64 } 65 .skeleton-productions { 66 height: 50px; 67 margin-bottom: 15px; 68 background: #eee; 69 } 70 </style>
④在Skeleton.vue同個目錄下,建立一個skeleton.entry.js
入口檔案
1 import Vue from 'vue' 2 import Skeleton from './Skeleton.vue' 3 4 export default new Vue({ 5 components: { 6 Skeleton 7 }, 8 template: '<skeleton />' 9 })
⑤在根目錄新建一個webpack.skeleton.conf.js
檔案,以專門用來進行骨架屏的構建,vue-server-renderer就是將Skeleton.vue生成json檔案,以便插入到html。
該配置檔案和普通的配置檔案基本完全一致,主要的區別在於其target: 'node'
,配置了externals
,以及在plugins
裡面加入了VueSSRServerPlugin
。在VueSSRServerPlugin
中,
指定了其輸出的json檔名。我們可以通過執行下列指令,在/dist
目錄下生成一個skeleton.json
檔案
const path = require('path') const webpack = require('webpack') const nodeExternals = require('webpack-node-externals') const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = { target: 'node', entry: { skeleton: './src/skeleton.entry.js' },
output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: '[name].js', libraryTarget: 'commonjs2' },
plugins: [
new HtmlWebpackPlugin(
{
title: 'test',
template: 'index.html',
}
)
],
module: { rules: [ { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] }, { test: /\.vue$/, loader: 'vue-loader' } ] }, externals: nodeExternals({ whitelist: /\.css$/ }), resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['*', '.js', '.vue', '.json'] }, plugins: [ new VueSSRServerPlugin({ filename: 'skeleton.json' }) ] }
⑥使用命令 將skeleton.vue檔案變成json格式
webpack --config ./webpack.skeleton.conf.js
⑦在根目錄下新建一個skeleton.js
,該檔案即將被用於往index.html
內插入骨架屏
1 const fs = require('fs') 2 const { resolve } = require('path') 3 4 const createBundleRenderer = require('vue-server-renderer').createBundleRenderer 5 6 // 讀取`skeleton.json`,以`index.html`為模板寫入內容 7 const renderer = createBundleRenderer(resolve(__dirname, './dist/skeleton.json'), { 8 template: fs.readFileSync(resolve(__dirname, './index.html'), 'utf-8') 9 }) 10 11 // 把上一步模板完成的內容寫入(替換)`index.html` 12 renderer.renderToString({}, (err, html) => { 13 fs.writeFileSync('index.html', html, 'utf-8') 14 })
⑧將<!--vue-ssr-outlet-->
佔位符寫在index.html裡面,才能注入程式碼
<div id="app"> <!--vue-ssr-outlet--> </div>
⑨最後,只要執行node skeleton.js
,就可以完成骨架屏的注入了。可以看下index.html的結果。
⑩進一步處理,把這些內容都壓縮一下,改寫skeleton.js
1 ... 2 3 + const htmlMinifier = require('html-minifier') 4 5 ... 6 7 renderer.renderToString({}, (err, html) => { 8 + html = htmlMinifier.minify(html, { 9 + collapseWhitespace: true, 10 + minifyCSS: true 11 + }) 12 fs.writeFileSync('index.html', html, 'utf-8') 13 })
結果:
看不懂,可以再次參考原作者https://segmentfault.com/a/1190000014832185