From dabf7ee294ee1e5c9abc07c839d1b6dce53cb81c Mon Sep 17 00:00:00 2001 From: TabSpace Date: Tue, 1 Mar 2022 09:38:48 +0800 Subject: [PATCH 01/13] =?UTF-8?q?fix(tree):=20tree=20=E7=BB=84=E4=BB=B6=20?= =?UTF-8?q?filter=20=E5=B1=9E=E6=80=A7=E7=A4=BA=E4=BE=8B=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=EF=BC=8C=E6=8F=90=E4=BE=9B=E8=BF=98=E5=8E=9F=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E7=9A=84=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/tree/demos/filter.vue | 17 +++++++++++++---- src/_common | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/examples/tree/demos/filter.vue b/examples/tree/demos/filter.vue index 4d4ce03c6..f3666b0cf 100644 --- a/examples/tree/demos/filter.vue +++ b/examples/tree/demos/filter.vue @@ -110,10 +110,19 @@ export default { methods: { onInput(state) { console.info('onInput:', state); - this.filterByText = (node) => { - const rs = node.data.label.indexOf(this.filterText) >= 0; - return rs; - }; + if (this.filterText) { + // 存在过滤文案,才启用过滤 + this.filterByText = (node) => { + const rs = node.data.label.indexOf(this.filterText) >= 0; + // 命中的节点会强制展示 + // 命中节点的路径节点会锁定展示 + // 未命中的节点会隐藏 + return rs; + }; + } else { + // 过滤文案为空,则还原 tree 为无过滤状态 + this.filterByText = null; + } }, }, }; diff --git a/src/_common b/src/_common index 380e23fbe..31be40f67 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 380e23fbe22fb7c09e81a56bd1c70b16e7d51cc1 +Subproject commit 31be40f67040b06f340f75a2f49602f273941399 From 9b09124eb0cd87987b1f362bf46f387a2888d45e Mon Sep 17 00:00:00 2001 From: TabSpace Date: Thu, 3 Mar 2022 18:30:49 +0800 Subject: [PATCH 02/13] =?UTF-8?q?refactor(tree):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E4=B8=8D=E7=94=A8=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/_common | 2 +- src/tree/td-tree.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_common b/src/_common index 3c30324b5..75b0dfa0a 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 3c30324b5f839faae2646d8bd45806cb1311a253 +Subproject commit 75b0dfa0a1dc0b360550ceea70797d1ecc4907bc diff --git a/src/tree/td-tree.tsx b/src/tree/td-tree.tsx index db1da81e1..ea0d597d4 100644 --- a/src/tree/td-tree.tsx +++ b/src/tree/td-tree.tsx @@ -1,4 +1,3 @@ -// import Vue, { VNode, VueConstructor, CreateElement } from 'vue'; import { VNode } from 'vue'; import upperFirst from 'lodash/upperFirst'; import pick from 'lodash/pick'; From 4ccce9935ab8e395e93210051edcf765a0b18ee8 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 4 Mar 2022 15:58:36 +0800 Subject: [PATCH 03/13] =?UTF-8?q?test(tree):=20tree=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20filter=20=E5=B1=9E=E6=80=A7=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/tree/filter.test.js | 83 ++++ test/unit/tree/treeNodeModel.test.js | 616 +++++++++++++-------------- 2 files changed, 376 insertions(+), 323 deletions(-) create mode 100644 test/unit/tree/filter.test.js diff --git a/test/unit/tree/filter.test.js b/test/unit/tree/filter.test.js new file mode 100644 index 000000000..8b8727713 --- /dev/null +++ b/test/unit/tree/filter.test.js @@ -0,0 +1,83 @@ +import { mount } from '@vue/test-utils'; +import Tree from '@/src/tree/index.ts'; +import { delay } from './kit'; + +describe('Tree:filter', () => { + jest.useRealTimers(); + describe(':props.filter', () => { + it('数据可过滤展示', async () => { + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + ]; + + const wrapper = mount({ + data() { + return { + filter: null, + }; + }, + created() { + this.filter = (node) => node.value.indexOf('2') >= 0; + }, + render() { + return ; + }, + }); + + await delay(10); + + const t1 = wrapper.find('[data-value="t1"]'); + let t1d1 = wrapper.find('[data-value="t1.1"]'); + const t1d2 = wrapper.find('[data-value="t1.2"]'); + + // t1.2 被命中, t1 被锁定, t1.1 隐藏 + expect(t1.exists()).toBe(true); + expect(t1.classes('t-tree__item--visible')).toBe(true); + expect(t1.classes('t-is-disabled')).toBe(true); + expect(t1d1.exists()).toBe(false); + expect(t1d2.classes('t-tree__item--visible')).toBe(true); + + await wrapper.setData({ + filter: (node) => node.value.indexOf('1.1') >= 0, + }); + + await delay(10); + + // t1.1 被命中, t1 被锁定, t1.2 隐藏 + t1d1 = wrapper.find('[data-value="t1.1"]'); + expect(t1d1.exists()).toBe(true); + expect(t1d1.classes('t-tree__item--visible')).toBe(true); + expect(t1d2.classes('t-tree__item--visible')).toBe(false); + + await wrapper.setData({ + filter: (node) => node.value.indexOf('1.3') >= 0, + }); + await delay(10); + + // 无命中,全部隐藏 + expect(t1.classes('t-tree__item--visible')).toBe(false); + expect(t1d1.classes('t-tree__item--visible')).toBe(false); + expect(t1d2.classes('t-tree__item--visible')).toBe(false); + + await wrapper.setData({ + filter: null, + }); + await delay(10); + + // 清除过滤器,全部显示 + expect(t1.classes('t-tree__item--visible')).toBe(true); + expect(t1d1.classes('t-tree__item--visible')).toBe(true); + expect(t1d2.classes('t-tree__item--visible')).toBe(true); + }); + }); +}); diff --git a/test/unit/tree/treeNodeModel.test.js b/test/unit/tree/treeNodeModel.test.js index 42eb22af1..2910f414b 100644 --- a/test/unit/tree/treeNodeModel.test.js +++ b/test/unit/tree/treeNodeModel.test.js @@ -6,21 +6,19 @@ describe('Tree:treeNodeModel', () => { jest.useRealTimers(); describe('#getLevel', () => { it('返回节点深度', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -33,23 +31,22 @@ describe('Tree:treeNodeModel', () => { describe('#getIndex', () => { it('获取子节点序号', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -61,23 +58,22 @@ describe('Tree:treeNodeModel', () => { describe('#isFirst', () => { it('是否为首节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -89,23 +85,22 @@ describe('Tree:treeNodeModel', () => { describe('#isLast', () => { it('是否为末尾节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -117,23 +112,22 @@ describe('Tree:treeNodeModel', () => { describe('#isLeaf', () => { it('是否为叶节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -144,23 +138,22 @@ describe('Tree:treeNodeModel', () => { describe('#insertBefore', () => { it('向前插入数据', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -181,23 +174,22 @@ describe('Tree:treeNodeModel', () => { describe('#insertAfter', () => { it('向后插入数据', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -218,34 +210,37 @@ describe('Tree:treeNodeModel', () => { describe('#appendData', () => { it('添加数据', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; const node1 = tree.getItem('t1'); const node2 = tree.getItem('t1.1'); - node1.appendData([{ - value: 't1.2', - children: [{ - value: 't1.2.1', - }], - }, { - value: 't1.3', - }]); + node1.appendData([ + { + value: 't1.2', + children: [ + { + value: 't1.2.1', + }, + ], + }, + { + value: 't1.3', + }, + ]); node2.appendData({ value: 't1.1.1', }); @@ -263,24 +258,24 @@ describe('Tree:treeNodeModel', () => { describe('#getPath', () => { it('获取路径节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - children: [{ - value: 't1.1.1', - }], - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + children: [ + { + value: 't1.1.1', + }, + ], + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -295,24 +290,24 @@ describe('Tree:treeNodeModel', () => { describe('#getParent', () => { it('获取指定节点的父节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - children: [{ - value: 't1.1.1', - }], - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + children: [ + { + value: 't1.1.1', + }, + ], + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -329,24 +324,24 @@ describe('Tree:treeNodeModel', () => { describe('#getParents', () => { it('获取所有父节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - children: [{ - value: 't1.1.1', - }], - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + children: [ + { + value: 't1.1.1', + }, + ], + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -360,24 +355,24 @@ describe('Tree:treeNodeModel', () => { describe('#getRoot', () => { it('获取根节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - children: [{ - value: 't1.1.1', - }], - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + children: [ + { + value: 't1.1.1', + }, + ], + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -389,27 +384,28 @@ describe('Tree:treeNodeModel', () => { describe('#getSiblings', () => { it('获取兄弟节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }, { - value: 't1.2', - }], - }, { - value: 't2', - }, { - value: 't3', - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + { + value: 't1.2', + }, + ], + }, + { + value: 't2', + }, + { + value: 't3', + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -426,26 +422,27 @@ describe('Tree:treeNodeModel', () => { describe('#getChildren', () => { it('返回基本子节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - children: [{ - value: 't1.1.1', - }], - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + children: [ + { + value: 't1.1.1', + }, + ], + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -456,26 +453,27 @@ describe('Tree:treeNodeModel', () => { }); it('返回所有子节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - children: [{ - value: 't1.1.1', - }], - }, { - value: 't1.2', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + children: [ + { + value: 't1.1.1', + }, + ], + }, + { + value: 't1.2', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const { tree } = wrapper.vm.$refs; @@ -488,110 +486,89 @@ describe('Tree:treeNodeModel', () => { describe('#remove', () => { it('不传参删除节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); - expect( - wrapper - .find('[data-value="t1.1"]') - .exists(), - ).toBe(true); + expect(wrapper.find('[data-value="t1.1"]').exists()).toBe(true); const node = wrapper.vm.$refs.tree.getItem('t1.1'); node.remove(); await delay(1); - expect( - wrapper - .find('[data-value="t1.1"]') - .exists(), - ).toBe(false); + expect(wrapper.find('[data-value="t1.1"]').exists()).toBe(false); }); it('传参删除节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); - expect( - wrapper - .find('[data-value="t1.1"]') - .exists(), - ).toBe(true); + expect(wrapper.find('[data-value="t1.1"]').exists()).toBe(true); const node = wrapper.vm.$refs.tree.getItem('t1'); node.remove('t1.1'); await delay(1); - expect( - wrapper - .find('[data-value="t1.1"]') - .exists(), - ).toBe(false); + expect(wrapper.find('[data-value="t1.1"]').exists()).toBe(false); }); it('删除不存在的节点', async () => { - const data = [{ - value: 't1', - children: [{ - value: 't1.1', - }], - }]; + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + ], + }, + ]; const wrapper = mount({ render() { - return ( - - ); + return ; }, }); const node = wrapper.vm.$refs.tree.getItem('t1'); + // 这会产生一个警告信息 node.remove('t1.2'); await delay(1); - expect( - wrapper - .find('[data-value="t1.1"]') - .exists(), - ).toBe(true); + expect(wrapper.find('[data-value="t1.1"]').exists()).toBe(true); }); }); describe('#setData', () => { it('设置节点数据', async () => { - const data = [{ - value: 't1', - info: 'a', - children: [{ - value: 't1.1', - info: 'b', - }], - }]; + const data = [ + { + value: 't1', + info: 'a', + children: [ + { + value: 't1.1', + info: 'b', + }, + ], + }, + ]; const wrapper = mount({ methods: { label(createElement, node) { @@ -599,14 +576,7 @@ describe('Tree:treeNodeModel', () => { }, }, render() { - return ( - - ); + return ; }, }); const el = wrapper.find('[data-value="t1.1"]'); From 14381a22e8e2fde3d2ec10edc3c21023840e206b Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 4 Mar 2022 16:01:06 +0800 Subject: [PATCH 04/13] chore(tree): merge upstream --- src/_common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_common b/src/_common index c550dd9ad..75b0dfa0a 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit c550dd9ad9fe150a5738d12a5fc2ab4818755a0f +Subproject commit 75b0dfa0a1dc0b360550ceea70797d1ecc4907bc From 63802d551a42812b7c78407e5bf342f91cc51812 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 4 Mar 2022 16:36:47 +0800 Subject: [PATCH 05/13] chore(tree): update src/_common --- src/_common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_common b/src/_common index 75b0dfa0a..d8add5ebe 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 75b0dfa0a1dc0b360550ceea70797d1ecc4907bc +Subproject commit d8add5ebe8ad63d21e24e5b39cd5195641e3bb34 From dbb280cc6d51f7b135a4cb3a1470c8633868f0b2 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Thu, 17 Mar 2022 14:44:21 +0800 Subject: [PATCH 06/13] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=20common=20?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/_common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_common b/src/_common index d8add5ebe..e4190e405 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit d8add5ebe8ad63d21e24e5b39cd5195641e3bb34 +Subproject commit e4190e405044b5cca0782a5fedfa590ab624fe26 From 8ffef65aa0cd5e0d2844a8fb4dc01c66b6deb0f0 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Thu, 17 Mar 2022 17:19:50 +0800 Subject: [PATCH 07/13] =?UTF-8?q?fix(tree):=20tree=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E8=A7=A3=E5=86=B3=20nodeView=20=E8=A2=AB=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E5=88=9B=E5=BB=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tree/td-tree.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tree/td-tree.tsx b/src/tree/td-tree.tsx index 282687ffe..21b9dd3f7 100644 --- a/src/tree/td-tree.tsx +++ b/src/tree/td-tree.tsx @@ -120,16 +120,15 @@ export default mixins(getConfigReceiverMixins('tre const nodesMap = this.getNodesMap(); const allNodes = store.getNodes(); this.treeNodes = allNodes.map((node: TreeNode) => { + // 维持住已经渲染的节点,不进行dom的增删 + let nodeView = nodesMap.get(node.value); // 如果有,重新生成新的vnode - if (node.visible) { + if (!nodeView && node.visible) { // 初次仅渲染可显示的节点 // 不存在节点视图,则创建该节点视图并插入到当前位置 - const nodeView = this.renderItem(node); + nodeView = this.renderItem(node); nodesMap.set(node.value, nodeView); - return nodeView; } - // 维持住已经渲染的节点,不进行dom的增删 - const nodeView = nodesMap.get(node.value); return nodeView; }); }, From b0b13f0b699ddb1e17c4f22111eb7cdde36fc40e Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 18 Mar 2022 11:09:28 +0800 Subject: [PATCH 08/13] =?UTF-8?q?fix(tree):=20tree=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=A7=BB=E9=99=A4=E7=9A=84=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=9C=AA=E8=A7=A6=E5=8F=91=20destroy=20=E7=94=9F=E5=91=BD?= =?UTF-8?q?=E5=91=A8=E6=9C=9F=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tree/td-tree.tsx | 15 +++++++++++++++ src/tree/tree-item.tsx | 6 ++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/tree/td-tree.tsx b/src/tree/td-tree.tsx index 21b9dd3f7..02fa9a48d 100644 --- a/src/tree/td-tree.tsx +++ b/src/tree/td-tree.tsx @@ -119,7 +119,9 @@ export default mixins(getConfigReceiverMixins('tre const { store } = this; const nodesMap = this.getNodesMap(); const allNodes = store.getNodes(); + const curNodesMap = new Map(); this.treeNodes = allNodes.map((node: TreeNode) => { + curNodesMap.set(node.value, 1); // 维持住已经渲染的节点,不进行dom的增删 let nodeView = nodesMap.get(node.value); // 如果有,重新生成新的vnode @@ -131,6 +133,19 @@ export default mixins(getConfigReceiverMixins('tre } return nodeView; }); + + // 更新缓存后,不再使用的节点要移除掉,避免内存泄露 + const removedNodes: VNode[] = []; + const keys = [...nodesMap.keys()]; + keys.forEach((value: string) => { + if (!curNodesMap.get(value)) { + const view = nodesMap.get(value); + removedNodes.push(view); + nodesMap.delete(value); + } + }); + curNodesMap.clear(); + removedNodes.length = 0; }, // 同步 Store 选项 updateStoreConfig() { diff --git a/src/tree/tree-item.tsx b/src/tree/tree-item.tsx index 955fd6533..31ef41220 100644 --- a/src/tree/tree-item.tsx +++ b/src/tree/tree-item.tsx @@ -304,9 +304,11 @@ export default mixins(getConfigReceiverMixins('tree'), keepAnim this.data = this.node.data; } }, + beforeDestroy() { + this.data = null; + }, render(createElement: CreateElement) { const { node } = this; - const { tree, level, value } = node; if (!tree || !tree.nodeMap.get(value)) { @@ -317,7 +319,7 @@ export default mixins(getConfigReceiverMixins('tree'), keepAnim return (
this.handleClick(evt)} From 0e42a5ccf774437ee83d53dad76496930ec86323 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 18 Mar 2022 11:53:58 +0800 Subject: [PATCH 09/13] =?UTF-8?q?fix:=20=E7=B2=BE=E7=AE=80=20tree=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=AF=B9=E8=8A=82=E7=82=B9=E7=9A=84=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tree/td-tree.tsx | 29 +++++++++++------------------ src/tree/tree-item.tsx | 2 +- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/tree/td-tree.tsx b/src/tree/td-tree.tsx index 02fa9a48d..e3d09d427 100644 --- a/src/tree/td-tree.tsx +++ b/src/tree/td-tree.tsx @@ -124,7 +124,7 @@ export default mixins(getConfigReceiverMixins('tre curNodesMap.set(node.value, 1); // 维持住已经渲染的节点,不进行dom的增删 let nodeView = nodesMap.get(node.value); - // 如果有,重新生成新的vnode + // 如果需要展示,生成新的vnode if (!nodeView && node.visible) { // 初次仅渲染可显示的节点 // 不存在节点视图,则创建该节点视图并插入到当前位置 @@ -134,18 +134,16 @@ export default mixins(getConfigReceiverMixins('tre return nodeView; }); - // 更新缓存后,不再使用的节点要移除掉,避免内存泄露 - const removedNodes: VNode[] = []; - const keys = [...nodesMap.keys()]; - keys.forEach((value: string) => { - if (!curNodesMap.get(value)) { - const view = nodesMap.get(value); - removedNodes.push(view); - nodesMap.delete(value); - } + // 更新缓存后,被删除的节点要移除掉,避免内存泄露 + this.$nextTick(() => { + const keys = [...nodesMap.keys()]; + keys.forEach((value: string) => { + if (!curNodesMap.get(value)) { + nodesMap.delete(value); + } + }); + curNodesMap.clear(); }); - curNodesMap.clear(); - removedNodes.length = 0; }, // 同步 Store 选项 updateStoreConfig() { @@ -487,11 +485,6 @@ export default mixins(getConfigReceiverMixins('tre ); - return ( -
- {treeNodeList} - {emptyNode} -
- ); + return
{emptyNode || treeNodeList}
; }, }); diff --git a/src/tree/tree-item.tsx b/src/tree/tree-item.tsx index 31ef41220..3616c964d 100644 --- a/src/tree/tree-item.tsx +++ b/src/tree/tree-item.tsx @@ -304,7 +304,7 @@ export default mixins(getConfigReceiverMixins('tree'), keepAnim this.data = this.node.data; } }, - beforeDestroy() { + destroyed() { this.data = null; }, render(createElement: CreateElement) { From 99787053e27409ed7c2448340e71b48cfa2ebb72 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 18 Mar 2022 12:05:01 +0800 Subject: [PATCH 10/13] =?UTF-8?q?test(tree):=20tree=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20remove=20=E6=96=B9=E6=B3=95=E7=9A=84?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/tree/api.test.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/unit/tree/api.test.js diff --git a/test/unit/tree/api.test.js b/test/unit/tree/api.test.js new file mode 100644 index 000000000..1820ef6d0 --- /dev/null +++ b/test/unit/tree/api.test.js @@ -0,0 +1,37 @@ +import { mount } from '@vue/test-utils'; +import Tree from '@/src/tree/index.ts'; +import { delay } from './kit'; + +describe('Tree:api', () => { + jest.useRealTimers(); + describe('remove', () => { + it('删除指定节点后,应当移除 dom 节点', async () => { + const data = [ + { + value: 't1', + children: [ + { + value: 't1.1', + }, + ], + }, + { + value: 't2', + }, + ]; + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.find('[data-value="t1"]').exists()).toBe(true); + expect(wrapper.find('[data-value="t1.1"]').exists()).toBe(true); + expect(wrapper.find('[data-value="t2"]').exists()).toBe(true); + + wrapper.vm.$refs.tree.remove('t2'); + await delay(10); + + expect(wrapper.find('[data-value="t2"]').exists()).toBe(false); + }); + }); +}); From b1f104e28130965bdbc25974b21ebb093b9feaa7 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 18 Mar 2022 12:09:10 +0800 Subject: [PATCH 11/13] =?UTF-8?q?chore(tree):=20=E6=9B=B4=E6=96=B0=20commo?= =?UTF-8?q?n=20=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/_common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_common b/src/_common index e4190e405..a312468e7 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit e4190e405044b5cca0782a5fedfa590ab624fe26 +Subproject commit a312468e758957d593677e4c922c77f42fc67aa0 From 56102f1c50e27bf749cb606855dd26e0b383788a Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 18 Mar 2022 14:17:36 +0800 Subject: [PATCH 12/13] =?UTF-8?q?test(tree):=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E6=B5=8B=E5=BF=AB=E7=85=A7=EF=BC=8Cempty=20=E5=91=88=E7=8E=B0?= =?UTF-8?q?=E6=97=B6=E4=B8=8D=E5=86=8D=E4=BF=9D=E7=95=99=20transition=20gr?= =?UTF-8?q?oup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/ssr/__snapshots__/ssr.test.js.snap | 5 ---- .../unit/tree/__snapshots__/demo.test.js.snap | 28 ------------------- 2 files changed, 33 deletions(-) diff --git a/test/ssr/__snapshots__/ssr.test.js.snap b/test/ssr/__snapshots__/ssr.test.js.snap index c6c22a9ed..55bc3ae54 100644 --- a/test/ssr/__snapshots__/ssr.test.js.snap +++ b/test/ssr/__snapshots__/ssr.test.js.snap @@ -2968,7 +2968,6 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/others.vue c
select time



Fearure Tag Fearure Tag Fearure Tag Fearure Tag

-
Tree Empty Data


@@ -15259,21 +15258,17 @@ exports[`ssr snapshot test renders ./examples/tree/demos/disabled.vue correctly exports[`ssr snapshot test renders ./examples/tree/demos/empty.vue correctly 1`] = `
-
暂无数据

-
😊 空数据(string)

-
😊 空数据( empty props )

-
😊 空数据(slot)
diff --git a/test/unit/tree/__snapshots__/demo.test.js.snap b/test/unit/tree/__snapshots__/demo.test.js.snap index 21181b0b4..376551f6e 100644 --- a/test/unit/tree/__snapshots__/demo.test.js.snap +++ b/test/unit/tree/__snapshots__/demo.test.js.snap @@ -3177,13 +3177,6 @@ exports[`Tree:demo empty demo works fine 1`] = `
-
@@ -3195,13 +3188,6 @@ exports[`Tree:demo empty demo works fine 1`] = `
-
@@ -3213,13 +3199,6 @@ exports[`Tree:demo empty demo works fine 1`] = `
-
@@ -3233,13 +3212,6 @@ exports[`Tree:demo empty demo works fine 1`] = `
-
From 88eec17f2e80a6284ca04740240cfb7f2cba8233 Mon Sep 17 00:00:00 2001 From: TabSpace Date: Fri, 18 Mar 2022 15:21:20 +0800 Subject: [PATCH 13/13] =?UTF-8?q?fix(tree):=20=E8=A7=A3=E5=86=B3=20checkbo?= =?UTF-8?q?x=20=E4=B8=8A=E7=82=B9=E5=87=BB=E4=BC=9A=E8=A7=A6=E5=8F=912?= =?UTF-8?q?=E6=AC=A1=20click=20=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tree/tree-item.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tree/tree-item.tsx b/src/tree/tree-item.tsx index 3616c964d..35f65b890 100644 --- a/src/tree/tree-item.tsx +++ b/src/tree/tree-item.tsx @@ -30,6 +30,7 @@ export default mixins(getConfigReceiverMixins('tree'), keepAnim data() { return { data: null, + clicked: false, }; }, methods: { @@ -280,6 +281,14 @@ export default mixins(getConfigReceiverMixins('tree'), keepAnim return itemNodes; }, handleClick(evt: MouseEvent) { + // checkbox 上也有 emit click 事件 + // 用这个逻辑避免重复的 click 事件被触发 + if (this.clicked) return; + this.clicked = true; + setTimeout(() => { + this.clicked = false; + }); + const { node } = this; const state: TypeEventState = { mouseEvent: evt,