关于Vue单页面骨架屏实践记录

作者:monkeyWang 时间:2024-05-09 15:14:23 

关于骨架屏介绍

骨架屏的作用主要是在网络请求较慢时,提供基础占位,当数据加载完成,恢复数据展示。这样给用户一种很自然的过渡,不会造成页面长时间白屏或者闪烁等情况。 常见的骨架屏实现方案有ssr服务端渲染和prerender两种解决方案。

这里主要通过代码为大家展示如何一步步做出这样一个骨架屏:

关于Vue单页面骨架屏实践记录

prerender 渲染骨架屏

本组件库骨架屏的实现也是基于预渲染去实现的,有关于预渲染更详细的介绍请参考这篇文章:处理 Vue 单页面 Meta SEO的另一种思路 下面我们主要介绍其实现步骤,首先我们也是需要配置webpack-plugin,不过已经有实现好的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>

当初次进入页面的时候我们需要显示骨架屏,数据加载完,我们需要移除骨架屏:


<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 () {
// 这里模拟数据请求
setTimeout(() => {
this.init = true
}, 250)
},
components: {
mainSkeleton
}
}
</script>

ssr 渲染骨架屏

下面我用我灵魂画师的笔法,画出了大致的过程:

关于Vue单页面骨架屏实践记录

首先创建我们的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编译成服务端渲染可用的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,最终产出响应的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});
 }
});
});

最后一步,我们对产出的html片段, css片段进行组装,产出最终的html,所以我们需要监听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

来源:https://segmentfault.com/a/1190000012403177

标签:vue,骨架屏,单页面
0
投稿

猜你喜欢

  • SQL Server 数据库管理常用的SQL和T-SQL语句

    2024-01-27 22:01:09
  • python实现简单文件读写函数

    2023-08-29 04:37:24
  • webstorm添加vue.js支持的方法教程

    2024-04-30 10:16:14
  • vue+element upload上传带参数的实例

    2024-05-09 09:32:56
  • Mysql基础知识点汇总

    2024-01-23 08:15:40
  • Python解释器及PyCharm工具安装过程

    2021-02-23 08:21:51
  • Django REST Framework 分页(Pagination)详解

    2022-07-25 04:21:06
  • 浅谈Python中的异常和JSON读写数据的实现

    2021-02-07 10:41:16
  • 用python开发一款操作MySQL的小工具

    2024-01-26 08:55:19
  • Python json解析库jsonpath原理及使用示例

    2022-05-18 09:43:00
  • python中将zip压缩包转为gz.tar的方法

    2022-02-28 18:50:49
  • 基于OpenCV和Gradio实现简单的人脸识别详解

    2022-10-21 22:59:11
  • python读取excel进行遍历/xlrd模块操作

    2022-11-09 18:44:51
  • vue2中基于vue-simple-upload实现文件分片上传组件功能

    2024-05-09 15:23:25
  • flask 实现上传图片并缩放作为头像的例子

    2021-09-08 06:32:42
  • 深入理解Python中变量赋值的问题

    2023-03-20 00:28:07
  • Bootstrap Multiselect 常用组件实现代码

    2024-05-21 10:14:36
  • Python在centos7.6上安装python3.9的详细教程(默认python版本为2.7.5)

    2022-05-09 18:58:43
  • Python列表推导式与生成器表达式用法示例

    2023-05-31 01:30:40
  • PHP iconv 解决utf-8和gb2312编码转换问题

    2024-04-29 13:56:58
  • asp之家 网络编程 m.aspxhome.com