Vue组件设计思考及Vuetify使用感受

Em

最近想拿 material design 写个小工具, 于是拿 awesome-vue 选了几个库, 应该是在 vuetify 和 muse-ui 中选择一个, 看起来 vuetify 文档更多一些, 更复杂一些…试用了下 vuetify

Tab组件 v-tabs / v-tab / v-tab-item

现状(2019-02-05)

  • v-tabs 可以绑定 v-model, 即 :value & @input
  • v-tab 没有和状态相关的 prop, 看文档中猜测是拿 key 做状态
  • v-tab-item 有一个 value 属性
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
<template lang="html">
<v-tabs centered color="blue" dark icons-and-text v-model="tab">
<v-tabs-slider color="yellow"></v-tabs-slider>

<v-tab href="#tab-1" key="upload">
上传
<v-icon>arrow_upward</v-icon>
</v-tab>
<v-tab href="#tab-2" key="download">
下载
<v-icon>arrow_downward</v-icon>
</v-tab>

<!-- <v-tab-item :key="'upload'" value="upload"> -->
<!-- upload -->
<!-- </v-tab-item> -->
<!-- <v-tab-item :key="'download'" value="download"> -->
<!-- download -->
<!-- </v-tab-item> -->

<!-- <v-tab-item v-for="i in 3" :value="'tab-' + i" :key="i">
<v-card flat>
<v-card-text>{{ text }}</v-card-text>
</v-card>
</v-tab-item> -->
</v-tabs>
</template>

<script>
export default {
data() {
return {
tab: 'upload',
}
},
}
</script>

<style lang="css" scoped></style>
  • 这样的代码, 没法实现默认选中, 例如默认选择 download tab
  • v-tabs 不写 v-model, 打开 v-tab-item 注释, cpu 打满, 哪里内存泄露了还是死循环了…

关于第一点, 翻了下 v-tab 的代码, active 实现逻辑由 Groupable 实现, active 状态存在组件内部…我猜, 我这个需求是做不了, 代码 https://github.com/vuetifyjs/vuetify/blob/v1.4.7/packages/vuetify/src/components/VTabs/VTab.js

关于 Tab 组件的实现

material-ui React

https://material-ui.com/demos/tabs/

1
2
3
4
5
6
7
8
9
10
11
12
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value} onChange={this.handleChange}>
<Tab label="Item One" />
<Tab label="Item Two" />
<Tab label="Item Three" />
</Tabs>
</AppBar>
{value === 0 && <TabContainer>Item One</TabContainer>}
{value === 1 && <TabContainer>Item Two</TabContainer>}
{value === 2 && <TabContainer>Item Three</TabContainer>}
</div>
  • Tabs 提供 value & onChange
  • 应用代码自己实现 TabPane 的显示与隐藏

ant-design-vue

https://vuecomponent.github.io/ant-design-vue/components/tabs-cn/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<a-tabs defaultActiveKey="1" @change="callback">
<a-tab-pane tab="Tab 1" key="1">Content of Tab Pane 1</a-tab-pane>
<a-tab-pane tab="Tab 2" key="2" forceRender>Content of Tab Pane 2</a-tab-pane>
<a-tab-pane tab="Tab 3" key="3">Content of Tab Pane 3</a-tab-pane>
</a-tabs>
</div>
</template>
<script>
export default {
data () {
return {
}
},
methods: {
callback (key) {
console.log(key)
},
},
}
</script>
  • a-tabs 使用 defaultActiveKey / activeKey 来确定当前状态, 使用 change 事件反应状态改变
  • a-tab-pane 使用 tab 属性或 tab slot 确定 tab 标题, key 定义一个 pane

ant.design

https://ant.design/components/tabs-cn/

和 ant-design-vue 一样

element-ui

http://element-cn.eleme.io/#/ji-chu-yong-fa

  • 使用 value 属性和 tab-click 属性确定当前选中

组件设计思考

上面写的几个 Tab 实现, 都有一个共同点, 就是

当前选中的状态是存在于应用层的, 组件层只接收 prop, 并根据 prop 展现出不同的 UI

我认为, 以及比较优秀的组件库都是这么做的, 组件应做为一个库(library)存在, 应尽可能的少持有状态, 在 prop 一致的情况下展现结果应该是可靠的.

vuetify 槽点

组件太多

  • 例如 AppBar 被拆成了 v-toolbar / v-toolbar-* 等等组件
  • v-icon 居然使用 default slot来确定 icon 类型, 在众多组件库中第一次见. 但是使用 prop 更好, 因为 prop 至少有个 validate 的过程.
  • 组件太多…
  • 更多….