Vue page skeleton screen injection practice

  Front end, User experience, vue.js

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.

clipboard.png

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-clicollocationwebpack-simpleThis 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 devTo 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#appAfter js is executed, thisdiv#appWill 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 atNetworkFound insidethrottleFunction, 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.

hzv4.gif

Now, we have a clear idea of how to implement skeleton screen on Vue page-indiv#appThe relevant contents of the skeleton screen can be directly inserted inside.

Three, easy maintenance plan

Obviously, manually indiv#appIt 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..vueFile that can be automatically injected by the tool at build timediv#appInside.

First of all, we are in/srcCreate a new one under the directorySkeleton.vueThe 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.jsEntry 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-rendererHere we go. This plug-in was originally used for server-side rendering, but in this example, we mainly use it to.vueFile processing intohtmlAndcssString function, to complete the skeleton screen injection, process is as follows:

clipboard.png

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.jsFile, 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 atpluginsIt’s addedVueSSRServerPlugin. InVueSSRServerPlugin, specifies the json file name of its output. We can do this by running the following instructions/distDirectory to generate askeleton.jsonDocuments:

webpack --config ./webpack.skeleton.conf.js

This document records the contents and styles of the skeleton screen and will be provided tovue-server-rendererUse.

Next, create a new one under the root directory.skeleton.jsWhich will soon be used toindex.htmlInsert 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 templatehtmlFile, need to be added at the location of the written content<! --vue-ssr-outlet-->Placeholder, this example indiv#appIn 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#appBetween Of course, we can go further and compress all these contents. rewriteskeleton.jsAddhtml-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:

clipboard.png

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.ReactThe skeleton screen injection practice, please look forward to!

The article related codes have been synchronized toGithub, welcome to check ~