As the front-end developer who has the closest contact with users, user experience is the most noteworthy issue. Regarding the display of the loading status of the page, there are mainly two kinds of mainstream: the loading graph and the progress bar. In addition, more and more APP uses “skeleton screen” to display unloaded content, giving users a brand-new experience. With the popularity of SPA in the front end, the problem of loading the first screen is also bothering developers. So is there a way to let SPA use skeleton screen? This is what this article will discuss.
The article related codes have been synchronized toGithub, welcome to check ~
First, what is the skeleton screen
In a nutshell, the skeleton screen is to use some graphics to take place when the content of the page is not loaded, and then replace it after the content is loaded.
This technology is widely used in some content-based APP and web pages. Next, we will take a simple Vue project as an example to explore how to implement skeleton screen in SPA project based on Vue.
Second, analyze the content loading process of Vue page
For the sake of simplicity, we usevue-cli
collocationwebpack-simple
This template is used to create a new project:
vue init webpack-simple vue-skeleton
At this time, we got a basic Vue project:
.
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ └── main.js
├── index.html
└── webpack.conf.js
Once the dependencies are installed, they can be passed throughnpm run dev
To run this project. However, before running the project, let’s look at what is written in the html file of the portal.
<! 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>
As you can see, there is only one DOMdiv#app
After js is executed, thisdiv#app
Will beReplace the wholeTherefore, we can do some experiments and add some contents to this div:
<div id="app">
<p>Hello skeleton</p>
<p>Hello skeleton</p>
<p>Hello skeleton</p>
</div>
Open chrome’s developer tool atNetwork
Found insidethrottle
Function, adjust the network speed to “Slow 3G”, refresh the page, and you can see that the page first shows three sentences of “Hello skeleton”, which will be replaced by the original contents after js is loaded.
Now, we have a clear idea of how to implement skeleton screen on Vue page-indiv#app
The relevant contents of the skeleton screen can be directly inserted inside.
Three, easy maintenance plan
Obviously, manually indiv#app
It is unscientific to write the contents of skeleton screen in it. We need an easy maintenance scheme with strong expansibility and automation. Since it is in Vue project, we certainly hope that the so-called skeleton screen is also one..vue
File that can be automatically injected by the tool at build timediv#app
Inside.
First of all, we are in/src
Create a new one under the directorySkeleton.vue
The document reads as follows:
<template>
<div class="skeleton page">
<div class="skeleton-nav"></div>
<div class="skeleton-swiper"></div>
<ul class="skeleton-tabs">
<li v-for="i in 8" class="skeleton-tabs-item"><span></span></li>
</ul>
<div class="skeleton-banner"></div>
<div v-for="i in 6" class="skeleton-productions"></div>
</div>
</template>
<style>
.skeleton {
position: relative;
height: 100%;
overflow: hidden;
padding: 15px;
box-sizing: border-box;
background: #fff;
}
.skeleton-nav {
height: 45px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-swiper {
height: 160px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-tabs {
list-style: none;
padding: 0;
margin: 0 -15px;
display: flex;
flex-wrap: wrap;
}
.skeleton-tabs-item {
width: 25%;
height: 55px;
box-sizing: border-box;
text-align: center;
margin-bottom: 15px;
}
.skeleton-tabs-item span {
display: inline-block;
width: 55px;
height: 55px;
border-radius: 55px;
background: #eee;
}
.skeleton-banner {
height: 60px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-productions {
height: 20px;
margin-bottom: 15px;
background: #eee;
}
</style>
Next, build another oneskeleton.entry.js
Entry file:
import Vue from 'vue'
import Skeleton from './Skeleton.vue'
export default new Vue({
components: {
Skeleton
},
template: '<skeleton />'
})
After the preparation of the skeleton screen is completed, it is the turn of a key plug-invue-server-renderer
Here we go. This plug-in was originally used for server-side rendering, but in this example, we mainly use it to.vue
File processing intohtml
Andcss
String function, to complete the skeleton screen injection, process is as follows:
IV. Implementation of the Plan
According to the flow chart, we also need to create a new one in the root directory.webpack.skeleton.conf.js
File, specially used for skeleton screen construction.
const path = require('path')
const webpack = require('webpack')
const nodeExternals = require('webpack-node-externals')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
module.exports = {
target: 'node',
entry: {
skeleton: './src/skeleton.entry.js'
},
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: '[name].js',
libraryTarget: 'commonjs2'
},
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'
})
]
}
It can be seen that this configuration file is basically identical to the ordinary configuration file, and the main difference is that ittarget: 'node'
, configured withexternals
, and atplugins
It’s addedVueSSRServerPlugin
. InVueSSRServerPlugin
, specifies the json file name of its output. We can do this by running the following instructions/dist
Directory to generate askeleton.json
Documents:
webpack --config ./webpack.skeleton.conf.js
This document records the contents and styles of the skeleton screen and will be provided tovue-server-renderer
Use.
Next, create a new one under the root directory.skeleton.js
Which will soon be used toindex.html
Insert the skeleton screen inside.
const fs = require('fs')
const { resolve } = require('path')
const createBundleRenderer = require('vue-server-renderer').createBundleRenderer
//Read' skeleton.json' and write content using' index.html 'as template
const renderer = createBundleRenderer(resolve(__dirname, './dist/skeleton.json'), {
template: fs.readFileSync(resolve(__dirname, './index.html'), 'utf-8')
})
//Write (replace) `index.html' the contents completed by the previous template
renderer.renderToString({}, (err, html) => {
fs.writeFileSync('index.html', html, 'utf-8')
})
Note that as a template
html
File, need to be added at the location of the written content<! --vue-ssr-outlet-->
Placeholder, this example indiv#app
In writing:<div id="app"> <! --vue-ssr-outlet--> </div>
Next, just runnode skeleton.js
, can complete the skeleton screen injection. The operation effect is as follows:
<html lang="en">
<head>
<meta charset="utf-8">
<title>vue-skeleton</title>
<style data-vue-ssr-id="742d88be:0">
.skeleton {
position: relative;
height: 100%;
overflow: hidden;
padding: 15px;
box-sizing: border-box;
background: #fff;
}
.skeleton-nav {
height: 45px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-swiper {
height: 160px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-tabs {
list-style: none;
padding: 0;
margin: 0 -15px;
display: flex;
flex-wrap: wrap;
}
.skeleton-tabs-item {
width: 25%;
height: 55px;
box-sizing: border-box;
text-align: center;
margin-bottom: 15px;
}
.skeleton-tabs-item span {
display: inline-block;
width: 55px;
height: 55px;
border-radius: 55px;
background: #eee;
}
.skeleton-banner {
height: 60px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-productions {
height: 20px;
margin-bottom: 15px;
background: #eee;
}
</style></head>
<body>
<div id="app">
<div data-server-rendered="true" class="skeleton page"><div class="skeleton-nav"></div> <div class="skeleton-swiper"></ div> <ul class="skeleton-tabs"><li class="skeleton-tabs-item"><span></span></li><li class="skeleton-tabs-item"><span></span></ li><li class="skeleton-tabs-item"><span></span></li><li class="skeleton-tabs-item"><span></span></li><li class="skeleton-tabs-item"><span></span></ li><li class="skeleton-tabs-item"><span></span></li><li class="skeleton-tabs-item"><span></span></li><li class="skeleton-tabs-item"><span></span></ li></ul> <div class="skeleton-banner"></div> <div class="skeleton-productions"></div><div class="skeleton-productions"></ div><div class="skeleton-productions"></div><div class="skeleton-productions"></div><div class="skeleton-productions"></div><div class="skeleton-productions"></div></div>
</div>
<script src="/dist/build.js"></script>
</body>
</html>
As you can see, the style of the skeleton screen passes through<style></style>
The tag is inserted directly and the contents of the skeleton screen are also placed on thediv#app
Between Of course, we can go further and compress all these contents. rewriteskeleton.js
Addhtml-minifier
:
...
+ const htmlMinifier = require('html-minifier')
...
renderer.renderToString({}, (err, html) => {
+ html = htmlMinifier.minify(html, {
+ collapseWhitespace: true,
+ minifyCSS: true
+ })
fs.writeFileSync('index.html', html, 'utf-8')
})
To see the effect:
The effect is very good! So far, Vue page access to skeleton screen has been fully realized.
The end
This article has implemented a set of simplest Vue page skeleton screen injection practices, if you want to see more complex examples, you can refer to“Adding Skeleton Screen to vue Project”This article, many thoughts of this article are also inspired by it, which is very worth reading.
If there is any better way to realize it, please also discuss with me. I will also summarize it when I have the chance.React
The skeleton screen injection practice, please look forward to!
The article related codes have been synchronized toGithub, welcome to check ~