This is the second article in the series of how to build low code. The holiday is coming soon, so I am not very busy, so I can send two articles in this period of time. The second article is still more inclined to design, maybe some people think it is troublesome, but in fact, it is not, a system can only have development potential if it has good design.

Analysis of ANTD rendering results

Since this low code is based on antD framework, all rendered results are similar in style to ANTD. In order to render the desired effect more accurately, antD analysis is essential. Without further ado, above.

Using a basic filter box as an example, we can see that the overall rendering can be divided into 3 layers.

The first layer, the second layer ant-form-item-control-input, ant-form-item-Control-input-contet, this is the rendering result of the ANTD form component that I used, each of which will wrap a layer of this, We can render it by default when we do UI renderings, all renderings will look like this by default, so we can add it through a unified portal, for example, I have a DOMUtils, self-wrapped shell (of course there should be other solutions to explore).

The third layer is where our component needs to render, and from there we need the data structure we originally designed, as mentioned in the first article. The following code is a simple description of the implementation process, describing the structure of each layer in this way. Those in the know may find this somewhat similar to the virtual DOM, and indeed my idea is to draw a similar data structure and then parse it.

One might wonder if generating such a structure costs performance, and it does, but our tree depth is limited, up to four levels, and the cost is minimal.

Dom data structure

/** * DOM data structure */
export interface RenderDOM {
    domType: string;
    type: string;
    class? :string; value? :string; placeholder? :string; isDisabled? :boolean; size? : { rows? :number, cols? :number
    };  // Display the number of lines required for multi-line text boxeschildren? : RenderDOM[];// There may be multiple levels of nesting (of course there may be multiple child nodes)
}
Copy the code

Component data structure instance

       {
                key: 'radio'.name: 'Radio button Group'.type: 'radio'.icon: 'icon-danxuananniuzu'.dom: {
                    domType: 'div'.type: 'div'.class: 'ant-radio-group ant-radio-group-outline'.children: [{domType: 'label'.type: 'label'.class: 'ant-radio-wrapper'.children: [{domType: 'span'.type: 'span'.class: 'ant-radio'.children: [{domType: 'input'.type: 'radio'.class: 'ant-radio-input'.value: ' '}, {domType: 'span'.type: 'span'.class: 'ant-radio-inner',}]}, {domType: 'span'.type: 'span'.value: ' ',}]}]}},Copy the code

Having said all that, the code above may look a little abstract, but it’s actually a tree, a multi-fork tree.

The image above is a description of a component rendering, which makes it much easier to look at. What we need to do is to use some algorithm to render the appeal div, here I choose DFS, depth-first tuning to meet my needs.

The render path is as follows:

Round 1: div-div-spAN-input

Round 2: -span

The DOM tree is rendered in 2 rounds.

How do I store UI data

Here is really a difficult point, because the storage effect of data determines the scalability of the system, the data structure is strong, so the scalability of the program is also very strong.

At this point I’m going to create a separate UIStoreService to serve all the stores.

For these features, our data structure is designed as follows (for now, and will be expanded as needed).

export interface Line {

    componentNums: number; // Number of components

    lineStyle: string; / / line style

    component: {
            slider: number; // The row of bars on which the component stands
            componentType: string; // Component type
            componentName: string; // Component name
            styles: string; // Component style
            isNeed: boolean; // Mandatoryreg? :string; // Regular expressions} []; }Copy the code

All data is stored in line, which greatly reduces the difficulty of parsing. Meanwhile, in order to ensure that a usable file can be generated in one time, when we add ANTD components, we will automatically design the rendering template of the components that need to be imported. This is called when the file is generated

Import '{${components // iterate}}' from antdCopy the code

Once the data is stored, our next step is to parse the data we’ve got.

So this is a reference to the UIStoreService that I mentioned above where the UI data is parsed and rendered into runnable code.

How to render accurately

Because of our low code platform, it is necessary to render the UI accurately. So I mentioned the concept of render subcontracting in my first article, which is also a strategy for doing render,

Because the low code involves more components, if rendering on demand, accurate rendering has become a problem that such developers have to think about. Here I plan the sub-component render, and we divide the components into three categories.

1. The button type

2. Imported

3. The wide choices

The classification is based on the DOM hierarchy. Button types generally have only one DOM layer, input types have one or two layers, and selectors have three to four layers.

After doing this, the component we drew is rendered perfectly in the browser.

At the end

Today’s article is more random, not according to the rules, I want to share my thoughts on the design. In terms of code, I’ve already reached the stage of drawing UI. If you are interested, you can go to my first blog post and find the corresponding code to study.