One, foreword

The company’s tree business is a complex batch, and the amount of data is very large, always say that the test at least W data, I am like a baboon.

The company uses the Element-UI framework for its backend system, and the natural tree component is called El-Tree.

To be honest, El-Tree is already pretty good, providing a wealth of callback events, attributes, and methods, making it the perfect choice for lightweight data rendering.

However, after more than half a year of project practice, I felt that El-Tree was stuck, with more and more data and more and more complex functions.

Testing this aspect of the BUG has reached the realm of testing must mention, optimize tree component performance imperative!! !

Find a way to the light.

2. Tree capable of rendering W data

2.1 the vue – giant – tree

So I am good at looking for resources, looking for ah, yi, find a very domineering guy:

Look at it, it’s easy to achieve high performance rendering of massive data.

Key words: easy, massive, high performance

And explained why they were used:

Come on, do you have a problem? = = = “big – tree. Json

Master Peng refused to accept the arrangement!

First download the json called big-tree to see if it is true!!

The size of the

content

Git Clone operation, first yarn, then serve run, F12 press a press.

To the naked eye: It loads very fast! Blink!

Take a look at dom loading:

?????

Really awesome!!

However, vue-giant-Tree is just like what it said, just a zTree shell, modified skin, all functions are Ztree, so I went to the Official website of Ztree to have a look at the documentation.

After careful research, ztree is really awesome!! It can run in IE6 and the performance is very good, but its icon icon is very simple, and uses the Sprite diagram way, using absolute positioning to obtain each icon, this does not meet the business needs of our company, we want good-looking, beautiful, can be replaced at any time icon.

The UI sister of the company said that it is difficult to provide a Sprite picture of the same size and position, and may not be able to work out.

I decided to modify the ZTree source code to use iconfont to get a flexible and rich icon configuration.

2.2 Come, come, stand on the shoulders of giants

Newton once said: I am so successful, because I stand on the shoulders of giants!

Master Peng is now stepping on vue-giant-Tree and ZTree, and has produced vue-magic-Tree that is capable of complex business and massive data rendering.

Knock knock knock!!

  • GitHub link: vue-magic-tree
  • Online test address: Test

Bright performance:

  • 1. Top 10 looks, beautiful skin from Vue-giant-Tree.
  • 2. Provide iconfont icon library to render ICONS of any node type.

Render multiple icon charts randomly

Thus, the giant tree satisfies O(n * N) business complexity.

This section describes the following services:

  • 1. Expand all nodes under a node in a branch by default. (In short, I want this expanded, that not expanded, this expanded to how many layers, that how many layers, project managers eat shit)
  • 2. Each node type must have a corresponding icon. Some nodes do not need ICONS.
  • 3. W pieces of data cannot have “refresh state” on render (project manager sucks crap)
  • 4. There’s a list of requirements that I won’t show you.

3 points above, El-Tree exits the game, i-Tree exits the game.

Both El-Tree and I-Tree provide custom rendering, as well as icon customization. So you can try, data amount of time, there are many cards. And the entire article only compares initial load times, not tick events at all. Our company el-tree in 3000 data and then click on all (click on the root node checkbox) will card 2-3 seconds, each time cancel, check, cancel, check, check, check a card.

If you don’t believe me, I’ll just say you know it yourself.

Next, look at the performance comparison test. If you don’t want to see it, you can click on the direct source code modification section

3. Set up a challenge (initialize render tree)

Rule description:

1. Same file, local load (ivew node name must be title, so iview data in the name changed to title, see my github repository code can see iView is not the global load of this data, but it is the same.)

const bigData = require("@/mock/big-tree.json");
Copy the code

2. Also clear the cache and refresh.

3. GIF same position, size, ratio.

Consistency is guaranteed, and it is obvious that iView and Element load lag is evident, while ZTree has almost no lag.

Online test address: Test (overseas server loading may be slow)

3.1 I refuse to accept the Vip Iview!

3.2 My crew cut brother Element refuses to accept!

3.3 Ztree

3.4 Result Analysis

Cattle group!!!!!

I would like to say that this data is a one-dimensional array, ztree according to the PID automatically spliced into a tree structure of data and render, but also so good!!

And ztree’s functions are extremely rich, you can meet all your needs, you have to see the document, combined with your business to develop.

I’m just here to say ztree awesome!!

4. Groping ZTree

Hey hey, oil head peng noodles uncle is coming

18 forbidden,

Now that we know the core of this tree is zTree, let’s download its open source code and get started!!

Ztree v3.5.41 JQuery ztree v3.5.41

As you can see, the directory structure is very simple, and everything that implements ZTree is in this JS folder. (So focus only on this folder)

4.1 Analysis by naming

There are eleven files, the first of which is the min version of jquery. Then a rough look, with min files to it ignore!

This leaves five files:

jquery.ztree.all.js
jquery.ztree.core.js
jquery.ztree.excheck.js
jquery.ztree.exedit.js
jquery.ztree.exhide.js
Copy the code

From the nomenclature, it’s clear:

  • All should be a collection of all functions.
  • Core is the core function.
  • Ex starts with extensions.

Because the project is introduced in the all file, in fact, just look at all. (All includes all of core and EX)

Do it!!

Since we need to change its archaic icon node, we start with documentation

We can find that the function setting of the control node icon is controlled by a variable called setting.view.showicon

Setting.view. showIcon: indicates whether to display the controller node icon.

So let’s take a look at this thing in the source code is how to deal with logic, so as to find a breakthrough.

4.2 From a global perspective

Open the all file: in fact, the notes are very detailed and perfect.

Definition:

  • _consts constants
  • _setting Specifies the default configuration object
  • Some initialization methods _init*,
  • Event binding and unbinding (_bingEvent, _unbindEvent), event proxy (_eventProxy).

Read on:

Definition:

  • A collection of methods that manipulate data
  • A collection of methods for the event broker (Event)
  • A collection of methods for event handling (Handler)
  • Combination of public methods for ZTree (Tools)
  • Set of methods to render the DOM (View)
  • The definition of ztree

We want to modify the DOM where the icon is located, so just look at the method under View. You can also search the showIcon keyword to find the location of the corresponding method. But it’s better to look at the big picture first, so that you have the full flow and life cycle of programming in mind.

4.3 View the method under view:

30-40 methods (roughly counted)

First don’t panic, first look at the function name, believe that you can understand.

Circle the makeDOMNodeIcon, which is the method that renders the icon and node name.

4.4 makeDOMNodeIcon Render the node icon and name

makeDOMNodeIcon: function (html, setting, node) {
    var nameStr = data.nodeName(setting, node),
      name = setting.view.nameIsHTML ? nameStr : nameStr.replace(/&/g.'& ').replace(/</g.'< ').replace(/>/g.'> ');
    html.push("<span id='", node.tId, consts.id.ICON,
      "' title='' treeNode", consts.id.ICON, " class='", view.makeNodeIcoClass(setting, node),
      "' style='", view.makeNodeIcoStyle(setting, node), "'></span><span id='", node.tId, consts.id.SPAN,
      "' class='", consts.className.NAME,
      "' >", name, "</span>");
  },
Copy the code

Very simply, this method does one thing: push two SPAN tags into the HTML, one for icon ICONS and one for displaying node names.

We need to modify the ICON icon dom. Take a closer look. The class and style of the ICON DOM are obtained dynamically through makeNodeIcoClass and makeNodeIcoStyle respectively.

Here I would like to introduce the iconfont rendering form — font class reference:

<i class="iconfont icon-xxx"></i>
Copy the code

So we just need to add these two classes to the icon dom of this node to render iconfont.

Scroll down:

4.5 makeNodeIcoClass Write the class name to node icon

Look at the makeNodeIcoClass:

makeNodeIcoClass: function (setting, node) {
    var icoCss = ["ico"];
    if(! node.isAjaxing) {var isParent = data.nodeIsParent(setting, node);
      icoCss[0] = (node.iconSkin ? node.iconSkin + "_" : "") + icoCss[0];
      if (isParent) {
        icoCss.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE);
      } else{ icoCss.push(consts.folder.DOCU); }}return consts.className.BUTTON + "" + icoCss.join('_');
  },
Copy the code

This method is as straightforward as its name, which simply returns a string, some class name.

BUTTON This is a constant and can be found in the _consts constant list:

Then icoCss is a fixed ICO + open close docU to choose one of the three splicing, if the iconSkin attribute is set, then it will be put on this. So you get two class names: button ICo_open.

It doesn’t really matter, because we’re going to completely rewrite this method.

Rewrite as follows:

 makeNodeIcoClass: function (setting, node) {
    const iconName = data.nodeIcon(setting, node);
    return `${consts.className.BUTTON} iconfont ${iconName}`; 
 }
Copy the code

We obtain the iconName through a nodeIcon method in the data set, and then splice it into the form of Button iconfont icon-xxx.

I keep consts.classname. BUTTON because it has a style and I’m not going to change it. This will render the iconfont icon.

Take a look at data.nodeicon (this method goes into the data collection) :

nodeIcon: function (setting, node, newName) {
    const key = setting.data.key.nodeType;
    if (typeofnewName ! = ='undefined') {
      node[key] = newName;
    }
    return setting.data.iconMap[node[key]] || "";
  },
Copy the code

First obtain in the global Settings object setting. Data. Key. This property nodeType, then get the node [key] = node. The nodeType = 0, and then through the key: returns the value of iconMap corresponds to the value, The final result is the icon class name for this node.

Nodes for example:

  { id: 1.pid: 0.name: "Check 1 at will.".open: true.nodeType: 0.chkDisabled: true },
Copy the code

Properties in the setting object:

In this case, the value obtained is setting.data.iconmap [0] = ‘iconjianyuede’

IconMap [node[key]] = setting. Data. IconMap [node[key]] = undefined. There will be no corresponding key in iconMap and ZTree will not render the icon.

4.6 makeNodeIcoStyle Style the node icon

MakeNodeIcoStyle:

 makeNodeIcoStyle: function (setting, node) {
    var icoStyle = [];
    if(! node.isAjaxing) {var isParent = data.nodeIsParent(setting, node);
      var icon = (isParent && node.iconOpen && node.iconClose) ? (node.open ? node.iconOpen : node.iconClose) : node[setting.data.key.icon];
      if (icon) icoStyle.push("background:url(", icon, ") 0 0 no-repeat;");
      if (setting.view.showIcon == false| |! tools.apply(setting.view.showIcon, [setting.treeId, node],true)) {
        icoStyle.push("width:0px; height:0px;"); }}return icoStyle.join(' ');
  },
Copy the code

IsParent and showIcon are used to control the style of icon dom. If it is displayed, it sets the path of background. If it is not displayed, it will be width and height 0.

Our business does not need to judge whether the parent node icon is displayed or not, so we do not care about the isParent attribute, as long as the showIcon, rewrite as follows:

makeNodeIcoStyle: function (setting, node) {
    if(! node.isAjaxing) {if (setting.view.showIcon == false| |! tools.apply(setting.view.showIcon, [setting.treeId, node],true)
          || data.nodeIcon(setting, node) === ' '
        ) {
        const iconStyle = "display:none;"
        return iconStyle
      }else {
        return ' '}}},Copy the code

If display returns an empty string, display: None; Can.

4.7 Let’s see the effect

First, iconfont was introduced into the project:

// main.js
import './style/iconfont.css'
Copy the code

Then quote ztree source file all:

Require it in, don’t forget to introduce jquery!!

/ / into the jquery
import * as $ from "jquery";
if(!window.jQuery){
  window.jQuery = $;
}
// Update ztree
require("./lib/jquery.ztree.all");

// Call ztree's official initialization method with parameters omitted
$.fn.zTree.init()
Copy the code

The end result is this:

It works fine, but when you do it, for example, when you fold the node, the icon will disappear. So, you still need to look at the source code and make changes.

4.8 Folding Events

Continue to look at the function name in the view collection, you can find:

All three of these methods have the prefix expandCollapse and all of these things.

Expand the expandCollapseNode method and scroll down to line 1235:

Here a Node. open attribute is used to call the replaceIcoClass method, replacing the class of the icon DOM. So next, modify this method.

replaceIcoClass:

replaceIcoClass: function (node, obj, newName) {
    if(! obj || node.isAjaxing)return;
    var tmpName = obj.attr("class");
    if (tmpName == undefined) return;
    var tmpList = tmpName.split("_");
    switch (newName) {
      case consts.folder.OPEN:
      case consts.folder.CLOSE:
      case consts.folder.DOCU:
        tmpList[tmpList.length - 1] = newName;
        break;
    }
    obj.attr("class", tmpList.join("_"));
  },
Copy the code

Alter class; alter class; alter class;

replaceIcoClass: function (node, obj, newName) {
    // According to your situation, I can delete all of them
 },
Copy the code

After the change, the node icon does not disappear when folding, it is normal. In this way, all the add, delete and change functions will not affect the normal display of the icon.

4.9 Temporarily complete the task of one stage

According to the above modification, normal use, can meet the needs of business and render mass data performance is also very good. However, it can only be said that the completion of a stage, after all, the dog needs to change every day…

Five, write in the back

I personally think can understand some source code, and modify some content, not bad. After all, is playing with a phone the same thing as making one?

We also do not flatter themselves, in fact, a lot of source code is not as difficult as you imagine, but you think you can not, your subconscious has told yourself: you do not understand. This leads to the misconception that the source code is hard to understand.

In fact, I want to tell you that read the source code, the benefits are very obvious:

  • 1. Look at how the code is written by the big factory or the big god. What is the code specification of the big factory?
  • 2. You can learn more the use of the higher-order functions, you what function when the interview is not often back some curry, closures, design patterns, compose, and so on, but the basic not used in their contact project, and within the source of these good open source project you can find some shadow, you can try to understand try to use up.
  • 3. I can understand what needs to be considered in the whole program design. The integrity and maintainability of these open source projects are very strong, from which I can learn and improve myself.

GitHub link: VuE-magic-Tree hope to help you. Online test address: Test

6, update,

1. Vue-magic-tree is packaged as a component and published on NPM. All you need to use it is:

yarn add vue-magic-tree jquery
Copy the code

Ps: jquery is a must.

2. This project can be cloned and customized.