2-4、ES6 实战

背景:真实的 vue 项目实战

树形组件

一个可展开的树形组件,功能样式可参考Tree 树形控件 | Element Plus

思路:树形组件是典型的面向对象编程,面向的是node节点,根节点是特殊的节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// tree.vue
<script>
import itemNode from './itemNode.vue'
import refs from './refs.js'
export default {
name: 'treeNode',

components: {
itemNode
},

// 接受参数
props: {
options: {
type: Array,
default(){
return []
}
},
}

data(){
return {
name: 'v_tree'
}
},

created() {
ref.init({
name: this.name
}, this)
},

destroyed() {
ref.destroy(this.name)
},

render(){
return (
<div class="tree">
<ul class="vue-tree">
{
this.options.map((itemData, index) => {
return (
<item-node
name={this.name}
option={itemData}
key={`${this.name}_${itemData.value}_${index}`}
/>
)
})
}
</ul>
</div>
)
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// itemNode.vue
<script>
import refs from './refs.js'
export default {
name: 'itemNode',

// 接受参数
props: {
option: {
type: Object,
default(){
return {}
}
},

name: string
}

data(){
let cid = this.cid || ('item' + ++count)
return {
expand: false,
level: (this.$parent.level || 0) + 1,
indent: 1,
checked: false,
cid
}
},

computed: {
hasChild(){
return !!this.option.children
}
},

methods: {
handleClickExpand() {
this.expand = !this.expand
},
handleClickItem() {
this.checked = !this.checked
},
setDefault() {
let tree = refs.get(this.cid)
let _value = tree.value

if(_value.indexOf(this.option.value > -1)) {
this.checked = true
}
}
},

render(){
return (
<li class={[
'tree-item',
this.checked && 'is-checked'
]}>
// 展开箭头
<div
class={[
'arrow',
this.expand ? 'expand' : ''
]}
style={{
display: this.hasChild ? 'block' : ''
}}
onClick={this.handleClickExpand}
>+<div>

// 节点-标题
<div
class={['v-tree__title']}
style={{
paddingLeft: (this.level * this.indent) + 'px'
}}
onClick={this.handleClickItem}
>
{this.option.text}
</div>

// 子节点
{
this.hasChild && (
<ul
class="v-tree__child"
style={{
display: this.expand ? 'block' : 'node'
}}
>
{
this.option.children.map((itemData, index) => {
return <item-node
name={this.name}
option={itemData}
key={`${this.name}_${itemData.value}_${index}`}
/>
})
}
</ul>
)
}
</li>
)
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// refs.js
// 做树交互逻辑的:父级选中联动子级选中

// 对整棵树进行抽象
class Ref {
// options: 配置项
// tree: 树本身
constructor(options, tree) {
let name = this.name = options.name
this.tree = tree
this.refs = {}
this.refs[name] = this
}

// 根据 id 获取节点或树
get(tid) {
return tid ? this.refs[tid] : this.tree
}

// 添加节点实例
set(vm) {
let cid = vm.cid
this.refs[cid] = vm
}
}

// 一整颗树
let refs = {}

// 初始化工厂函数
let init = (options, tree) => {
let name = options.name

if(!refs[name]) {
// 如果 refs[name] 没值,则生成一颗树
return new Ref(options, tree)
}

return refs[name]
}

// 销毁实例
let destroy = (name) => {
refs[name] && delete refs[name]
}

// 获取
let get = (name, cid) => {
return refs[name] && refs[name].get(cid)
}

// 设置
let set = (name, vm) => {
refs[name] && refs[name].set(vm)
}

export default {
init,
destroy,
get,
set
}

2-4、ES6 实战
https://mrhzq.github.io/职业上一二事/前端面试/前端八股文/2-4、ES6 实战/
作者
黄智强
发布于
2024年1月13日
许可协议