elment-ui中tree木块相关文件如下图:
下图梳理一下各个文件之间的引用关系(箭头的方向表示使用)
1 uti.js
1.1 markNodeData 标记节点
export const NODE_KEY = '$treeNodeId';
export const markNodeData = function(node, data) {if (!data || data[NODE_KEY]) return;Object.defineProperty(data, NODE_KEY, {value: node.id,enumerable: false,configurable: false,writable: false});
};
定义常量NODE_KEY; 判断节点是否存在NODE_KEY 属性,存在则返回;否则使用Object.defineProperty定义节点的NODE_KEY 属性,属性是只读的。
1.2 getNodeKey 获取节点NODE_KEY属性
export const getNodeKey = function(key, data) {if (!key) return data[NODE_KEY];return data[key];
};
如果没有指定key, 则返回NODE_KEY 。
1.3 findNearestComponent 寻找最近的组件
export const findNearestComponent = (element, componentName) => {let target = element;while (target && target.tagName !== 'BODY') {if (target.__vue__ && target.__vue__.$options.name === componentName) {return target.__vue__;}target = target.parentNode;}return null;
};
如果target存在__vue__属性(代表对应的vue组件),并且组件名等于componentName则返回这个vue组件,否则向上判断target的父节点。
__vue__属性是在vue源码中定义的属性:
if (prevEl) {prevEl.__vue__ = null
}
if (vm.$el) {vm.$el.__vue__ = vm
}
详见vue源码 (路径为: src\core\instance\lifecycle.js)
2.node.js分析
node.js中定义了Node类和获取子节点状态、重新初始化节点选中状态以及获取节点属性的方法,我们逐一了解一下:
2.1 getChildState 获取子节点状态
export const getChildState = node => {let all = true;let none = true;let allWithoutDisable = true;for (let i = 0, j = node.length; i < j; i++) {const n = node[i];if (n.checked !== true || n.indeterminate) {all = false;if (!n.disabled) {allWithoutDisable = false;}}if (n.checked !== false || n.indeterminate) {none = false;}}return { all, none, allWithoutDisable, half: !all && !none };
};
代码中的单词indeterminate表示“不确定的”,getChildState判断一个节点的子节点状态, all代表全部都勾选;none代表一个都没勾选;allWithoutDisable代表所有的都禁用;half表示有勾选的有没勾选的。
all, none, allWithoutDisable初始值全是true; 循环遍历子节点列表,如果有一个节点的checked不为true或者为indeterminate则all为false;如果某个节点没有禁用则allWithoutDisable为false; 如果某个节点勾选了或者indeterminate则none为假。
2.2 reInitChecked 根据子节点状态重置节点勾选状态
const reInitChecked = function(node) {if (node.childNodes.length === 0) return;const {all, none, half} = getChildState(node.childNodes);if (all) {node.checked = true;node.indeterminate = false;} else if (half) {node.checked = false;node.indeterminate = true;} else if (none) {node.checked = false;node.indeterminate = false;}const parent = node.parent;if (!parent || parent.level === 0) return;if (!node.store.checkStrictly) {reInitChecked(parent);}
};
如果子节点列表为空则直接返回;否则获取子节点状态进行判断:如果子节点全选了,那么设置当前节点为选中状态(checked为true, indeterminate为false);如果子节点为half即有选的有没选的则设置当前节点为不选中状态(checked为false, indeterminate为true);如果子节点全都没选中,则设置当前节点为不选中状态(checked为false, indeterminate为false)。
设置完当前节点后则要处理当前节点的父节点,如果父节点不存在或者层级为第0层(根节点)则返回,否则判断父子节点之间是否严格不关联,如果不是的话,则递归检查父节点。关于checkStrictly,文档中有如下说明:
下图reInitChecked方法的调用关系(箭头方向表示被调用):
2.3 getPropertyFromData从数据中获取属性
const getPropertyFromData = function(node, prop) {const props = node.stor