Vue頁面骨架屏的實現(xiàn)方法
在開發(fā)webapp的時候總是會受到首屏加載時間過長的影響,主流的解決方法是在載入完成之前顯示loading圖效果,而一些大公司會配置一套服務(wù)端渲染的架構(gòu)來解決這個問題??紤]到ssr所要解決的一系列問題,越來越多的APP采用了“骨架屏”的方式去提升用戶體驗。
小米商城:
一、分析Vue頁面的內(nèi)容加載過程
vue項目中的入口index.html只有簡單的內(nèi)容:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>Document</title> </head> <body> <div id="root"> </div> <script type="text/javascript" src="bundle.js"></script></body> </body> </html>
當(dāng)js執(zhí)行完之后,會用vue渲染成的dom將div#root
完全替換掉。
我們在div#root
中加入模擬骨架屏,在Chrome開發(fā)者工具調(diào)整網(wǎng)速:
<div id="root"> 這里是骨架屏 </div>
由此可知,將骨架屏內(nèi)容直接插入div#root
中即可實現(xiàn)骨架屏。
二、使用vue-server-renderer來實現(xiàn)骨架屏
我們需要骨架屏也是一個單獨的.vue
文件,因此我們需要用到vue-server-renderer
。對vue服務(wù)端渲染有所了解的同學(xué)一定知道,這個插件能夠?qū)ue項目在node端打包成一個bundle,然后由bundle生成對應(yīng)的html。
首先是生成項目:
. ├── build │ ├── webpack.config.client.js │ └── webpack.config.server.js ├── src │ └── views │ ├── index │ │ └── index.vue │ ├── skeleton │ │ └── skeleton.vue │ ├── app.vue │ ├── index.js │ └── skeleton-entry.js ├── index.html └── skeleton.js └── package.json
vue的服務(wù)端渲染一般會用vue-server-renderer
將整個項目在node端打包成一份bundle,而這里我們只要一份有骨架屏的html,所以會有一個單獨的骨架屏入口文件skeleton-entry.js
,一個骨架屏打包webpack配置webpack.config.server.js
,而skeleton.js
作用是將webpack打包出來的bundle寫入到index.html
中。
//skeleton-entry.js import Vue from 'vue' import Skeleton from './views/skeleton/skeleton.vue' export default new Vue({ components: { Skeleton }, template: '<skeleton />' })
//webpack.config.server.js const path = require('path') const { VueLoaderPlugin } = require('vue-loader') const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') module.exports = { mode: process.env.NODE_ENV, target: 'node', entry: path.join(__dirname, '../src/skeleton-entry.js'), output: { path: path.join(__dirname, '../server-dist'), filename: 'server.bundle.js', libraryTarget: 'commonjs2' }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] } ] }, externals: Object.keys(require('../package.json').dependencies), resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' } }, plugins: [ new VueLoaderPlugin(), new VueSSRServerPlugin({ filename: 'skeleton.json' }) ] }
其中骨架屏的webpack配置因為是node端,所以需要target: 'node'
libraryTarget: 'commonjs2'
。在VueSSRServerPlugin
中,指定了其輸出的json文件名。當(dāng)執(zhí)行webpack會在/server-dist目錄下生成一個skeleton.json
文件,這個文件記載了骨架屏的內(nèi)容和樣式,會提供給vue-server-renderer
使用。
//skeleton.js const fs = require('fs') const path = require('path') const createBundleRenderer = require('vue-server-renderer').createBundleRenderer // 讀取`skeleton.json`,以`index.html`為模板寫入內(nèi)容 const renderer = createBundleRenderer(path.join(__dirname, './server-dist/skeleton.json'), { template: fs.readFileSync(path.join(__dirname, './index.html'), 'utf-8') }) // 把上一步模板完成的內(nèi)容寫入(替換)`index.html` renderer.renderToString({}, (err, html) => { fs.writeFileSync('index.html', html, 'utf-8') })
注意,作為模板的html文件,需要在被寫入內(nèi)容的位置添加<!--vue-ssr-outlet-->占位符,本例子在div#root里寫入:
<div id="root"> <!--vue-ssr-outlet--> </div>
最后執(zhí)行node skeleton
就能實現(xiàn)vue的骨架屏。
最終的index.html
:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <title>Document</title> <style data-vue-ssr-id="a7049cb4:0"> .skeleton[data-v-61761ff8] { position: relative; height: 100%; overflow: hidden; padding: 15px; box-sizing: border-box; background: #fff; } .skeleton-nav[data-v-61761ff8] { height: 45px; background: #eee; margin-bottom: 15px; } .skeleton-swiper[data-v-61761ff8] { height: 160px; background: #eee; margin-bottom: 15px; } .skeleton-tabs[data-v-61761ff8] { list-style: none; padding: 0; margin: 0 -15px; display: flex; flex-wrap: wrap; } .skeleton-tabs-item[data-v-61761ff8] { width: 25%; height: 55px; box-sizing: border-box; text-align: center; margin-bottom: 15px; } .skeleton-tabs-item span[data-v-61761ff8] { display: inline-block; width: 55px; height: 55px; border-radius: 55px; background: #eee; } .skeleton-banner[data-v-61761ff8] { height: 60px; background: #eee; margin-bottom: 15px; } .skeleton-productions[data-v-61761ff8] { height: 20px; margin-bottom: 15px; background: #eee; } </style></head> <body> <div id="root"> <div data-server-rendered="true" class="skeleton page" data-v-61761ff8><div class="skeleton-nav" data-v-61761ff8></div> <div class="skeleton-swiper" data-v-61761ff8></div> <ul class="skeleton-tabs" data-v-61761ff8><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li></ul> <div class="skeleton-banner" data-v-61761ff8></div> <div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div></div> </div> </body> </html>
看下效果:
效果還是闊以的。
尾聲
文章開頭小米商城手機(jī)頁面就是用的這樣的方法,不同的是它的骨架屏是一個base64的圖片。
更多關(guān)于vue-server-renderer
內(nèi)容請戳vue-ssr
文章相關(guān)代碼已經(jīng)同步到Github,歡迎查閱~
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue通過滾動行為實現(xiàn)從列表到詳情,返回列表原位置的方法
今天小編就為大家分享一篇vue通過滾動行為實現(xiàn)從列表到詳情,返回列表原位置的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08Vue3使用el-table組件實現(xiàn)分頁、多選以及回顯功能
這篇文章主要介紹了Vue3使用el-table組件實現(xiàn)分頁、多選以及回顯功能,文中通過代碼示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-09-09VUE側(cè)邊導(dǎo)航欄實現(xiàn)篩選過濾的示例代碼
本文主要介紹了VUE側(cè)邊導(dǎo)航欄實現(xiàn)篩選過濾的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05分享12個Vue開發(fā)中的性能優(yōu)化小技巧(實用!)
一般來說,你不需要太關(guān)心vue的運行時性能,它在運行時非常快,但付出的代價是初始化時相對較慢,下面這篇文章主要給大家分享介紹了十二個Vue開發(fā)中的性能優(yōu)化小技巧,需要的朋友可以參考下2022-02-02vue+echarts定時重新繪制以達(dá)到刷新的動效問題
這篇文章主要介紹了vue+echarts定時重新繪制以達(dá)到刷新的動效問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06Vue使用axios post方式將表單中的數(shù)據(jù)以json格式提交給后端接收操作實例
這篇文章主要介紹了Vue使用axios post方式將表單中的數(shù)據(jù)以json格式提交給后端接收操作,結(jié)合實例形式分析了vue基于axios庫post傳送表單json格式數(shù)據(jù)相關(guān)操作實現(xiàn)技巧與注意事項,需要的朋友可以參考下2023-06-06