Killblanks use pre-render and skeleton screen to solve page blanks
Published: 2021-03-08 11:38:18

# Preface

> White screens have always been a major problem that has plagued the front-end when the CSR project was born. How to increase the waiting time of users, reduce the bounce rate, and improve the page performance under the condition of low cost is the front-end has been solving problems. This kind of solution directly generates a skeleton screen from the page node, and pre-renders the user to display the outline of the content while waiting for the content to load, providing a better user experience and making the content feel faster.

![](https://camo.githubusercontent.com/1ff3890ec174e86b4c33565a7dfc64798e31ced8834abdcce8c10669d979a29d/68747470733a2f2f70392d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f33633234306432376437343834633339623435346132336437383034343463647e74706c762d6b3375316662706663702d77617465726d61726b2e696d616765)

# How does the white screen come about

This is a screenshot of the `Chrome performance` of a normal CSR project. As you can see from the snapshot, the time for the white screen is about 330ms.

The execution logic of the page during this time:

* Request HTML -> Wait for HTML document -> Parse HTML -> `Request JS and CSS resources` \-> `Execute vendors and index.js logic`

After 330ms, the page appears in a general frame, the execution logic of the page:

* `vue render each component` \-> `mounted page mount` \-> call the business API request data -> render the returned data -> complete page

It is precisely because the CSR project spends a lot of time in the processes of `waiting for file loading`, `CSSOM construction`, `JS parsing`, etc., that the user will be in a state of gray and white screens that are not interactive for a long time, that is, blanks.

**For details, please see the article of Hanyang** [**Pre-rendering during construction: Practice of first frame optimization of web pages**](https://tech.meituan.com/2018/11/15/first-contentful-paint-practice.html)

# What will the white screen affect

* [Why Web Performance Matters: Is Your Site Driving Customers Away?](http://www.mcrinc.com/documents/newsletters/201110_why_web_performance_matters.pdf)

![](https://camo.githubusercontent.com/bba6f237e9b151e306ffb640fc89ac831bf82ae8a59a1ac32a7d5352f05031a2/68747470733a2f2f70332d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f30363730333330663639333334616661623835383432343562306532663836397e74706c762d6b3375316662706663702d77617465726d61726b2e696d616765)

>By analyzing more than 150 websites and page abandonment data of 150 million page visits, Gomez discovered the lack of visitor loyalty. The page response time increased from 2 seconds to 10 seconds, and the page bounce rate increased by 38%.

To put it simply, each additional 1s of page waiting time is a decrease in the bounce rate and transaction rate for the app. For the boss, it is "losing money", and for you, it is impossible to increase the salary.

# Currently commonly used white screen solutions

|Scheme|[Pre-rendering](https://github.com/chrisvfritz/prerender-spa-plugin)|[SSR](https://ssr.vuejs.org/zh/)|[NSR](https:// www.infoq.cn/article/9ukos4xh_6wl4fh1fogl)|
|:-|:-|:-|:-|
|Advantages|Does not rely on data|SEO friendly, easy to build, FMP is faster than pre-rendering|SEO friendly, high first screen performance, FMP is the fastest|
|Disadvantages|SEO is not friendly, FMP is slow|High cost, high front-end load pressure|High cost, high client pressure|

Both SSR and NSR are very good solutions to the white screen, but the shortcomings are also obvious. The cost is too high. SSR relies heavily on the stability of the service. For small and medium-sized companies, there are few resources to provide a stable server for the front end. In the environment, once the node fails, the loss is great, and the CSR solution is very costly for the client and there are many uncertainties.

# Pre-rendering is not a silver bullet

Based on this, `pre-rendering` may be the simplest and most practical solution, but `pre-rendering` is not a silver bullet, let’s take a look at the first version of our team’s implementation of `pre-rendering`

![](https://camo.githubusercontent.com/19150385f86bb2f5b8e3d20cf3a500951d9708ae438b0094213c51b440e821ab/68747470733a2f2f70312d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f34356361363465366330326334336161613537636631626133356232346362357e74706c762d6b3375316662706663702d77617465726d61726b2e696d616765)

When you happily hand over the hard-made pre-rendering to the visual inspection, you may get just a word `ugly` :(

That's right, `ugly` or `unfriendly`, `feeling that the internet speed is very slow`, these are the reactions of colleagues who have seen the first version. Why is this happening?

It can be found by comparing the two screenshots

- 1. The picture is not loaded, causing a large area of ​​color blocks on the page
- 2. The back-end request is not returned, which also caused the vacancy of the page and even the deformation of the frame

We found that the problem is mainly in the placeholder element. If the element can be placed in advance, the pre-rendering can render a very beautiful page, so is there any placeholder element that is `convenient` `fast` `good-looking`?

When I searched for a solution, I found that someone had proposed a placeholder solution.

[A scheme for automatically generating skeleton screens](https://github.com/Jocs/jocs.github.io/issues/22)

# Skeleton screen

Yes, it is [@Jocs](https://github.com/Jocs), who shared [A scheme for automatically generating skeleton screens](https://github.com/Jocs/jocs.github .io/issues/22), and the subsequent open source [page-skeleton-webpack-plugin](https://github.com/ElemeFE/page-skeleton-webpack-plugin), providing a lower cost solution The plan of the skeleton screen provides a new idea for the realization of the skeleton screen. For the specific plan, please see his article

It is a pity that the `page-skeleton-webpack-plugin` has not been updated and maintained for three years. Although it is a very feasible solution, there is still a lot of room for improvement, so I decided to use the `page-skeleton-webpack-plugin` On the basis of, provide a more convenient solution, this is the origin of [Killblanks](https://warpcgd.github.io/killblanks/)

# [Killblanks](https://warpcgd.github.io/killblanks/)

This project officially started in 2020. After half a year of debugging and development, it finally successfully passed the `abtest` test and went live. And in February 2021 [open source](https://github.com/warpcgd/killblanks), thanks to everyone who paid for it.

![](https://camo.githubusercontent.com/11f2b86fbf01ecc07f19389ddb5d8e56b5367c22840cd39e0ab21f2fd537152d/68747470733a2f2f70362d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f31373938353764613137313834376462613739623330346261393437613364357e74706c762d6b3375316662706663702d77617465726d61726b2e696d616765)

# Principle

Use `Purpeteer` to simulate the browser request page function, and load the Google plug-in [@killblanks/skeleton-ext](https://warpcgd.github.io/killblanks/en/guides/skeleton-ext/) Generate the page of the skeleton screen component, straight out the `html` file

# Frame

![](https://camo.githubusercontent.com/4c74720519703c4df8b065d7f41350336a66e4a96a48f540f24a817bd67b5e49/68747470733a2f2f70392d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f37386366613134616466646534646264393433396135656131393439623639357e74706c762d6b3375316662706663702d77617465726d61726b2e696d616765)

Currently [Killblanks](https://warpcgd.github.io/killblanks/) is built on [lerna](https://github.com/lerna/lerna) and consists of three core functions

* [@killblanks/prerender](https://warpcgd.github.io/killblanks/guides/prerender/) Provides pre-rendering capabilities, reads web content through `Purpeteer`, and outputs HTML directly
* [@killblanks/skeleton](https://github.com/warpcgd/killblanks/tree/main/packages/skeleton) Modified the skeleton code of [page-skeleton-webpack-plugin](https://github.com/ElemeFE /page-skeleton-webpack-plugin) and fixed some of the bugs
* [@killblanks/skeleton-ext](https://warpcgd.github.io/killblanks/guides/skeleton-ext/) Google plug-in for skeleton to facilitate debugging and output of skeleton screen components

# Use

Killblanks is very convenient to use, if you understand the principle, you can get started in three minutes

### 1. Installation

      yarn add @killblanks/prerender -D

### 2. Configuration

    // webpack.config.js
    const prerender = require('@killblanks/prerender')
    
    export default {
      ...
      plugins: [new prerender()]
      ...
    }

* For detailed steps, please see [@killblanks/prerender](https://warpcgd.github.io/killblanks/guides/prerender/)

### 3. Use @killblanks/skeleton-ext

* For detailed steps, please see [@killblanks/skeleton-ext](https://warpcgd.github.io/killblanks/guides/skeleton-ext/)

### 4. Use the generated skeleton screen components in the project

* For example, like what is done in [DEMO](https://github.com/warpcgd/killblanks/blob/main/packages/docs%26demo/docs/.vuepress/components/effect/basic/index.vue)

```js
    // index.vue
    <template>
      <div class="container">
        <skeleton :show="!!filterProductList.length">
          <div class="productionList">
            <div v-for="(item, key) in filterProductList" :key="item.goods_id + key" class="production">
              xxx
            </div>
          </div>
        </skeleton>
      </div>
    </template>
    
    <script>
    import skeleton from './skeleton'
    export default {
      components: {
        skeleton
      },
      data: () => {
        return {
          filterProductList: []
        }
      },
      mounted() {
        setTimeout(() => {
          const res = JSON.parse(
            `{"goods_id":"5e7d6d331d41c801b95f594f","name":"skeleton-test","photo":"https://o-static.ihago.net/ikxd/e62403ac0d365c57b4dbc1a0ab7e9cf4/128.png","svga_photo":"","tag":"new","type":1,"type":1805,"real_price":199,"price":299,"discount":8000,"update_time":1594695268}`
          )
          this.filterProductList = Array(10).fill(res)
        }, 3000)
      }
    }
    </script>
```

```js
    // skeleton.vue
      <script>
    import Vue from 'vue'
    const skeletonLoader = {
      name: 'skeletocnLoader',
      functional: true,
      props: {
        show: {
          type: Boolean,
          default: false
        }
      },
      render(h, context) {
        const { show } = context.props
        if (!show || window.__PRERENDER_INJECTED__) {
          const html = `<div>xxx</div>`
          const component = Vue.compile(html)
          return h(component)
        } else {
          return context.children[0]
        }
      }
    }
    export default skeletonLoader
    </script>
```
### 5. Enter PRERENDER_SKELETON in the browser's console
```sh
    Enter `PRERENDER_SKELETON` in the Chrome console to start the skeleton screen preview
```

# Final effect

![](https://o-static.ihago.net/skeleton/d50dd0e429df79ae5a2f17e55e519392/preview.gif)

# Performance

Finally, look at the `abtest` test, [Killblanks](https://warpcgd.github.io/killblanks/) brought about the changes in page performance data

### Data Sources

Use the company's online activities in Indonesia to conduct abtest to obtain relevant data

### Data

|type|total|fcp|lcp|
|:-|:-|:-|:-|
|@killblanks|1532|536ms|661ms|
|Normal|1730|990ms|993ms|

### [First-contentful-paint(fcp)](https://github.com/w3c/paint-timing)

![](https://camo.githubusercontent.com/536effa7f6f206efc51ca55e3b1d642855b9c6d5918c2e06ad206d7c484da685/68747470733a2f2f70332d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f62393535643866333864346134326632626565306637663230373434643131647e74706c762d6b3375316662706663702d77617465726d61726b2e696d616765)

* FCP average comparison: 536: 990 @killblanks can increase `454ms`, an average increase of `45%`

### [Largest-contentful-paint(lcp)](https://web.dev/optimize-lcp/)

![](https://camo.githubusercontent.com/18a8945f053dd12ed34dbce616766d509fec68e91ff9593285ec1d89f14ae792/68747470733a2f2f70392d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f62393535643866333864346134326632626565306637663230373434643131647e74706c762d6b3375316662706663702d77617465726d61726b2e696d616765)

* LCP average comparison: 661: 993 @killblanks can increase `332ms`, an average increase of `33.4%`

# Future

killblanks There are still many functions to be improved in the future, which is currently being done

* unit test
* Optimize documentation
* Support react && angular
* Complete demo

Welcome to the front-end friends to leave your own opinions in [issue](https://github.com/warpcgd/killblanks), If you have time, please [killblanks](https://github.com/warpcgd/ killblanks) Click ⭐.