關(guān)于Vue單頁面骨架屏實踐記錄
關(guān)于骨架屏介紹
骨架屏的作用主要是在網(wǎng)絡(luò)請求較慢時,提供基礎(chǔ)占位,當數(shù)據(jù)加載完成,恢復(fù)數(shù)據(jù)展示。這樣給用戶一種很自然的過渡,不會造成頁面長時間白屏或者閃爍等情況。 常見的骨架屏實現(xiàn)方案有ssr服務(wù)端渲染和prerender兩種解決方案。
這里主要通過代碼為大家展示如何一步步做出這樣一個骨架屏:

prerender 渲染骨架屏
本組件庫骨架屏的實現(xiàn)也是基于預(yù)渲染去實現(xiàn)的,有關(guān)于預(yù)渲染更詳細的介紹請參考這篇文章:處理 Vue 單頁面 Meta SEO的另一種思路 下面我們主要介紹其實現(xiàn)步驟,首先我們也是需要配置webpack-plugin,不過已經(jīng)有實現(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
['/']
)
]
}
然后寫好我們的骨架屏文件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>
當初次進入頁面的時候我們需要顯示骨架屏,數(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ù)請求
setTimeout(() => {
this.init = true
}, 250)
},
components: {
mainSkeleton
}
}
</script>
ssr 渲染骨架屏
下面我用我靈魂畫師的筆法,畫出了大致的過程:

首先創(chuàng)建我們的skeleton.entry.js
import Vue from 'vue';
import Skeleton from './skeleton.vue';
export default new Vue({
components: {
Skeleton
},
template: '<skeleton />'
});
當然這里的skeleton.vue使我們事先寫好的骨架屏組件,看起來可能是這樣:
<template> <div class="skeleton-wrapper"> <header class="skeleton-header"></header> <div class="skeleton-block"></div> </div> </template>
然后我們需要的是能把skeleton.entry.js編譯成服務(wù)端渲染可用的bundle文件,所以我們需要有個編譯骨架屏的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: []
});
接下來最終的步驟,就是編寫我們的webpackPlugin,我們期望我們的webpackPlugin可以幫我們把入口文件編譯成bundle,然后再通過vue-server-renderer來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});
}
});
});
最后一步,我們對產(chǎn)出的html片段, css片段進行組裝,產(chǎn)出最終的html,所以我們需要監(jiān)聽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é)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Vue-cli集成axios請求出現(xiàn)CORS跨域問題及解決
這篇文章主要介紹了Vue-cli集成axios請求出現(xiàn)CORS跨域問題及解決方案,具有很好的參考價值,希望對大家有所幫助,2023-10-10
詳解vue-router2.0動態(tài)路由獲取參數(shù)
本篇文章主要介紹了詳解vue-router2.0動態(tài)路由獲取參數(shù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
vue3中reactive和ref的實現(xiàn)與區(qū)別詳解
reactive和ref都是vue3實現(xiàn)響應(yīng)式系統(tǒng)的api,他們是如何實現(xiàn)響應(yīng)式的呢,reactive和ref又有什么區(qū)別呢,下面小編就來和大家詳細講講,希望對大家有所幫助2023-10-10
vue項目打包為APP,靜態(tài)資源正常顯示,但API請求不到數(shù)據(jù)的操作
這篇文章主要介紹了vue項目打包為APP,靜態(tài)資源正常顯示,但API請求不到數(shù)據(jù)的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Vue結(jié)合Video.js播放m3u8視頻流的方法示例
本篇文章主要介紹了Vue+Video.js播放m3u8視頻流的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
解決element-ui的el-dialog組件中調(diào)用ref無效的問題
這篇文章主要介紹了解決element-ui的el-dialog組件中調(diào)用ref無效的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02
vue之保留小數(shù)點兩位小數(shù) 使用filters(過濾器)
這篇文章主要介紹了vue之保留小數(shù)點兩位小數(shù) 使用filters(過濾器),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11

