Vue skeleton screen implementation scheme

As one of the most mainstream frameworks in front-end, vue has a large number of users. From students’ practice projects to large-scale projects of enterprises, vue has its commendable points. But once the project becomes huge, any project will become stuck. Here is an implementation of skeleton screen, one of the solutions to optimize vue User Experience. Strictly speaking, skeleton screen is also a way to optimize the first screen experience.

Skeleton screen introduction

Skeleton screen can be understood as a blank version of the page, a simple critical rendering path, before the data is loaded in. You can take a look at the Facebook skeleton screen implementation below. You can see that before the page is fully rendered, the user will see a skeleton screen page with a simple style that depicts the general frame of the current page, and then each placeholder in the skeleton screen is completely replaced by actual resources. During this process, users will feel that the content is gradually loading and will be presented, which reduces the user’s anxiety and makes the loading process subjectively smooth.

Skeleton screen generation method

Vue-server-renderer

Handwritten HTML and CSS customize the skeleton screen for the target page. The main idea is to use vue-server-renderer, a plugin originally used for server-side rendering, to process the ‘.vue’ file we wrote as’HTML 'and insert it into the page template. Mount Point to complete the injection of the skeleton screen. This method is not very civilized. If the page style changes, you have to change the skeleton screen again, which increases the maintenance cost.

Principle

Those who have used vue know that after the vue project is packaged, there is an HTML file with an entry. There is only a div with the id of app in that file, while all other js files are actually dynamically calculated and generated HTML tags inserted into the id. The div of the app.

When we go to request the vue page, we first load the index.html file and then introduce the corresponding js code through the script tag in the html file. If these js files are too large, the download is too slow or the js running time is too long. It will lead to a white screen on the page for a long time.

So naturally think of this solution, first modify the index.html file, add the native html to the div with the id of the app as the skeleton screen first, and replace the content in the div when the js operation is completed

Implementation process

But manually writing skeleton screen in div #app is obviously unreasonable.

Since we are a vue project, skeleton screen of course also needs to be a vue file, it can be automatically injected into the div #app when the component

Create a new vue for storing skeleton screen files, such as Skeleton.vue
Create a new entry file, such as skeleton.entry.js
1
2
3
4
5
6
7
8
9
import Vue from 'vue'
import Skeleton from './Skeleton.vue'

export default new Vue({
components: {
Skeleton
},
template: '<skeleton />'
})
Use the vue-server-renderer plugin

This plugin was originally used for server-side rendering (the server level generates different html pages according to different requests and returns them to the front end, and the browser renders them directly. As opposed to front-end rendering, the front end first gets part of the page, and then gets it from the background. Get the data, and then assemble the page on the front end), but we use it here to process the vue file into html and css strings. The process is as follows:

Concrete realization

Create webpack.skeleton.conf.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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'
})
]
}

After running the following command, skeleton.json will be generated in the dist folder, which records the styles of skeleton.vue and its references. This file will be provided to vue-server-renderer for use

Create a new skeleton.js in the root directory to insert the skeleton screen into index.html.

Use images as skeleton screens

This solution is simpler than the first one, requiring only the UI personnel to draw an image, but it is more difficult to maintain than the first one.

Automatically generate skeleton screen

Automatically generate and automatically insert a static skeleton screen This method is similar to the first method, but it automatically generates a skeleton screen. You can pay attention to the open source plug-in of Ele.me page-skeleton-webpack-pluginIt generates the corresponding skeleton screen page according to different routing pages in the project, and packages the skeleton screen page into the corresponding static routing page through webpack. However, it should be noted that this plugin currently only supports routing in history mode, and does not support hash mode. And currently only supports the skeleton screen of the home page, and there is no component-level local skeleton screen implementation.

Features

Page Skeleton is a webpack plugin. The purpose of this plugin is to generate corresponding skeleton screen pages according to different routing pages in your project, and package the skeleton screen pages into the corresponding static routing pages through webpack.

  • Support multiple loading animations
  • For mobile end web pages
  • Supports multiple routes
  • Customizable, you can configure the shape color of the skeleton block through the configuration item, and you can also modify the source code of the skeleton page directly on the preview page
  • Can be used with almost zero configuration

Installation

npm install --save-dev page-skeleton-webpack-plugin

npm install --save-dev html-webpack-plugin

Use

修改build/webpack.base.config.js

This file configures the configuration of webpack packaging, including the loader, plugin, and path to the generated file of webpack packaging. This file will be referenced by the webpack.dev .config.js and webpack.prod .config.js files for packaging in Development Environment and production environment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { SkeletonPlugin } = require('page-skeleton-webpack-plugin')
const path = require('path')
const webpackConfig = {
entry: 'index.js',
output: {
path: __dirname + '/dist',
filename: 'index.bundle.js'
},
plugin: [
new HtmlWebpackPlugin({
// Your HtmlWebpackPlugin config
}),

new SkeletonPlugin({
Pathname: path.resolve (__dirname, '${customPath }`), // address to store the shell file
StaticDir: path.resolve (__dirname, './dist '), // preferably same as'output.path'
Routes : ['/', '/ search '], // Add the routes that need to generate the skeleton screen to the array
})
]
}

Since the plugin selects different operations based on NODE_ENV environment variables, you need to display the scrpt option in the package.json file to configure the environment variables as follows

1
2
3
4
"scripts": {
"dev": "cross-env NODE_ENV=development node server.js",
"build": "rm -rf dist && cross-env NODE_ENV=production webpack --progress --hide-modules"
}

The cross-env is also a plugin that can dynamically modify the value of NODE_ENV variables

Modify

Add <! – shell – > inside the root element

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
< html long = "and" >
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- shell -->
</div>
</body>
</html>
Write to skeleton page
  1. Call out the plug-in interface through Ctrl | Cmd + enter in the development page, or enter’toggleBar 'in the JavaScript Console of the browser to call out the interface.

  2. Click the button in the interactive interface to preview the skeleton page. This process may take about 20s. When the plugin is ready for the skeleton page, it will automatically open the preview page through the browser.

  3. Scan the QR code in the preview page to preview the skeleton page on the mobile phone. You can edit the source code directly on the preview page. By clicking the Write button in the upper right corner, the skeleton page is written into the shell.html file.

  4. Repackage the application through webpack. When the page restarts, you can see the skeleton structure of the application before obtaining the data.

Precautions

You may encounter a problem here, that is, the Development Environment can display, but the production environment does not generate and insert the skeleton screen. The reason may be that there is a configuration in webpack.prod, js that will eliminate all comments. This configuration will remove the <! --shell-- > tag, so the insertion point of the skeleton screen cannot be found

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
plugins: [
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
//removeComments: true, remove comments
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
]

Use vue-skeleton-webpack-plugin

It changes the way of inserting skeleton screen from manual to automatic. The principle is to use Vue pre-rendering function during construction. The rendered HTML fragment of the skeleton screen component is inserted into the Mount Point of the HTML page template, and the style is internal connection to the’head 'tag. This plugin can set different skeleton screens for different routes of a single page, or for multiple pages. At the same time, for the convenience of debugging during development, the skeleton screen will be written into the router as a route

Installation

npm install vue-skeleton-webpack-plugin

Create skeleton screen component
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<template>
<div>
<div class="skeleton">
<div class="skeleton-head"></div>
<div class="skeleton-body">
<div class="skeleton-name"></div>
<div class="skeleton-title"></div>
<div class="skeleton-content"></div>
</div>
</div>
</div>
</template>

<script>
export default {
name: 'skeleton'
};
</script>

<style scoped>
.skeleton {
padding: 15px;
}
.skeleton .skeleton-head,
.skeleton .skeleton-name,
.skeleton .skeleton-title,
.skeleton .skeleton-content,
.skeleton .skeleton-content {
background: rgb(194, 207, 214);
}
.skeleton-head {
width: 33px;
height: 33px;
border-radius: 50%;
float: left;
}
.skeleton-body {
margin-left: 50px;
}
.skeleton-name{
width: 150px;
height: 30px;
margin-bottom: 10px;
}
.skeleton-title {
width: 100%;
height: 30px;
}
.skeleton-content {
width: 100%;
height: 30px;
margin-top: 10px;
}
</style>
Create entry file for skeleton screen
1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import Skeleton from './Skeleton'
export default new Vue({
components: {
Skeleton
},
template: `
<div>
<skeleton id="skeleton1" style="display:none"/>
</div>`
})
Create webpack config file for skeleton screen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
'use strict';
const path = require('path')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const nodeExternals = require('webpack-node-externals')
const config = require('../config')
const utils = require('./utils')
const isProduction = process.env.NODE_ENV = 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap

function resolve(dir) {
return path.join(__dirname, dir)
}

let skeletonWebpackConfig = merge(baseWebpackConfig, {
target: 'node',
devtool: false,
entry: {
app: resolve('../src/entry-skeleton.js')
},
output: Object.assign({}, baseWebpackConfig.output, {
libraryTarget: 'commonjs2'
}),
externals: nodeExternals({
whitelist: /\.css$/
}),
plugins: []
})

// important: enable extract-text-webpack-plugin
//Key configuration
skeletonWebpackConfig.module.rules[0].options.loaders = utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: true
})

module.exports = skeletonWebpackConfig
分别在webpack.prod.conf.js和webpack.dev.conf.js

First, you need to introduce the vue-skeleton-webpack-plugin plugin

1
const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')

Use the plugin to add an item to the plugins array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new SkeletonWebpackPlugin({
webpackConfig: require('./webpack.skeleton.conf'),
quiet: true,
minimize: true,
router: {
mode: 'history',
routes: [
{
Path: '/client/a/Quiksns/comment',//Use route accordingly
SkeletonId: 'skeleton1'//ID of the skeleton screen used
},
]
}
}),
Verification

After starting the vue project, enter http://localhost:8080/client/a/Quiksns/comment in the browser

We can see that there will be a skeleton screen before the page is loaded. If the page loads too fast and cannot be seen clearly, you can also use the performance tab of chrome Devtools to record the operation (pay attention to check the screenshot option), and we can see each page. Frame situation.

Reference article

https://zhuanlan.zhihu.com/p/35505062

https://www.cnblogs.com/FarmanKKK/p/9712913.html