During the optimization of the project recently, in addition to the overall architecture change, we found that the problem of white screen on the home page was very obvious every time when loading.
Why is there a blank screen
React, Vue and Angular are the dominant front-end frameworks, and most front-end applications in the market are based on these frameworks or libraries. These three frameworks have a common feature: they are all JS-driven, and the page does not display any content until the JS code is parsed. The so-called white screen.
Users hate to see a blank screen that shows nothing, and they are likely to suspect that something is wrong with the network or application. Vue, for example, converts data and computed state values in components to SET and GET access properties using object. defineProperty to listen for data changes when the application starts. All of this is done at startup time, which inevitably results in a slower page startup than a non-JS-driven (such as jQuery) page.
So our home page is a typical case, in one of our front-end weekly meeting, our boss asked us a question, how to give users a better experience.
SSR
My first reaction at this time was to try not to have a blank screen, you can use VueSSR, which is the server side straight out of the page.
First, we learned that server-side rendering has two main purposes, one is SEO, and the other is to speed up the presentation of content. In these two benefits at the same time, we also need to assess the cost of the service side rendering, first of all, we need support from the server, thus involves service building, deploying, and so on, at the same time the web project is a large flow of website, also need to consider the load on the server, and the corresponding caching policy, especially as our industry, because of the different geographical position, Different users see different pages, which is called a thousand pages, which also makes caching difficult.
pre-rendered
Pre-render is the process by which a page is rendered during the build of a project using a rendering mechanism such as Puppeteer or JsDOM, and then inserted into THE HTML so that the page is pre-rendered before it is launched.
But it will eventually abandoned, pre-rendered rendered page data is in the process of building has been packaged into HTML, when the real access page, real data may have and pre-rendered data have great discrepancy, and pre-rendered page is an interactive page, the page didn’t start before, Users cannot interact with the pre-rendered page, and the data in the pre-rendered page will affect users’ access to real information, and even lead users to make some wrong decisions when it comes to some prices, amounts and geographical locations.
Skeleton diagram
Skeleton Page is when you open a mobile Web Page and show the user a rough look of the Page before the Page is parsed and the data loaded. In skeleton pages, images, text, and ICONS are displayed as gray rectangles or circles, and the user gets a sense of the basic CSS style and layout of the page that is about to load before the actual page is displayed.
Skeleton screen first experience
At first, I thought of the skeleton screen as a page to write a CSS and Html, or let the UI design a skeleton. But there are downsides to this. What if the product changes its requirements, not only to the code, but also to the skeleton page or skeleton diagram?
Later, WHEN I saw the article of Ele. me and the mature product Page-skeleton-webpack-plugin, I suddenly understood the difference between me and Big guy
Analysis – Ele. me skeleton screen
The basic scheme for generating skeleton pages
Puppeteer controls Headless Chrome on the server side to open the page that needs to generate skeleton pages in development. After the page loading and rendering is completed, the existing elements are covered by overlapping styles by deleting or adding elements in the page on the premise of retaining the page layout style. In this way, the display of hidden images, text and images without changing the page layout is covered by styles, making them displayed as gray blocks. Then extract the modified HTML and CSS styles, and you have a skeleton page.
Before we go into the details of the skeleton generation page, let’s take a look at puppeteer, as it’s described on GitHub.
Puppeteer is a Node library that provides a high-level API for controlling Chromium or Chrome via the DevTools protocol.
The Puppeteer API is hierarchical and reflects the browser structure. To be honest, this is my first time to contact the Node library, I just started to use the installation encountered a lot of holes, hahaha hahaha, embarrassed.
For those who want to know more about puppetter, see puppetter Installation – The Solution
Skeleton screen development basically started with this Node library. Let’s take a look at the underlying code
skeleton.js
const puppeteer = require('puppeteer')
const devices = require('puppeteer/DeviceDescriptors') // Puppeteer provides puppeteer () const {sleep, genScriptContent} = require('./util/utils'// Public tool method const scriptFns = require('./util/browserUtils')
const skeleton = async function(url, option = {}) {
const defaultOption = {
device: 'iPhone 6'} const {device, defer = 0, // Delay time remove = [], // page does not want to remove class name array excludes = [], Launch: launch: launch: launch: launch: launch: launchOpt } = Object.assign({}, defaultOption, Option) // Puppeteer will create a Browser object via puppeteer.launch or puppeteer.connect when Puppeteer connects to a Chromium instance. Return a new [Page] object. [Page] is created in a default browser context. Launch (launchOpt) const page = await browser.newpage () // create a newPage /** * according to the specified parameters and user Agent generates the simulator. * @param {options} * viewPort <[Object]> width <[number]> width of the page, in pixels. DeviceScaleFactor <[number]> defines device scaling, (similar to DPR). 1 by default. IsMobile <[Boolean]> Whether to include the Meta ViewPort tag. The defaultfalse. HasTouch <[Boolean]> Specifies whether the terminal supports touch. The defaultfalseIsLandscape <[Boolean]> specify whether the terminal isLandscape mode. The defaultfalse. UserAgent <[string]> * */ await Page. emulate(devices[device]) await page.goto(URL) // Apply some utils AddScriptTag ({content: genScriptContent(... Note also that the defer configuration tells Puppeteer how long to wait after opening the page, because some of the content in the page has not actually loaded after opening the page in development, and if the skeleton page is generated before that, It is possible that the resulting skeleton page will not look like the real page. Failed to generate the skeleton page. **/ await sleep(defer) /** * page.evaluate(pageFunction, ... args) * pageFunction <[function[string]] | > method to be executed in page instance context... args <... [Serializable] | [JSHandle] > to return to pageFunction parameters: <[Promise]<[Serializable]>> pageFunction implement result */ const HTML = await page. Evaluate (async (remove, excludes, hide ) => { const $ = document.querySelectorAll.bind(document)if (remove.length) {
const removeEle = $(remove.join(', '))
Array.from(removeEle).forEach(ele => ele.parentNode.removeChild(ele))
}
if (hide.length) {
const hideEle = $(hide.join(', '))
Array.from(hideEle).forEach(ele => ele.style.opacity = 0)
}
const excludesEle = excludes.length ? Array.from($(excludes.join(', '))) : []
await traverse(document.documentElement, excludesEle)
return document.documentElement.outerHTML
}, remove, excludes,hide)
// browser.close()
return { html }
}
module.exports = skeleton
Copy the code
In the next post, we’ll take a closer look at how the skeleton screen divides pages into blocks based on different elements: After separating the elements into different blocks, the next step is to process each block separately, including adding and subtracting elements and overwriting styles, with the sole purpose of converting these blocks into skeleton page styles.
Ran Luo – A solution for automatically generating skeleton screens