Record a difficult background reconstruction

Keywords: Javascript Vue Programming Attribute

There is a requirement that the original background needs to be reconstructed. The front end is shown as follows:

As you can see, this one has the function of adding and deleting, and it needs to grow like this. The data format given by the back end is simplified to:

{
    "data": {
        "path": "data",
        "type": "dict",
        "showName": "Editor of classical Chinese",
        "value": null,
        "isNecessary": true,
        "subDefine": [
            {
                "path": "data/title",
                "type": "string",
                "showName": "Title",
                "value": "Zhou Yafu's army willow",
                "isNecessary": true,
                "subDefine": null
            },
            {
                "path": "data/book",
                "type": "list",
                "showName": "textbook",
                "value": null,
                "isNecessary": true,
                "subDefine": [
                    {
                        "path": "data/book/book_0",
                        "type": "dict",
                        "showName": "1",
                        "value": null,
                        "isNecessary": false,
                        "subDefine": [
                            {
                                "path": "data/book/book_0/version",
                                "type": "string",
                                "showName": "teaching material",
                                "value": "New edition of human education",
                                "isNecessary": true,
                                "subDefine": null
                            }
                        ]
                    },
                    {
                        "path": "data/book/book_1",
                        "type": "dict",
                        "showName": "2",
                        "value": null,
                        "isNecessary": false,
                        "subDefine": [
                            {
                                "path": "data/book/book_1/version",
                                "type": "string",
                                "showName": "teaching material",
                                "value": "Edited Edition",
                                "isNecessary": true,
                                "subDefine": null
                            }
                        ]
                    }
                ]
            }
        ]
    }
}

Look at the meaning of each parameter:

  • Path: current path
  • Type: indicates the type
  • showName: words displayed
  • value: the content displayed in the input box
  • Is necessary
  • Subdefinition: child elements, if any, render. If not, do not render

How can the backend transfer the data to me? I need to transfer it to him in such a format. The intermediate user may modify the value value, and then need to verify these values and transfer them to the backend. To be honest, when I got this requirement, I was confused. It was originally written in jq, and the original child nodes were all dynamically inserted in jq. Now I need to migrate this technology stack to Vue

How to draw this structure

First of all, I need to work out this structure chart. How to do this structure chart? Let's record my heart journey

Plug-in unit

Lazy people have lazy people's thinking, my first reaction is to find a plug-in or something, nothing to worry about, the transfer of data is finished. The element UI is also used in the background, so the first idea is to use the tree plug-in, but the tree component looks like this

Although the content of the node can be defined by itself, it is different from the product requirements. If you want to change it, you may need to change many styles of replication, so this scheme is temporarily abandoned.

jsx

I've asked the next small partner about this scheme, whether subdefinition exists for the rendering of sub nodes, so I don't know how many layers this needs to be rendered. So I asked my experienced partner about jsx. He gave the idea of jsx. Because jsx is more flexible, functional programming may be a better way to solve this problem. I haven't used jsx before, so I studied for an afternoon and started to work. First, I added some jsx code to this project. After that, I found that I missed a loader and reported an error as follows:


After finding some reasons, we found that it was added in vue.config.js

chainWebpack: (config) => {
    config.module.rules.delete('js'); 
}

So we won't compile jsx in this way. We can actually use jsx to complete this project here, but a company's own component library is used in the project. Turning it off conflicts with this component library. I won't say how to conflict. Anyway, it's a bit difficult to modify the component library, so we gave up this idea.

Recursive component

After hearing my confusion, a little friend next to me put forward the idea of recursive component, that is, a component can nest the same component, for example, tree.vue is a component, and it can also call the tree component internally, but the component needs to add the name attribute, as follows:

## tree.vue
<template>
    <div>
        <tree/>
    </div>
</template>
<script>
export default {
    name: 'tree'
}
</script>

Because the content of each node is about the same length, we only need to control a few variables to achieve this effect, and then we can control whether a child node is needed through whether there is a subdefinition.

data processing

Because each data is different in value and performance, I use traversal to process each data. I feel that this method is not very good. I have better ideas to put forward.

function dealData(list, type) {
  const final = [];
  list.forEach((item) => {
    let subDefine;
    if (item.subDefine) {
      subDefine = this.dealData(item.subDefine, item.type);
    }
    final.push({
      required: item.isNecessary ? 'Must fill' : '',
      ...item,
      subDefine,
    });
  });
  return final;
};

Here, each data is processed accordingly, and if the sub element has subDefine, continue to call the function. The problem is solved, but it is found that the efficiency is very poor, but I have no research on the algorithm, if there is a small partner with better suggestions can be put forward Various messy things are added to the source code. The following is the simplified tree.vue content:

<template>
  <div class="tree">
    <ul>
      <li
        v-for="(item, key) in trees"
        :key="key">
        <span>{{item.showName}}</span>
        <span @click="add" v-if="item.type === 'dict'">add</span>
        <span @click="del" v-if="item.type === 'list'">add</span>
        <zyb-input
          v-if="item.type === 'string'"
          v-model="item.value">
        </zyb-input>
        <tree
          v-if="item.subDefine"
          :min-data="minData"
          :trees="item.subDefine">
        </tree>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  props: {
    minData: Object,
    trees: [],
  },
};
</script>

You can see in the requirements diagram above that you can add or delete. Here are my solutions

delete

Delete is simple. Click Delete to actually delete the last element of the parent's subdefinition, that is, the last element of the parent's trees. The code is as follows:

trees.pop()

This will delete the last element

Add to

When the back-end is passed to the front-end, there is a minData as well as a data. The data format of this minData is the same as that of data. The difference is that the value of each item is empty. If the item can be extended, it means that you can add child elements to the subdefinition. The subdefinition is not empty, but there is only one element. This data is extremely useful when adding sub elements. For example, the subdefinition of the current element is empty. When we add elements to it, what's the data structure of this new element. At this point, you need to find out which element's path in minData is the same as the current path. Previously, I thought about finding the same loop traversal, but I denied it for a moment. Although we don't have much research on the algorithm, we can't use the idea of such low. So first of all, handle the mindData. As mentioned earlier, the path of each element is different. That is, you can recreate an object. The key is the path of each data, and the value is the data. At this time, when we need to add a new element, we only need to know the corresponding path, then take the data whose key is equal to path from the minData, and then take the first data of the subdefinition of that data.
The following is the data processing function of minData:

constructPathObj(subDefine, res = {}) {
  subDefine.forEach((value) => {
    res[value.path] = value;
    if (value.subDefine) {
      this.constructPathObj(value.subDefine, res);
    }
  });
  return res;
}
minDataa = constructPathObj(data)

In this way, we get an object whose path is key and data is value. It should also be noted that path is unique as mentioned earlier, so path cannot be repeated when adding new elements. For example, now there is an element in the subdefinition whose path is data / book? 1, and the backend requires the newly added element whose path is data / book? 2, so there is the following code

const { subDefine } = item;
let index;
if (subDefine.length === 0) {
  // Find child elements according to path
  index = 0;
} else {
  index = subDefine.length;
}
const temp = this.dealData(minData[information.path].subDefine[0], index);
subDefine.push(temp);

function dealData(data, index) {
  const temp = {};
  if (data.subDefine) {
    temp.subDefine = [];
    data.subDefine.forEach((val) => {
      // First, process the data path passed to the later
      val.path = val.path.replace(/(.*)_[0-9]/, `$1_${data.showName}`);
      temp.subDefine.push(this.dealData(val));
    });
  }
  if (data.type === 'dict') {
    temp.showName = index + 1;
    temp.path = data.path.replace(/(.*)_[0-9]/, `$1_${index}`);
  }
  return {
    ...data,
    ...temp,
  };
}

In this way, the path of each generated element will be standardized, and the effect of adding a new element will be achieved.

summary

This project is a reconstruction project, so the existing data structure cannot be changed, so we need to process the original data to get the data we need. Because this project uses a lot of recursion and loops, resulting in poor performance, so we have to add a loading, haha ~ ~. Of course, when I finished this article, I found that many places are unnecessary for loops and recursion, and there is a lot of room for performance optimization. This article is mainly about how to think about this project. Welcome to make suggestions

Posted by majocmatt on Fri, 08 Nov 2019 01:11:52 -0800