Visual scaffolding is one of the most effective ways to reduce duplication and release productivity when organizations reach a certain size. Due to the special personnel, most of our work is fixed. Gradually, you can easily find that we have been doing a lot of repetitive things. In this case, we think about component-based development, trying to pull things out of common use, but that’s still not enough. We still spend at least two or three days building every requirement, but most of it is already done or similar. Therefore, we need a more flexible and radical solution, ideally one that responds to demand with zero development.

Page visual construction, is such a solution. As you can see in any industry, once your organization is big enough, development resources don’t match the growing demand, and a system of this nature will always emerge. Using the page visual construction system, the demander can quickly build a complex page and publish it online in a very short time through simple editing operations without going through the development process. Not only does this exponentially improve the response to requirements, but it also frees up productivity on the development side, freeing us from redundant development to focus on other scenarios that need urgent attention.

What is the MPM

MPM (Mart Page Maker) is a visual building system for stores developed by JD. After three iterations since 2016, MPM has developed into an operation system with rich component templates, powerful configuration functions and a wide range of audiences.

Component template richness

In the four years since its launch, MPM has accumulated a wealth of components and templates. Apart from those that have been removed from the shelves, MPM now has 30+ components and 500+ templates, and its business capabilities cover many scenarios such as commodities, shopping guides and marketing.

Powerful configuration function

For many manually developed pages, it is still a difficult task to implement straight out, and MPM pages built from operators support first screen straight out by default. We built a highly available Node outbound layer, which is responsible for obtaining page configuration data, aggregating request interfaces, and finally rendering the content on the first screen of the page, thus breaking the bottleneck of experiencing the first screen of complex shopping pages.

In addition to the first screen support, MPM also has some other powerful features, such as:

  • Floor BI sorting: thousands of faces, according to different user attributes show different floor priority sorting;

  • Automatic burying point: RD marks for data statistics are automatically created and buried on the page to avoid RD misburying or missing burying problems during manual development.

  • Page health diagnosis: Verifies page configurations and provides diagnosis lists, including verifying the validity and validity of configuration data and checking the impact of some components on the page, for example, whether multiple navigation components conflict with each other and whether mandatory login requirements of some components meet expectations.

Carrying key business

MPM has been involved in many promotion activities over the years. For example, MPM built more than 90% of the promotion venues of Jingdong business in Shenzhen, including the main venue, all the first-level venues and most of the second-level venues.

MPM element design

System elements are the basic components of a system, and are the core points that need to be considered before designing and implementing a system. To deduce the system elements, we must first have a deep cognition and understanding of the system design background and solution scenarios. As a visual building system for stores, MPM faces scenes constrained in the sales market, that is to say, what we need to build is no longer all pages, but only the pages of stores, which is the foundation of all MPM design. Therefore, we need to have a full understanding of the store.

Three characteristics of the store

In the e-commerce industry, the store is an important entrance to the sales channel. Under normal circumstances, the store gathers a large number of different categories of goods for unified sales, which can effectively create a “shopping” atmosphere and improve the conversion of orders. Through analysis, we conclude that the selling field has such three obvious characteristics.

Relatively independent floors

Most of the floors of the store are arranged in a cascading pattern from top to bottom, independent of each other and less related to each other. In contrast, a page like product details, where all the sections are related to the same product, has more floors that are related to each other.

Focus on display and light interaction

The main function of the store is to attract buyers, so the store is basically the display of some commodities, materials, graphic materials, and few interactive logic as complex as gameplay activities.

Various business scenarios

It is precisely because of the strong drainage capacity of the store that all business lines hope to occupy their own resources in the market. Therefore, in this case, the store naturally has to carry a variety of business scenarios, and the business interfaces involved become very many.

System element derivation

Then, based on the characteristics of stores analyzed above, how can we deduce the system elements of MPM?

First of all, we know that for any page visual construction system, attributes are essential. Take the familiar H5 production tool iH5 as an example, its configuration mode is basically “drag a button, configure the text of the button” such operations, in which, the text of the configuration button is the attribute, it is not difficult to see that the attribute is the minimum configuration unit of a page visual construction system.

Second, the configuration structure must be hierarchical, requiring coarse-grained configuration patterns on top of properties. IH5 implements this form with controls (images, text, buttons), such as the buttons in the above example, so iH5’s configuration structure is really control-properties. However, MPM is not a good fit for this configuration structure because, while the finer the granularity of the configuration, the more flexible the configuration, the higher the configuration cost. Store is a content rich page, to control to build the page, then set up a store is bound to spend a lot of time and energy. In addition, the store floor has A lot of complex data display logic, for example, if field A has value, display A, otherwise display field B. If pages are built in the dimension of controls, the implementation of this logic falls into the hands of the operator, but the operator does not want or care about this. We hope that when the operation wants a page to have a floor, it can be added and simply configured to present it.

As a result, MPM uses two coarser configuration forms — component/template. Components are the first carrier of business scenarios, and templates are like the skin of components, providing them with powerful UI presentation and expression capabilities. Component/template is a floor, which greatly reduces the configuration cost of operation, and the three-layer configuration structure of component-template-attribute also effectively ensures the flexibility of store construction.

Furthermore, as mentioned above, there are a lot of business interfaces carried by the store scenario. If we simply hand over the logic of interface request to the components, on the one hand, the components will initiate the request separately and the request cannot be effectively managed; on the other hand, the interface logic and component logic are coupled and cannot be combined and reused. So we need something to take over the data interaction logic that all components should be responsible for, and manage all interface requests in a unified way: the data source.

MPM has four major system elements

Component, template, attribute and data source are the four major system elements of MPM store visual building system.

component

Components are the first carrier of business scenarios. Each type of business scenario corresponds to a component in MPM. Therefore, components are classified by business attributes, including commodity components, kill components, and coupon components.

In the store, we built each floor with a separate MPM component instance, which is based on the store’s “relatively independent floor” feature. The benefits of this approach are: Without considering stores feature when we are faced with is a general page, page structure tree structure is obvious, the tree is very difficult to operate, and when we consider stores floor unrelated features, stores page structure is directly from a node tree structure is simplified as a floor sequence structure, namely the arraylist of floor, This greatly simplifies the implementation of MPM scaffolding pages.

Each component represents a business scenario, so as the top component of the three-tier configuration structure, its responsibility is mainly to implement the general logic of the business scenario, such as: navigation component is responsible for navigation positioning, coupon component is responsible for checking and receiving coupons.

Based on Vue, it is easy to imagine implementing an MPM component using Vue components:

/** ** kill component */
import Vue from 'vue';
import utilMixins from './utils';

/** * Register Vue component */
export default function register () {
  Vue.component('seckill', {
    props: ['params'].mixins: [utilMixins],
    data () {
      return {
        // ...
      };
    },
    created () {
      // ...
    },
    methods: {
      // ...}})}Copy the code

In MPM, each MPM component is registered as a corresponding Vue global component in which common logic is implemented. Each Vue component has a fixed props property, params, that holds user configuration data for the floor. Since it is a global component, we can directly traverse the configuration when assembling the page, rendering floor by floor and mounting the display.

It is also worth noting that we do not specify the template property in the Vue component. This is because we designed the elements by dividing the configuration into component and template layers. As you can imagine, the MPM template is actually the Template of the Vue component, which we pull out and inject dynamically in other steps.

The template

The template is the UI layer of the component. MPM requires the component to have flexible UI presentation capability. Therefore, we separate the UI layer of the component and configure it dynamically. There are multiple templates under a component, so component-template is a 1-n relationship. But templates are by no means a pure UI layer. In practice, templates will always contain some simple or complex private logic. For example, some templates of commodity components may require carrying reservation or voucher actions, which requires that our templates have the ability to undertake these private logic.

For MPM templates, we describe them in a fixed format OF HTML:

<! -- Template CSS code -->
<style>
  .rank_2212_215 {
    background: #fff;
  }
</style>

<! -- Template HTML code, based on Vue -->
<template>
  <div>
    <p>Welcome to develop a template of MPM! </p>
  </div>
</template>

<! -- Private properties -->
<script class="extends">
  const com_extend = [
    { "name": "Title"."nick": "title"."type": "text"}]</script>

<! -- Private logic -->
<script class="methods">
  const com_js = {
    priceFormat () {
      // ...}};</script>

<! -- Life cycle -->
<script class="hooks">
  const com_vueHook = {
    mounted () {
      // ...}}</script>
Copy the code

The HTML is not a canonical structure, but is rendered in a custom format that MPM provides a specialized parser to parse. It has the basic components of style, template, script.extends, script.methods, script.hooks:

  • Style: template CSS code, MPM parsing extraction, will be directly injected into the global CSS code effect;

  • Template: The template code of the template, which was parsed and extracted by MPM, was compiled into the render function by vue.compile and injected into the component;

  • Extends: extends is a private property of a template. After being pared and extracted by MPM, the configuration of the private property is attached to the component data data.extend.

  • Script. methods: a template private method, which is a tool method macro that complements component methods. After parsing and extraction by MPM, the configuration of private attributes will be mounted to the component data data.fnobj;

  • Script.hooks: template life cycle functions that correspond to the Vue component life cycle and will be called within the component life cycle after being parsed and extracted by MPM.

This pattern is similar to the structure of Vue single file components, and we chose HTML to implement MPM templates because Vue single files did not exist at that time, and HTML gave us ready-made editor highlighting and syntax hints support. So, in fact, we could define our own.mpm file to hold MPM templates, and provide an editor plug-in and a compilation process to parse such a file, but that’s another story.

attribute

Attribute is the smallest unit of MPM configuration, and flexible combination of configuration attribute is the driving force to realize the diversification of stores. Due to various configuration scenarios, MPM needs to provide various configuration attributes, including date selection, text filling, image uploading, and color selection.

On the other hand, in order to fit the hierarchical structure, MPM attributes also need to be divided into public attributes and private attributes. Public attributes are component-level attributes, such as the item group ID of the item group component. Private attributes are template-level attributes, mainly attributes that template private logic depends on.

In addition, MPM attributes also need to be validated for key configurations such as links, story ids, and prize pool identifiers.

Based on these requirements, we describe configuration properties as objects with a fixed structure:

[{"name": "Date"."nick": "date"."type": "date" },
  { "name": "Title"."nick": "title"."type": "text" },
  { "name": "Image"."nick": "image"."type": "img" },
  { "name": "Color"."nick": "color"."type": "color" },
  { "name": "Radio"."nick": "radio"."type": "radio"."data": [{"name": "Option one."."value": 1 },
    { "name": "Option two"."value": 2}]."value": "1" },
  { "name": "Pops"."nick": "option"."type": "option"."data": [{"name": "Option one."."value": 1 },
    { "name": "Option two"."value": 2}]."value": ["1"] {},"name": "Scope"."nick": "range"."type": "range"."min": 230."max": 280}]Copy the code

After the above code is parsed by MPM, the property configuration is displayed as shown above. Each Object corresponds to a configuration. The Type attribute of object is used to specify the configuration type. We provide up to 10+ configuration types to meet different configuration scenarios. Finally, after user configuration, we will probably save the data format as follows:

{
  "date": "The 2020-01-01 00:00:00"."title": "I am the title of the configuration"."image": "//a.com/image.png"."color": "#FFFFFF"."radio": 1."option": [1.2]."range": 250
}
Copy the code

In addition, attributes can use the Type and regex fields to perform simple regex validation of the user’s configuration.

Some special configuration types have the verification capability by default. If this type of attribute is applied, the configuration looks the same as text, but preset verification rules can be applied to configuration data in real time. For example, type= URL is used to verify URL links, type= ID is used to verify ids with pure digits and no more than 30 digits. Type =char User verification identifier, such as digits, and underscores (_).

[{"name": "Category id"."nick": "cateid"."type": "id"}]Copy the code

If the existing regex rules do not meet the requirements, you can also customize your regex rules by using the regex field. In order to reuse the existing regex rules, we allow you to specify the system’s own regex rules as $+ type. To achieve “multiple ids separated by English comma” verification requirements, very simple and easy to read.

[{"name": "Category id"."nick": "cateid"."type": "id"."regex": "^$id(,$id)*$"."tips": "Wrong format, please check symbols and Spaces!"."ps": "Separate multiple ids with commas."}]Copy the code

The data source

As mentioned above, the store bears many business scenarios and involves various interfaces. If each component is allowed to request and process data independently, data requests will become difficult to maintain and uncontrollable. Therefore, we need to design a data center for MPM that centrally manages and maintains all interface requests.

A data center consists of several data sources, each of which corresponds to an interface, or more accurately, each of which corresponds to a class of request actions, including interface addresses, input processing, response processing, and so on. In addition, MPM requests are made independently by each floor. Without a mechanism in place to ensure this, it is likely that the same MPM page will make many requests to the same interface, which can be a huge waste of network resources if the interface itself supports batch requests. Therefore, MPM also needs to provide data sources with the ability to merge requests and distribute responses.

For this design, we provide a data source center and several data sources.

A data source is a class that creates different request objects based on different user configurations. A request object represents a request action that will include at least the interface address, request parameters, and response processing:

export default class GroupBuying {
  constructor (option) {
    // Parameter processing
    this.params = {
      activeid: option.groupid
    }
  }
  // Request an address
  url = '//wqcoss.jd.com/mcoss/pingou/show';
  // Request parameters
  params = {};
  // Request a callback
  callback (result) {
    // ...
    returnresult; }}Copy the code

The data source center is expressed as a Vue global component, ds, which accepts an input parameter field, mpmSource, from props. This field specifies which data source to use.

/** * Data source center */
import Vue from 'vue';
import requester from './requeter';
import utilMixins from './utils';
import * as dataSourceMap from './data-source-map';

export default function register () {
  Vue.component('ds', {
    props: ['params'].mixins: [utilMixins],
    data () {
      return {
        // ...
        result: null
      };
    },
    async created () {
      const { mpmsource } = this.params;
      // Get the corresponding data source class
      const DataSource = dataSourceMap[mpmsource];
      // instantiate a request object
      const req = new DataSource(this.params);
      // Initiate a request
      const result = await requester.fetch(req);
      // Mount interface data
      this.data.result = result;
    },
    methods: {
      // ...}})}Copy the code

Creating a DS instance does this: We first get the corresponding data source class from the MPMSource, pass in the configuration data, and instantiate a request object. Requester, the MPM’s own requester, can understand the request object, initiate the request, process the data, and finally mount the data.

We only need to use this in the MPM template:

<template>
  <ds :params="{ mpmsource: 'groupbuying', ... }" inline-template>
    <p>{{result.list.length}}</p>
  </ds>
</template>
Copy the code

The Vue inline template allows you to dynamically specify the component’s template, where data is requested from the DS component so that we can use the data directly in the DS component’s inline template.

In addition, to support interface merge and response distribution, we provide data sources with the ability to customize interface merge and distribution policies:

export default class GroupBuying {
  // ...
  batch = {
    // Limit to 20
    limit: 20.// Merge requests
    merge (reqlist) {
      return {
        activeid: reqlist.map(req= > req.data.activeid).join(', ')}},// Distribute the response results
    unpack (result, reqlist) {
      const ret = {};
      reqlist.forEach(req= > {
        const key = md5(JSON.stringify(req));
        ret[key] = result[req.data.activeid];
      });

      returnret; }}}Copy the code

Batch describes the request merge and distribution policies of the data source. When the data source has the Batch attribute, the requests are not initiated immediately, but are placed in a waiting queue. Batch. limit specifies the upper limit of the number of merged requests. When the waiting queue reaches this upper limit or the default maximum waiting time is reached, the requests are packaged by the batch.merge function, which constructs new, merged request parameters, and then the requests are sent.

After the request is responded, the response data will first enter the batch.unpack function for unpacking and distribution. The result of unpackaging is a mapping object, the key is the MD5 value of the request object, and the value is the corresponding data of the request object. Requester, the requester of MPM, automatically sorts the mapping object, distributes the data to each request object, and then enters the response handler for processing.

The latter

Based on the store construction scene, we extracted and focused on the design of four system elements of the MPM store visual construction system, which is also the basis of other MPM process design. It is estimated that there may be many doubts after reading: how to design the MPM editing process? How do save publications work? How is isomorphism realized? . I still don’t have a complete understanding of MPM. Of course, MPM is a large and complex system, and we can’t get everyone to understand it all at once. Therefore, in the future, we will sort out more interesting MPM designs and share them with you. I hope you can pay more attention to them.


If you think this article is valuable to you, please like it and pay attention to our official website and WecTeam. We have quality articles every week: