用 reactive oo 象的封 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mizuhashi
V2EX    Vue.js

用 reactive oo 象的封

  •  
  •   mizuhashi 1 天前 997 次点击

    我不定是否是普遍的用法,但我 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.paidorder.pay

    本帖在 reddit 上的版本: https://www.reddit.com/r/vuejs/comments/1owezu4/reactive_as_an_object_encapsulation/

    18 条回复    2025-11-15 09:59:03 +08:00
    XCFOX
        1
    XCFOX  
       1 天前   1
    我都是写 reactive + class 的。
    class 简直是天然的 store ,状态和方法一目了然。

    ```ts
    import { reactive } from 'vue'

    class ClientStore {
    name = 'Alice'

    get greeting() {
    return `Hello ${this.name}`
    }

    updateName(newName: string) {
    this.name = newName
    }
    }

    export const client = reactive(new ClientStore())
    ```
    lynchuh
        2
    lynchuh  
       1 天前   1
    牛,学到了!
    mizuhashi
        3
    mizuhashi  
    OP
       1 天前
    @XCFOX 好像也可以,不有用上 computed 的存
    jspatrick
        4
    jspatrick  
       1 天前
    分享下我项目内的常见写法,这是表格相关的功能,几乎完全是 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').forat('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() {
    }
    });
    ```
    jspatrick
        5
    jspatrick  
       1 天前   1
    mizuhashi
        6
    mizuhashi  
    OP
       1 天前
    @jspatrick 原可以引用自己
    duuu
        7
    duuu  
       1 天前   2
    楼主的 useClient 其实就是我们常用的 Composable 的写法,只是在 return 里套了一层 reactive 。好处暂时看来是用的地方不用写.value 了。但是我感觉这样写反而会有心智负担,因为简单从代码里看 name ,和 greeting 的用法都是要.value 的,如果用了 useClient 后,client.greeting 和,client.name 都不用.value 反而在每次写的时候要思考一下。如果这种写法在团队里没有形成规范的话,也会让同事调用你的代码写起来很困惑
    duuu
        8
    duuu  
       1 天前
    @jspatrick vue3 的优点就是每个模块的代码可以集中写,你这样按 vue2 的 data 来放,就丢失这个优点了~
    jspatrick
        9
    jspatrick  
       1 天前
    @duuu #8 其实就是抽离不抽离的关系,如果文件复杂度上升,就抽出去做成 hooks 来用,如果只是简单的页面,这么写还是有好处的,所有的表单逻辑其实都被写在了 state 里,这整个 state 其实就是 hooks 本来要导出的东西,关注点也不会被其他 hooks 打断,并不是页面全被塞进 state ,而是某个业务相关的东西被单独做成一个 state ,一个页面也可能有多个不同业务的 state
    dumbass
        10
    dumbass  
       1 天前
    @duuu #8 正确的。我会把 loading/list/searchParams/options... 拆成不同的 ref
    duuu     11
    duuu  
       1 天前
    @jspatrick 理解,不过如果是我的习惯的话,如果不抽成 hooks ,我不会在外面套一层 reactive 。我会跟#10 一样拆成不同的 ref
    lizhenda
        12
    lizhenda  
       1 天前
    这不就是 vue3 推荐的使用方法么
    UnluckyNinja
        13
    UnluckyNinja  
       1 天前   1
    这其实就是 composable 的用法,但官方指南不建议在 composable 函数里对返回值用 reactive 包装,这样会导致解构语法响应性不正确。
    比如你这个例子,如果使用者写 `const { name } = useClient()`,name 就会失去响应性。
    官方的推荐做法是返回 ref 作为返回对象的属性,如果调用方想使用整体响应性并免除.value ,可以自己用 reactive 包装。

    详见 https://cn.vuejs.org/guide/reusability/composables
    大量 composable 参考可以浏览 VueUse https://vueuse.org/
    mizuhashi
        14
    mizuhashi  
    OP
       1 天前 via iPhone
    @UnluckyNinja 原文有提到用 reactive 包,我知道官方推的返回 ref 。不於 domain object 我想要解,就是想要它的封性,如果解了就都散了
    Ketteiron
        15
    Ketteiron  
       1 天前
    @UnluckyNinja 官方文档这么写问题很大,似乎是暗示 return 一个 reactive 会丢失响应式,我第一次看文档时也是这么认为的。实际并不会,因为返回的是代理对象,只有对代理对象进行解构才会丢失响应式。
    我想没人会 return obj ,正常都是 return { obj }
    ref x,y 和 reactive ({x,y}) 是等价的两种写法,只是后者不能解构
    const {obj:{x,y}} = useXxx()
    我想也没人这么闲用嵌套解构语法
    文档只需说不建议返回 reactive 对象就行了,初学者可能会以为 {reactive()} 也是不行的。
    UnluckyNinja
        16
    UnluckyNinja  
       1 天前
    @mizuhashi #14 只要学 vue 之前已经知道 JS 的解构语法,应该不会搞混返回 XX 对象和返回嵌套的 XX 对象的区别
    UnluckyNinja
        17
    UnluckyNinja  
       1 天前
    @UnluckyNinja #16 回错了,想回的是 #15 楼
    riceball
        18
    riceball  
       16 小时 25 分钟前
    你是否真的需要对象中的每一个属性都 reactive?是否存在不需要交互的属性?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1017 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 18:24 PVG 02:24 LAX 10:24 JFK 13:24
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86