jeremygo

jeremygo

我是把下一颗珍珠串在绳子上的人

Vue Visualization Module Engine

Background#

The marketing activities and landing page requirements within the team are iterated quite frequently. After research, a marketing visualization building platform was established based on the group's self-developed visualization platform, which is more aligned with the team's business characteristics. In terms of rendering layer implementation, considering that many historical project pages still belong to core business and require long-term iterative maintenance, in addition to the conventional page framework rendering, it is necessary to extend support for module-level rendering, allowing historical projects to be introduced and used at a low cost through normal component forms. Therefore, a visualization module engine was designed and implemented, which has been promoted in two business line projects within the team. This is a summary record of that.

Specific Implementation#

First, consider the basic form and functionality of the module engine:

In terms of form, the technical stack for the C-end H5 projects within the team is all Vue, so the basic form of this rendering engine is a Vue component (for project integration experience, what is actually exported is a Vue plugin, which will globally register this component internally);

In terms of functionality, to ensure an out-of-the-box experience, it should at least have a closed-loop logic for obtaining configuration materials and rendering. Therefore, the rendering engine needs to know the json key corresponding to the configuration materials, the environment of the project service (to distinguish the request address of the configuration materials), and the component rendering source (i.e., the packaged component library code). It also needs to consider flexible communication and interaction between business projects and internal components, so the module engine needs to support any attributes and event listeners to be passed through;

The final external component API design is as follows:

Props:

AttributeDescriptionType
sSupports directly passing in json key, the module engine can obtain configuration materials based on thisstring
envObtains the request environment corresponding to the configuration materials (test, pre-release, online)string
widgetsComponent rendering source, the module engine matches the corresponding component when parsing configuration materialsfunction
propsCustom attributes for business components, the module engine will merge these into all configured componentsobject

Events:
@getXpubData(data)
Callback function after obtaining configuration materials, custom processing logic can be based on configuration data
@xxx(...)
Custom events for business components, the module engine will merge and listen to all corresponding events thrown by the configured components

As for the commonly used Slot in Vue components, since its role is to customize the rendering content at specific positions within the component, and this part is completely taken over by the visual editor, it is not considered here.

Project integration example:

import Roo from '@vision/roo'
// The business component library needs to be installed by itself, importing the corresponding js/css source files
import renderWidgets from '@vision-xxx/dist/h5/index.js'
import '@vision-xxx/dist/h5/index.css'

// Plugin installation
Vue.use(Roo, {
  renderWidgets: [
    renderWidgets
  ]
})

// Global component usage
<template>
  <roo-component
    // The json key and corresponding environment are requested internally by the module engine
    s="op-json-xxx"
    env="production"
    // The props passed in will be injected into all components used internally
    props="{
      token: 'xxx'
    }"
    // Can listen to events exposed within the component for corresponding handling, events with the same name will trigger repeatedly
    @customEvent="eventHandler"
    // Events receiving material configuration exposed by the module engine
    @getXpubData="handlerXpubData"
  />
</template>

Next, in the core rendering logic, the standard configuration data published by the visual editor is parsed and converted to generate rendering functions. The detailed process is as follows:

  • First, the material configuration data will be requested from the CDN based on the json key;
// Example configuration data
{
  // Module id 
  "id": "000",
  // Page title
  "title": "Questionnaire",
  // Common global related extension fields
  "global": {
    "dir": "ltr",
    "code": {
      "js": "",
      "css": "",
      "jsForHeader": ""
    },
    "share": {},
    "mixins": {
      "globalmixin": {
          "pageStatus": 1
      }
    },
    "pubArea": "dpub",
    "canShare": true,
    "onlineUrl_json": "http://xxx.com/op-json-xxx.json"
  },
  // Page component and style display related fields
  "scenes": {
    "music": {
      "url": "",
      "name": ""
    },
    // Top node style
    "style": {
      "overflow": "auto",
      "minHeight": 603,
      "backgroundSize": "100% 100%",
      "backgroundColor": "rgba(0,0,0,0)",
      "backgroundImage": "",
      "backgroundRepeat": "no-repeat",
      "backgroundPosition": "center center"
    },
    "dsList": [],
    // Component list configuration
    "layers": [
      {
        // Component id, key
        "id": "widget-xxx",
        "key": "ManhattanQuestionnaireNps",
        // Component name
        "name": "Small Questionnaire - NPS",
        // Component element type
        "type": "ManhattanQuestionnaireNps",
        "label": "",
        // Component passed attributes
        "props": {
          "img": {
            "url": "https://xxx.com/xxx.png",
            "fileName": "nps_background.png"
          },
          "maxText": "10 points will definitely recommend",
          "minText": "0 points will absolutely not recommend"
        },
        // Component style
        "style": {
          "top": 0,
          "left": 0,
          "color": "#333",
          "right": "unset",
          "border": "0px solid #000000",
          "boxShadow": "#000000 0px 0px 0px 0px",
          "marginRight": 0,
          "marginBottom": 0,
          "backgroundColor": ""
        },
        "id_name": "Small Questionnaire - NPS3",
        "effectList": [],
        "editorStatus": {
          "show": true,
          "active": false,
          "isLock": false,
          "status": 0
        }
      }
    ]
  }
}
  • Based on the page status field global?.mixins?.globalmixin?.pageStatus in the configuration data, determine whether to display. If not displayed, return an empty node directly (module configuration offline scenario); if displayed, throw the getXpubData event (as defined above);
  • Combine the widgets component rendering source to generate the rendering function return:
    • Retrieve the component list layers from the configuration data, iterate to add the passed custom attributes and listener event objects (the listeners in the current component context), and mount them to the props and on fields of each component object, generating an initial top node object;
      // Example top node object
      {
         type: 'div',
         style: {
           "overflow": "auto",
           "minHeight": 603,
           "backgroundSize": "100% 100%",
           "backgroundColor": "rgba(0,0,0,0)",
           "backgroundImage": "",
           "backgroundRepeat": "no-repeat",
           "backgroundPosition": "center center"
         },
         children: [
           {
             "id": "widget-xxx",
             "key": "ManhattanQuestionnaireNps",
             "name": "Small Questionnaire - NPS",
             "type": "ManhattanQuestionnaireNps",
             "label": "",
             "props": {
               "img": {
                 "url": "https://xxx.com/xxx.png",
                 "fileName": "nps_background.png"
               },
               "maxText": "10 points will definitely recommend",
               "minText": "0 points will absolutely not recommend",
               // Mounted passed attributes
               "token": "xxx"
             },
             // Mounted event object
             "on": {
                "customEvent": eventHandler
             },
             "style": {
               "top": 0,
               "left": 0,
               "color": "#333",
               "right": "unset",
               "border": "0px solid #000000",
               "boxShadow": "#000000 0px 0px 0px 0px",
               "marginRight": 0,
               "marginBottom": 0,
               "backgroundColor": ""
             },
             "id_name": "Small Questionnaire - NPS3",
             "effectList": [],
             "editorStatus": {
               "show": true,
               "active": false,
               "isLock": false,
               "status": 0
             }
           }
         ],
       }
      
    • Generate the createElement rendering function string based on the top node object:
      • The first tag parameter distinguishes between HTML reserved elements and component elements;
      • The second parameter data object converts props, style, attrs, and on into corresponding string forms, where style values are uniformly converted to vw base, and the on event object uses a global custom event pool for event registration and triggering;
      • The final child element parameter is executed recursively to generate.
    • The rendering function string is generated through new Function to create a function declaration, binding widgets to the function scope (matching the corresponding component element tags) before returning the rendering function.

The final returned rendering function is rendered and displayed by the Vue renderer in the project.

The complete configuration integration and parsing process in the project is as follows:

image

Integration Process Performance Optimization#

The module engine can be integrated into any position on the page, and the default process for obtaining configuration data and rendering internally by the module engine is as follows:

image

For pages with very high performance requirements, the above process leads to excessive loading and rendering time for the overall component, ultimately affecting the overall interactive duration of the page. Therefore, in such scenarios, it is necessary to consider a more reasonable way to integrate.

In high-performance business projects, we provide a new loading process scheme, moving the request for material configuration and each component's request for business data to a unified page initialization request interface. The module engine only performs static rendering, so a new data attribute has been added to the module engine for directly receiving configuration data scenarios.

At the same time, the request interfaces and processing logic for different business components may vary, so we have also established a set of common business interface and data processing function configuration standards, corresponding to an SDK for parsing and processing. This SDK can be directly called during the initialization interface call.

The rendering process after data pre-fetching is as follows:

image

Most business projects also have initial data fetching nodes in place, and the overall interactive duration remains basically unaffected after integration.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.