How the Preview Engine (`engine-entry.js`) Renders Pages in Luban-H5
The preview engine in 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, 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 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.
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.
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.
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.
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.
// 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, compute styles, props, and event handlers.
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, iterates over Element instances and mounts each via a <node-wrapper> component. It generates event handlers and passes preview data to each element.
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, applies the element’s computed style with absolute positioning and injects animation mixins. It serves as the final styling layer before DOM insertion.
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 |
Main preview engine component and entry point. |
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 |
NodeWrapper component applying styles and animations. |
front-end/h5/src/components/core/models/element.js |
Element class parsing raw data and computing preview properties. |
core/plugins/index.js |
Plugin registry for global component installation. |
core/models/data-source.js |
Data hydration logic for dynamic content. |
Summary
- The preview engine in
engine-entry.jstransforms staticwindow.__workJSON 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, 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) 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) 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →