關(guān)于Vue單頁(yè)面骨架屏實(shí)踐記錄
關(guān)于骨架屏介紹
骨架屏的作用主要是在網(wǎng)絡(luò)請(qǐng)求較慢時(shí),提供基礎(chǔ)占位,當(dāng)數(shù)據(jù)加載完成,恢復(fù)數(shù)據(jù)展示。這樣給用戶一種很自然的過(guò)渡,不會(huì)造成頁(yè)面長(zhǎng)時(shí)間白屏或者閃爍等情況。 常見(jiàn)的骨架屏實(shí)現(xiàn)方案有ssr服務(wù)端渲染和prerender兩種解決方案。
這里主要通過(guò)代碼為大家展示如何一步步做出這樣一個(gè)骨架屏:
prerender 渲染骨架屏
本組件庫(kù)骨架屏的實(shí)現(xiàn)也是基于預(yù)渲染去實(shí)現(xiàn)的,有關(guān)于預(yù)渲染更詳細(xì)的介紹請(qǐng)參考這篇文章:處理 Vue 單頁(yè)面 Meta SEO的另一種思路 下面我們主要介紹其實(shí)現(xiàn)步驟,首先我們也是需要配置webpack-plugin,不過(guò)已經(jīng)有實(shí)現(xiàn)好的prerender-spa-plugin可用
var path = require('path') var PrerenderSpaPlugin = require('prerender-spa-plugin') module.exports = { // ... plugins: [ new PrerenderSpaPlugin( // Absolute path to compiled SPA path.join(__dirname, '../dist'), // List of routes to prerender ['/'] ) ] }
然后寫(xiě)好我們的骨架屏文件main.skeleton.vue
<template> <div class="main-skeleton"> <w-skeleton height="80px"></w-skeleton> <div> <div class="skeleton-container"> <div class="skeleton"> <w-skeleton height="300px"></w-skeleton> </div> <w-skeleton height="45px"></w-skeleton> </div> <div class="skeleton-bottom"> <w-skeleton height="45px"></w-skeleton> </div> </div> </div> </template>
當(dāng)初次進(jìn)入頁(yè)面的時(shí)候我們需要顯示骨架屏,數(shù)據(jù)加載完,我們需要移除骨架屏:
<template> <div id="app"> <mainSkeleton v-if="!init"></mainSkeleton> <div v-else> <div class="body"></div> </div> </div> </template> <script> import mainSkeleton from './main.skeleton.vue' export default { name: 'app', data () { return { init: false } }, mounted () { // 這里模擬數(shù)據(jù)請(qǐng)求 setTimeout(() => { this.init = true }, 250) }, components: { mainSkeleton } } </script>
ssr 渲染骨架屏
下面我用我靈魂畫(huà)師的筆法,畫(huà)出了大致的過(guò)程:
首先創(chuàng)建我們的skeleton.entry.js
import Vue from 'vue'; import Skeleton from './skeleton.vue'; export default new Vue({ components: { Skeleton }, template: '<skeleton />' });
當(dāng)然這里的skeleton.vue使我們事先寫(xiě)好的骨架屏組件,看起來(lái)可能是這樣:
<template> <div class="skeleton-wrapper"> <header class="skeleton-header"></header> <div class="skeleton-block"></div> </div> </template>
然后我們需要的是能把skeleton.entry.js編譯成服務(wù)端渲染可用的bundle文件,所以我們需要有個(gè)編譯骨架屏的webpack.ssr.conf.js文件:
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/skeleton.entry.js') }, output: Object.assign({}, baseWebpackConfig.output, { libraryTarget: 'commonjs2' }), externals: nodeExternals({ whitelist: /\.css$/ }), plugins: [] });
接下來(lái)最終的步驟,就是編寫(xiě)我們的webpackPlugin,我們期望我們的webpackPlugin可以幫我們把入口文件編譯成bundle,然后再通過(guò)vue-server-renderer來(lái)render bundle,最終產(chǎn)出響應(yīng)的html片段和css片段,這里貼出核心代碼:
// webpack start to work var serverCompiler = webpack(serverWebpackConfig); var mfs = new MFS(); // output to mfs serverCompiler.outputFileSystem = mfs; serverCompiler.watch({}, function (err, stats) { if (err) { reject(err); return; } stats = stats.toJson(); stats.errors.forEach(function (err) { console.error(err); }); stats.warnings.forEach(function (err) { console.warn(err); }); var bundle = mfs.readFileSync(outputPath, 'utf-8'); var skeletonCss = mfs.readFileSync(outputCssPath, 'utf-8'); // create renderer with bundle var renderer = createBundleRenderer(bundle); // use vue ssr to render skeleton renderer.renderToString({}, function (err, skeletonHtml) { if (err) { reject(err); } else { resolve({skeletonHtml: skeletonHtml, skeletonCss: skeletonCss}); } }); });
最后一步,我們對(duì)產(chǎn)出的html片段, css片段進(jìn)行組裝,產(chǎn)出最終的html,所以我們需要監(jiān)聽(tīng)webpack 的編譯掛載之前的事件:
compiler.plugin('compilation', function (compilation) { // add listener for html-webpack-plugin compilation.plugin('html-webpack-plugin-before-html-processing', function (htmlPluginData, callback) { ssr(webpackConfig).then(function (ref) { var skeletonHtml = ref.skeletonHtml; var skeletonCss = ref.skeletonCss; // insert inlined styles into html var headTagEndPos = htmlPluginData.html.lastIndexOf('</head>'); htmlPluginData.html = insertAt(htmlPluginData.html, ("<style>" + skeletonCss + "</style>"), headTagEndPos); // replace mounted point with ssr result in html var appPos = htmlPluginData.html.lastIndexOf(insertAfter) + insertAfter.length; htmlPluginData.html = insertAt(htmlPluginData.html, skeletonHtml, appPos); callback(null, htmlPluginData); }); }); });
github 地址: VV-UI/VV-UI
演示地址: vv-ui
文檔地址:skeleton
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Vue-cli集成axios請(qǐng)求出現(xiàn)CORS跨域問(wèn)題及解決
這篇文章主要介紹了Vue-cli集成axios請(qǐng)求出現(xiàn)CORS跨域問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,2023-10-10詳解vue-router2.0動(dòng)態(tài)路由獲取參數(shù)
本篇文章主要介紹了詳解vue-router2.0動(dòng)態(tài)路由獲取參數(shù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06vue后臺(tái)管理添加多語(yǔ)言功能的實(shí)現(xiàn)示例
這篇文章主要介紹了vue后臺(tái)管理添加多語(yǔ)言功能的實(shí)現(xiàn)示例,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下2021-04-04vue3中reactive和ref的實(shí)現(xiàn)與區(qū)別詳解
reactive和ref都是vue3實(shí)現(xiàn)響應(yīng)式系統(tǒng)的api,他們是如何實(shí)現(xiàn)響應(yīng)式的呢,reactive和ref又有什么區(qū)別呢,下面小編就來(lái)和大家詳細(xì)講講,希望對(duì)大家有所幫助2023-10-10vue項(xiàng)目打包為APP,靜態(tài)資源正常顯示,但API請(qǐng)求不到數(shù)據(jù)的操作
這篇文章主要介紹了vue項(xiàng)目打包為APP,靜態(tài)資源正常顯示,但API請(qǐng)求不到數(shù)據(jù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09Vue結(jié)合Video.js播放m3u8視頻流的方法示例
本篇文章主要介紹了Vue+Video.js播放m3u8視頻流的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05vue不操作dom實(shí)現(xiàn)圖片輪播的示例代碼
這篇文章主要介紹了vue不操作dom實(shí)現(xiàn)圖片輪播的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12解決element-ui的el-dialog組件中調(diào)用ref無(wú)效的問(wèn)題
這篇文章主要介紹了解決element-ui的el-dialog組件中調(diào)用ref無(wú)效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02vue之保留小數(shù)點(diǎn)兩位小數(shù) 使用filters(過(guò)濾器)
這篇文章主要介紹了vue之保留小數(shù)點(diǎn)兩位小數(shù) 使用filters(過(guò)濾器),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11