我不確定這是否是普遍的用法,但我發現 reactive 非常適合用來建立類似 OO 的封裝。
reactive會把 refs 展平,所以可以這麼寫:
function useClient() {
const name = ref('Alice')
const greeting = computed(() => `Hello ${name.value}`)
function updateName(newName: string) {
name.value = newName
}
return reactive({
name,
greeting,
updateName
})
}
在 component 裏:
const client = useClient()
client.greeting // => 'Hello Alice'
client.updateName('Bob')
client.greeting // => 'Hello Bob'
現在client管理它自己的狀態,其暴露的接口可以直接用在模板裏。
我們也可以組合這些對象和保留嚮應性:
function useOrder(client: ReturnType<typeof useClient>) {
const createdBy = computed(() => `Created by ${client.name}`)
// 你也可以在這裏調用 client.updateName
return reactive({
createdBy
})
}
const client = useClient()
const order = useOrder(client)
order.createdBy // => 'Created by Alice'
client.updateName('Bob')
order.createdBy // => 'Created by Bob'
我覺得這是 vue 相對於其他庫特別的屬性,我只需要傳遞一個對象,而它擁有自己的狀態和方法。
在現實中,這些對象一般會基於後端數據,我們可以在後端數據的基礎上擴展狀態和方法。
async function useOrder(client: ReturnType<typeof useClient>) {
const orderData = reactive(await fetchOrderData())
const paid = ref(false)
async function pay() {
const res = await paymentAPI()
paid.value = res.success
}
return reactive({
...toRefs(orderData), // 所有 orderData 的屬性會被暴露
// 我們需要 toRefs 來保持嚮應性
paid,
pay
})
}
現在給定一個 order ,我們可以直接在模板裏綁定order.paid 和order.pay。
本帖在 reddit 上的版本: https://www.reddit.com/r/vuejs/comments/1owezu4/reactive_as_an_object_encapsulation/
1
XCFOX 12 小时 57 分钟前 |
2
lynchuh 9 小时 27 分钟前 牛,学到了!
|
4
jspatrick 8 小时 55 分钟前
分享下我项目内的常见写法,这是表格相关的功能,几乎完全是 v2 的迁移版...
```javascript const state = reactive({ loading: false, list: [], searchParams: { department: null, company: null, vehicleType: [], thisWeek: false, thisMonth: false, thisYear: false, startTime: computed(() => { if (!state.searchParams.time) return undefined; const startTime = state.searchParams.time[0]; return dayjs(startTime).startOf('day').format('YYYY-MM-DD HH:mm:ss'); }), endTime: computed(() => { if (!state.searchParams.time) return undefined; const endTime = state.searchParams.time[1]; return dayjs(endTime).endOf('day').format('YYYY-MM-DD HH:mm:ss'); }), time: [dayjs().startOf('month').format('YYYY-MM-DD'), dayjs().endOf('day').format('YYYY-MM-DD')] }, options: { department: [], company: [], vehicleType: [] }, columns: hooks.useDefaultColumns([]), pagination: hooks.usePagination(() => state.search()), selectTimeMode(idx) { }, async search() { } }); ``` |
5
jspatrick 8 小时 50 分钟前 |
7
duuu 8 小时 24 分钟前 楼主的 useClient 其实就是我们常用的 Composable 的写法,只是在 return 里套了一层 reactive 。好处暂时看来是用的地方不用写.value 了。但是我感觉这样写反而会有心智负担,因为简单从代码里看 name ,和 greeting 的用法都是要.value 的,如果用了 useClient 后,client.greeting 和,client.name 都不用.value 反而在每次写的时候要思考一下。如果这种写法在团队里没有形成规范的话,也会让同事调用你的代码写起来很困惑
|
9
jspatrick 8 小时 13 分钟前
@duuu #8 其实就是抽离不抽离的关系,如果文件复杂度上升,就抽出去做成 hooks 来用,如果只是简单的页面,这么写还是有好处的,所有的表单逻辑其实都被写在了 state 里,这整个 state 其实就是 hooks 本来要导出的东西,关注点也不会被其他 hooks 打断,并不是页面全被塞进 state ,而是某个业务相关的东西被单独做成一个 state ,一个页面也可能有多个不同业务的 state
|
12
lizhenda 7 小时 51 分钟前
这不就是 vue3 推荐的使用方法么 ···
|
13
UnluckyNinja 6 小时 33 分钟前 这其实就是 composable 的用法,但官方指南不建议在 composable 函数里对返回值用 reactive 包装,这样会导致解构语法响应性不正确。
比如你这个例子,如果使用者写 `const { name } = useClient()`,name 就会失去响应性。 官方的推荐做法是返回 ref 作为返回对象的属性,如果调用方想使用整体响应性并免除.value ,可以自己用 reactive 包装。 详见 https://cn.vuejs.org/guide/reusability/composables , 大量 composable 参考可以浏览 VueUse https://vueuse.org/ |
14
mizuhashi OP @UnluckyNinja 原來文檔裏有提到用 reactive 包,我知道官方推薦的返回 ref 。不過對於 domain object 我從沒想要解構,就是想要它的封裝性,如果解構了就都散了
|
15
Ketteiron 3 分钟前
@UnluckyNinja 官方文档这么写问题很大,似乎是暗示 return 一个 reactive 会丢失响应式,我第一次看文档时也是这么认为的。实际并不会,因为返回的是代理对象,只有对代理对象进行解构才会丢失响应式。
我想没人会 return obj ,正常都是 return { obj } ref x,y 和 reactive ({x,y}) 是等价的两种写法,只是后者不能解构 const {obj:{x,y}} = useXxx() 我想也没人这么闲用嵌套解构语法 文档只需说不建议返回 reactive 对象就行了,初学者可能会以为 {reactive()} 也是不行的。 |