简述
组件(Component) 是vue框架中最核心的概念,所有逻辑都围绕组件展开,得组件者,得天下。
这里我主要想说一下几种不同的组件写法,并阐明不同的写法的优缺点以及适用场景。
场景
这里通过一个简单的场景:统计Button点击次数,来展示不同的写法。
click-demo.gif
经典的三段式写法
<template>
<div>
You clicked: {{ count }} times
<button @click="handleClick">
Click me
</button>
</div>
</template>
<script lang="javascript">
export default {
data() {
return {
count: 0
}
},
methods: {
handleClick() {
this.count++
}
}
}
</script>
这里,用template
申明了表现层,data()
返回组件所需的数据,methods
申明了处理逻辑。
在简单的场景下,把表现层,数据,处理逻辑申明在同一个文件问题并不大,可读性也能接受。
但假设,在上面的基础上还需要增加新的需求:1秒内多次点击的情况下,只统计1次。
这种情况下,我们就需要把组件改成如下:
<template>
<div>
You clicked: {{ count }} times
<button @click="handleClick">
Click me
</button>
</div>
</template>
<script lang="javascript">
export default {
data() {
return {
count: 0,
// 增加一个标志位,控制是否进行点击统计
shouldCount: true
}
},
methods: {
handleClick() {
// 如果标志为 false,则说明还在上一次点击的1秒以内,则不统计
if (!this.shouldCount) return
// 否则进行统计, 并立即把标志位设定为false
this.count++
this.shouldCount = false
// 1秒之后,重新激活标志位
setTimeout(() => {
this.shouldCount = true
}, 1000)
}
}
}
</script>
改造之后,整个组件的逻辑部分开始膨胀。之前那种一目了然的感觉没有了。
数据部分data()
多返回了一个shouldCount
标志位,handleClick
也结合标志位进行了相应控制。
这里把数据处理,转换,以及展示都混在一起了,造成了理解成本增加。
组合式写法(composition)
借助@vue/composition-api
,使用组合式写法,组件可以简单如下:
<template>
<div>
You clicked: {{ count }} times
<button @click="handleClick">
Click me
</button>
</div>
</template>
<script lang="javascript">
// 数据,以及处理逻辑抽离到另外一个文件
import { useCounter } from 'path/to/counter-hook'
export default {
setup() {
const { count, increment } = useCounter({ initialValue: 0, delay: 1000 })
return { count, increment }
}
}
另一个文件counter-hook
import { ref } from '@vue/composition-api'
export const useCounter = ({intialValue = 0, delay = 1000}) => {
const count = ref(intialValue)
const increment = () => { count.value++ }
return {
count,
increment: throttle(increment, delay)
}
}
function throttle(cb, duration) {
let shouldCall = true
return function(...args) => {
if (!shouldCall) return
const context = this
cb.apply(context, args)
shouldCall = false
setTimeout(() => {
shouldCall = true
}, duration)
}
}
这种写法,把数据 和 展现层进行解耦。对于逻辑复杂的情况下,还可以将数据逻辑拆分到好几个文件,进一步分解数据处理逻辑。
另外,展现层独立于数据操作的情况下,团队成员可以分开并行开发UI和业务逻辑。同时,遵循了封装变化的原则。
组合式写法进阶版(tsx)
这种方式,对于熟悉tsx
的开发者,可以获得更精细的展现层控制,以及类型推断带来的效率提升和bug率降低
// 注意,这里必须引入`h`,后续tsx语法依赖
import { defineComponent, h } from '@vue/composition-api'
// 数据,以及处理逻辑抽离到另外一个文件
import { useCounter } from 'path/to/counter'
export default defineComponent({
setup() {![click-demo.gif](https://upload-images.jianshu.io/upload_images/11213662-443cdec75b5608c3.gif?imageMogr2/auto-orient/strip)
const { count, increment } = useCounter({ initialValue: 0, delay: 1000 })
return () => (
<div>
You clicked: { count } times
<button onClick={increment}>
Click me
</button>
</div>
)
}
})