# How the Preview Engine (`engine-entry.js`) Renders Pages in Luban-H5

> Discover how the preview engine in engine-entry.js renders H5 pages by transforming JSON into interactive Vue components. Learn about its pipeline for single and multi-page navigation.

- Repository: [小小鲁班/luban-h5](https://github.com/ly525/luban-h5)
- Tags: internals
- Published: 2026-03-06

---

**The preview engine in [`engine-entry.js`](https://github.com/ly525/luban-h5/blob/main/engine-entry.js) renders H5 pages by converting raw JSON work definitions into interactive Vue components, supporting both long-scrolling single pages and swiper-based multi-page navigation through a pipeline of Element models, RenderPreview components, and NodeWrapper styling layers.**

The Luban-H5 visual editor generates page definitions stored in `window.__work`, but it is the preview engine that transforms this static data into a live, interactive experience. Located at [`front-end/h5/src/engine-entry.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/engine-entry.js), this Vue-based engine orchestrates component registration, data hydration, and conditional rendering logic to output either a continuous scrolling page or a swipeable slide deck.

## Rendering Modes and Architecture

The preview engine operates in two distinct display modes determined by the work configuration. Both modes process the same underlying element data but present it through different container structures.

### Long Page Mode

When `PAGE_MODE.LONG_PAGE` is active, the engine renders the first page’s elements into a single vertically scrolling container. This mode sets `overflow-y: scroll` on the container and maps all elements directly without swiper wrappers. The height is calculated from `window.__work.height` to maintain design proportions while allowing natural scrolling.

### Swiper Page Mode

By default, the engine renders each page as an independent swiper slide. It wraps the content in `<div class="swiper-container">` and `<div class="swiper-wrapper">` structures, with each page becoming a `<section class="swiper-slide flat">`. This enables horizontal swipe navigation between pages while maintaining isolated element trees per slide.

## Step-by-Step Rendering Pipeline

The transformation from JSON definition to DOM elements follows a strict eight-stage pipeline implemented in [`engine-entry.js`](https://github.com/ly525/luban-h5/blob/main/engine-entry.js) and its dependencies.

### 1. Component Registration

The engine registers itself and all core plugins as global Vue components during installation. This ensures that dynamic element types (text, image, button) are available for runtime rendering.

```javascript
const install = function (Vue) {
  Vue.component(Engine.name, Engine)
  pluginsList.forEach(plugin => Vue.component(plugin.name, plugin.component))
}
// Auto-install when Vue is present on the page
if (typeof window !== 'undefined' && window.Vue) install(window.Vue)

```

### 2. Data Hydration

In the `created()` lifecycle hook, the engine triggers data source requests to populate dynamic content before rendering begins.

```javascript
created () {
  DataSource.dispatchRequest(window.__work)
}

```

### 3. Render Decision Logic

The `render()` function validates whether the preview can be displayed by checking URL parameters or publish status. If `view_mode` is not set to `preview` and the work is unpublished, it renders a placeholder tip instead.

```javascript
const query = new URLSearchParams(window.location.search)
const canRender = query.get('view_mode') === 'preview' || work.is_publish
if (!canRender) return this.renderUnPublishTip()

```

### 4. Container Preparation

`getContainerStyle()` computes the outer wrapper styles, applying `overflow-y: scroll` for long pages and setting dimensions based on the work configuration.

```javascript
const containerStyle = this.getContainerStyle(work)

```

### 5. Page Rendering Execution

Depending on the mode, either `renderLongPage()` or `renderSwiperPage()` executes. Long page mode renders the first page’s elements directly, while swiper mode maps each page to a slide section.

```javascript
// Long page implementation
renderLongPage () {
  if (!window.__work.pages.length) return
  const work = window.__work
  return this.renderPreview(work.pages[0].elements)
}

// Swiper page implementation
renderSwiperPage () {
  const work = window.__work
  return (
    <div class="swiper-container">
      <div class="swiper-wrapper">
        {work.pages.map(page => (
          <section class="swiper-slide flat">
            { this.renderPreview(page.elements) }
          </section>
        ))}
      </div>
      <div class="swiper-pagination"></div>
    </div>
  )
}

```

### 6. Element Model Conversion

`renderPreview(pageElements)` instantiates **Element** model objects from the raw JSON data. These models, defined in [`front-end/h5/src/components/core/models/element.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/models/element.js), compute styles, props, and event handlers.

```javascript
const elements = pageElements.map(element => new Element(element))
const height = this.isLongPage ? window.__work.height + 'px' : '100%'
return <RenderPreview elements={elements} height={height} />

```

### 7. RenderPreview Component Execution

The `RenderPreview` component, located in [`front-end/h5/src/components/core/editor/canvas/preview.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/editor/canvas/preview.js), iterates over Element instances and mounts each via a `<node-wrapper>` component. It generates event handlers and passes preview data to each element.

```javascript
elements.map(element => (
  <node-wrapper element={element}>
    { this.$createElement(element.uuid, {
        ...element.getPreviewData({ isNodeWrapper: false }),
        nativeOn: this.genEventHandlers(element)
      })
    }
  </node-wrapper>
))

```

### 8. NodeWrapper Styling Application

`NodeWrapper`, defined in [`front-end/h5/src/components/core/preview/node-wrapper.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/preview/node-wrapper.js), applies the element’s computed style with absolute positioning and injects animation mixins. It serves as the final styling layer before DOM insertion.

```javascript
render (h) {
  return (
    <div style={ this.element.getStyle({ position: 'absolute' })}>
      {this.$slots.default}
    </div>
  )
}

```

## Key Implementation Files

The preview rendering system spans several critical files in the Luban-H5 codebase:

| File | Role |
|------|------|
| **[`front-end/h5/src/engine-entry.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/engine-entry.js)** | Main preview engine component and entry point. |
| **[`front-end/h5/src/components/core/editor/canvas/preview.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/editor/canvas/preview.js)** | `RenderPreview` component that iterates over Element instances. |
| **[`front-end/h5/src/components/core/preview/node-wrapper.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/preview/node-wrapper.js)** | `NodeWrapper` component applying styles and animations. |
| **[`front-end/h5/src/components/core/models/element.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/models/element.js)** | `Element` class parsing raw data and computing preview properties. |
| **[`core/plugins/index.js`](https://github.com/ly525/luban-h5/blob/main/core/plugins/index.js)** | Plugin registry for global component installation. |
| **[`core/models/data-source.js`](https://github.com/ly525/luban-h5/blob/main/core/models/data-source.js)** | Data hydration logic for dynamic content. |

## Summary

- The **preview engine** in [`engine-entry.js`](https://github.com/ly525/luban-h5/blob/main/engine-entry.js) transforms static `window.__work` JSON into interactive Vue components through an eight-stage pipeline.
- It supports **two rendering modes**: long-page vertical scrolling and swiper-based horizontal navigation.
- **Element models** convert raw data into computed objects with styles, props, and event handlers.
- **RenderPreview** and **NodeWrapper** components handle the final DOM construction, applying absolute positioning and animation mixins.
- The engine validates rendering permissions via URL query parameters (`view_mode=preview`) or publish status before executing the pipeline.

## Frequently Asked Questions

### How does the preview engine determine which rendering mode to use?

The engine checks the work configuration for `PAGE_MODE.LONG_PAGE`. If this mode is active, it calls `renderLongPage()` to output a single scrollable container. Otherwise, it defaults to `renderSwiperPage()`, which wraps each page in swiper slide markup for horizontal navigation.

### What is the role of the Element model in the rendering pipeline?

The **Element** model, defined in [`front-end/h5/src/components/core/models/element.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/models/element.js), acts as a transformation layer. It takes raw JSON element data from `window.__work` and computes styles, properties, and event handlers required for preview rendering. This ensures that components receive properly formatted data regardless of how the work was constructed in the editor.

### How does the engine prevent unpublished works from being previewed?

In the `render()` method, the engine validates permissions by checking `window.location.search` for `view_mode=preview` or verifying that `work.is_publish` is true. If neither condition is met, the engine aborts the rendering pipeline and displays an unpublished tip via `renderUnPublishTip()` instead of the interactive content.

### What is the difference between RenderPreview and NodeWrapper components?

**RenderPreview** ([`front-end/h5/src/components/core/editor/canvas/preview.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/editor/canvas/preview.js)) is responsible for iterating over the array of Element instances and generating the component tree. **NodeWrapper** ([`front-end/h5/src/components/core/preview/node-wrapper.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/preview/node-wrapper.js)) is a presentational wrapper that applies absolute positioning, computed styles, and animation mixins to each individual element. RenderPreview handles the list; NodeWrapper handles the individual element styling.