title: Prerender

Prerender is a technology provided by Taro CLI to improve the rendering speed of page initialization on the mini program side. It is implemented on the same principle as Server-side Rendering: the initialized state of the page is rendered directly as a stateless (dataless) wxml, and the rendering is performed before the framework and business logic run process. The initial rendering of a Prerender page is usually the same or faster than a native mini program.

Why Prerender?

Taro Next goes through the following steps when a page is loaded.

  1. the framework (React/Nerv/Vue) renders the page into the virtual DOM
  2. Taro runtime serializes the page’s virtual DOM into renderable data and uses setData() to drive page rendering
  3. the mini program itself renders the serialized data

Compared to native mini program or compiled mini program frameworks, steps 1 and 2 are redundant. If there are no performance issues with the business logic code of the page, most of the performance bottlenecks are in setData() in step 2: since the initial rendering is the entire virtual DOM tree of the page, the amount of data is relatively large, so setData() needs to pass a relatively large amount of data, resulting in a white screen time when initializing the page. This usually happens when the number of wxml nodes for initial rendering of a page is large or when the user’s machine performance is low.

Using Prerender

Using Prerender is very simple, you can find the config folder in the root of your project and change any of the three [project configurations] index.js/dev.js/prod.js depending on your project (. /config.md), the Taro CLI will automatically start prerender at build time based on your configuration: index.js/dev.js/prod.js.

``js title="/config/index.js 或 /config/dev.js 或 /config/prod.js " const config = { ... mini: { prerender: { match: 'pages/shop/**', // All pages starting withpages/shop/participate in prerender include: ['pages/any/way/index'], //pages/any/way/indexwill include prerender exclude: ['pages/shop/index/index'] //pages/shop/index/index` will not prerender } } };

module.exports = config

  1. The complete Prerender configuration can be found in the following table:
  2. | Parameters | Type | Default | Required | Description |
  3. | ------------ | ------------ | ------------ | ------------ | ------------ |
  4. | match | `string` `string[]` | | NO | glob string or an array of glob strings, pages that match this parameter will be added to prerender |
  5. | include | `Array<string>` `Array<PageConfig>` | `[]` | NO | Page paths that exactly match the string in the array are added to prerender
  6. | exclude | `string[]` | `[]` | NO | Page paths that are identical to the string in the array **will not** be added to prerender
  7. | mock | `Record<string, unknown>` | | NO | Global variables running in the prerender environment, with the key name as the variable name and the key value as the variable value
  8. | console | `boolean` | `false` | NO | Whether the `console` print statement is executed in the prerender process
  9. | transformData | `Function` | | NO | Custom virtual DOM tree processing function, the return value of the function will be used as an argument to `transformXML`
  10. | transformXML | `Function` | | NO | Custom XML processing function that returns the wxml to be rendered by the end of the Taro runtime initialization
  11. Types useful in the table.
  12. ```typescript
  13. // PageConfig is the page parameter configured by the developer in prerender.includes
  14. interface PageConfig {
  15. path: string // page path
  16. params: Record<string, unknown> // page's routing parameters. Corresponding to `getCurrentInstance().router.params`
  17. }
  18. // DOM tree data, which Taro renders dynamically by traversing it
  19. interface MiniData {
  20. ["cn" /* ChildNodes */]: MiniData[]
  21. ["nn" /* NodeName */]: string
  22. ["cl" /* Class */]: string
  23. ["st" /* Style */]: string
  24. ["v" /* NodeValue */]: string
  25. uid: string
  26. [prop: string]: unknown
  27. }
  28. type transformData = (data: MiniData, config: PageConfig) => MiniData
  29. type transformXML = (
  30. data: MiniData,
  31. config: PageConfig,
  32. xml: string //xml strings that have already been processed by the built-in xml conversion function
  33. ) => string

All configuration options for Prerender are optional, in most cases you only need to focus on match, include and exclude, match and include are filled in at least once to match a pre-rendered page, all three can coexist, and when there is a match conflict the priority is match < include < exclude.

As with all technologies, Prerender is not a silver bullet, and the following trade-offs or limitations apply when using Prerender.

  • The size of the page packing will increase. prerender is essentially a space-for-time technique, and the amount of increase in size depends on the amount of pre-rendered wxml.
  • Until the real DOM and events are mounted by the Taro runtime (a process known as hydrate in server-side rendering), the pre-rendered pages do not do anything accordingly.
  • Prerender does not perform lifecycles such as componentDidMount()(React)/ready()(Vue), in line with server-side rendering. If there is a need to process data, you can advance the lifecycle to static getDerivedStateFromProps()(React) or created()(Vue).

Advanced Instructions And Use

PRERENDER Global Variable

There is a global variable named PRERENDER in the pre-rendering container, which has the value true. You can write separate business logic for the pre-rendering period by determining whether this variable exists or not:

  1. if (typeof PRERENDER !== 'undefined') { // The following code will only be executed in pre-rendering
  2. // do something
  3. }

disablePrerender

For any native component that does not need to be displayed in Prerender time, you can set the component’s disablePrerender property to true and neither the component nor its descendants will be rendered as wxml strings.

  1. /* The component with id test and its descendants are not displayed during pre-rendering */
  2. <View id="test" disablePrerender>
  3. ...children
  4. </View>

Custom Rendering

When the default pre-rendered results do not meet your expectations, Taro provides two configuration items to customize the pre-rendered content.

transformData() in the Prerender configuration operates on the virtual DOM to be rendered.

  1. const config = {
  2. ...
  3. mini: {
  4. prerender: {
  5. match: 'pages/**',
  6. tranformData (data, { path }) {
  7. if (path === 'pages/video/index') {
  8. // If the page is 'page/video/index' the page is only pre-rendered with a video component
  9. // For the data structure of data, see the data type signature above
  10. data.nn = 'video'
  11. data.cn = []
  12. data.src = 'https://v.qq.com/iframe/player.html?vid=y08180lrvth&tiny=0&auto=0'
  13. return data
  14. }
  15. return data
  16. }
  17. }
  18. }
  19. }

The transformXML() in the Prerender configuration allows you to customize the pre-rendered output wxml.

  1. const config = {
  2. ...
  3. mini: {
  4. prerender: {
  5. match: 'pages/**',
  6. tranformXML (data, { path }, xml) {
  7. if (path === 'pages/video/index') {
  8. // If the page is 'page/video/index' the page will only pre-render a video component
  9. return `<video src="https://v.qq.com/iframe/player.html?vid=y08180lrvth&tiny=0&auto=0" />`
  10. }
  11. return xml
  12. }
  13. }
  14. }
  15. }

Reduce the number of pre-rendered wxml

In general, users only need to see the first page, but in fact the initial rendering of the page we build the business logic may render all the content of the page, and the reason for the slow initial rendering of Taro is that the amount of data passed for the first time is too large, so we can adjust our business logic to achieve the purpose of rendering only the first screen.

  1. class SomePage extends Component {
  2. state = {
  3. mounted: false
  4. }
  5. componentDidMount () {
  6. // Wait for the component to load, render the first screen first before we render the rest to reduce the amount of data for the first rendering
  7. // When mounted is true, the DOM trees of CompA, B, and C will only be rendered as data in the mini program
  8. // Note that we need to do this in the `componentDidMount()` cycle (which corresponds to Vue's `ready()`), earlier in the lifecycle `setState()` will be merged and updated with the first rendered data
  9. // Use nextTick to ensure that this setState is not merged with the first render
  10. Taro.nextTick(() => {
  11. this.setState({
  12. mounted: true
  13. })
  14. })
  15. }
  16. render () {
  17. return <View>
  18. <FirstScreen /> /* Suppose we know that this component will take up all of the user's screen */
  19. {this.state.mounted && <React.Fragment> /* CompA, B, C it does not appear in the first screen at first */
  20. <CompA />
  21. <CompB />
  22. <CompC />
  23. </React.Fragment>}
  24. </View>
  25. }
  26. }

In addition to speeding up first screen rendering and hydrate, this optimization also reduces the added wxml volume of Prerender. When your optimizations are thorough enough, you will find that Prerender is not needed in most cases.