在上一篇文章中,我们大致了解了如何使用Vue3.0编写一个简单的计数器程序。不过,正如熟悉Vue2.x的朋友所看到的,我们用Vue3.0实现出来的代码和Vue2.x的看起来并没有什么太大区别。难道Vue3.0就这样了?没什么新的东西吗?
非也。Vue3.0考虑到新老框架的平滑过渡,做了挺不错的向下兼容,对Vue2.x风格的写法几乎都支持。这种Vue2.x的写法被称之为Options API(可选项式API),我们在创建组件的时候,其实都是在拼装一个可选项集合,比如我们传入data、computed、methods、watch以及created、mounted等生命周期函数,用这个可选项集合来声明一个组件:
export default {
name: 'MyComp',
props: ['title'],
data() {
return {
a: 1,
b: '斤',
c: '代码'
}
},
methods: {
printInfo(txt) {
console.log(`Result is ${this.a}${this.b}${this.c}`)
}
},
created() {
console.log('created!!!')
}
}
这种方式的代码,组织良好,各个部分划分的比较清晰。但它也有缺点,就是这样的写法在逻辑复用上面不太友好。我们知道JS里最简洁、最清晰的复用方式就是将逻辑封装在一个个单纯的函数里,然后函数与函数之间互相调用。像上面这种嵌套对象的形式,复用性和优雅度就打了折扣。另外,以函数形式组织成的模块,在通过具名方式进行导入使用,在使用tree-shaking(摇树优化,可减小打包尺寸)的时候支持的更好,有更好的效果。下面是具名方式导入模块中的函数的例子,大家记得在编写代码的时候养成具名导入的好习惯:
import { myFunc1, myFunc2 } from 'my-module'
再则,由于Vue3.0支持TypeScript,TypeScript很重要的一个特性就是可以进行类型推导,而函数天然对类型推导非常友好(至少比嵌套对象要好得多)。
因此,像Composition API这种函数式的编程风格,成为了新框架的亮点,也可能是各个前端框架未来的主角。下面,我们就尝试使用Composition API来改造我们前文所写的计数器程序,主要是对Counter组件进行初步改造,代码大致就是这样的:
const { createApp, ref } = Vue
// 计数器组件
const Counter = {
template: `
<div class="counter-display">
<span class="counter-label">恭喜你,你已经写了</span>
<span class="counter-text">{{ count }}</span>
<span class="counter-label">斤代码!</span>
</div>
<div class="counter-btns">
<button class="btn" @click="increase">写一斤</button>
<button class="btn" @click="reset">删库啦</button>
</div>
`,
setup() {
// 创建一个响应式的对象
const count = ref(0)
// 操作函数
const increase = () => { count.value++ }
const reset = () => { count.value = 0 }
// 导出给模板访问
return { count, increase, reset }
}
}
我们观察到,data、methods部分都不见了,取而代之的是一个setup方法,它是Vue3.0中新增的组件入口,专为使用Composition API而设计,调用时机是在组件生命周期的 beforeCreate 和 created 之间(所以在 setup 里面是访问不了你所期望的 this 对象的,即它里面的this并不是指向当前组件,这点需要注意也尽量避免使用)。原先在 data 里的响应式对象属性 count 在这里成为了一个使用 ref 函数创建的响应式常量;而用于递增和重置这个 count 值的函数内部,不再需要通过 this 引用任何东西(也不推荐使用),这为我们进行进一步的重构提供了机会。我们可以把对 count 操作的业务逻辑独立提取出来:
// 计数器组件
const Counter = {
template: `
<div class="counter-display">
<span class="counter-label">恭喜你,你已经写了</span>
<span class="counter-text">{{ count }}</span>
<span class="counter-label">斤代码!</span>
</div>
<div class="counter-btns">
<button class="btn" @click="increase">写一斤</button>
<button class="btn" @click="reset">删库啦</button>
</div>
`,
setup() {
const countOps = useCount()
return { ...countOps }
}
}
// 对count值的操作逻辑
function useCount() {
const count = ref(0)
const increase = () => { count.value++ }
const reset = () => { count.value = 0 }
return { count, increase, reset }
}
我们将 count 值操作逻辑都分离到了 useCount() 函数中,这种做法有利于拆分复杂的业务逻辑,让代码看起来更清晰,更好维护;在我们使用模块化机制的时候,更可以进一步把这些独立逻辑函数移入模块文件中,让每一部分的代码都变得更干净。
好了,今天就先改到这儿,大家理解和消化一下代码中出现的新东西吧,后续我会做更详细的解读分析。