以前用Vue2的时候,写组件基本就是按`data`、`computed`、`methods`、`watch`来划分。小页面这么写确实清楚,但页面一大,同一个功能的东西可能散落在好几个地方,改一个功能要在文件里上下翻。 而Vue3的`Composition API`,说白了就是让你可以按功能来组织代码,而不是按API类型。 ## 为什么会想换写法? 拿一个用户列表页举例,通常会有: * 用户数据 * 加载中状态 * 分页 * 搜索关键词 * 请求报错处理 如果用Options API,大概会写成这样: ```ts export default { data() { return { users: [], loading: false, keyword: '' } }, computed: { filteredUsers() { // ... } }, methods: { async loadUsers() { // ... } }, watch: { keyword() { // ... } } } ``` 逻辑没错,但如果你要改搜索相关的逻辑,可能得在data、watch、computed之间来回切。此组件的业务内容再多一点,维护起来相当难受。 但Composition API可以把相关的东西放在一块: ```ts ``` 至少,相关变量和计算逻辑挨在一起,读起来顺畅一些。 ## ref和reactive怎么选 `ref` 和 `reactive` 是Vue3里最常用的两个响应式API。 基本类型我一般直接用`ref`: ```ts const count = ref(0) count.value++ ``` 对象可以用`reactive`: ```ts const user = reactive({ name: 'Tom', age: 18 }) user.age++ ``` 不过在实际项目中,我慢慢倾向于统一用`ref`,尤其是用到ts的项目。比如: ```ts const user = ref(null) ``` 这种写法在表示“数据还没加载”的时候比`reactive`自然很多,不用额外处理空对象的情况。 ## computed用来处理计算出来的数据 我一般不太喜欢在模板里写太复杂的表达式,比如: ```html {{ users.filter(user => user.enabled).length }} ``` 逻辑简单还好,一旦条件多了,刚写完就看不懂了(doge 我将其抽到`computed`中实现: ```ts const enabledUsers = computed(() => { return users.value.filter(user => user.enabled) }) ``` 模板里只留干净的展示: ```html {{ enabledUsers.length }} ``` 何不乐哉。 ## watch不是用来算值的 `watch`确实好用,但我觉得它挺容易被用过头。 比如监听关键词变化去重新请求数据,这没问题: ``` watch(keyword, () => { loadUsers() }) ``` 但如果是拿一个状态去算另一个状态,我一般会优先考虑`computed`。 举个例子,**不推荐**这样写: ```ts const firstName = ref('') const lastName = ref('') const fullName = ref('') watch( [firstName, lastName], () => { fullName.value = `${firstName.value} ${lastName.value}` } ) ``` 直接用`computed`更简单: ```ts const fullName = computed(() => { return `${firstName.value} ${lastName.value}` }) ``` 我自己的判断标准:如果能从已有数据推导出来,就`computed`;如果需要触发异步操作或者修改外部状态,再 `watch`。 ## 最大的好处其实是逻辑复用 说实话,` 文章目录