V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhaojingfeng
V2EX  ›  程序员

vue 无法监听实例内部修改的变化

  •  
  •   zhaojingfeng · 2022-04-07 22:02:39 +08:00 · 2026 次点击
    这是一个创建于 953 天前的主题,其中的信息可能已经有所发展或是发生改变。
    const form = reactive(new T_RAGENCY())
    
    export class T_RAGENCY {
     ID:number
     updateID = () => {
    	this.ID++
     }
    }
    
    form.updateID()
    

    watchEffect 或 watch 无法监听到变化. 页面中双向绑定的值也无法变化.

    第 1 条附言  ·  2022-04-08 09:08:40 +08:00
    感谢🙏 @Kawa

    附上解决方案

    export class T_RAGENCY {
    ID:number
    updateID = () => {
    this._.ID.value++
    }

    _:UnwrapNestedRefs<any>
    }

    form._ = {...toRefs(form)}
    15 条回复    2022-04-08 22:39:26 +08:00
    zhaojingfeng
        1
    zhaojingfeng  
    OP
       2022-04-07 22:13:05 +08:00
    有大佬知道怎么解决么
    noe132
        2
    noe132  
       2022-04-07 22:19:08 +08:00
    你试试在 updateID 把 "this" log 出来看看是什么?
    ymcz852
        3
    ymcz852  
       2022-04-07 22:21:17 +08:00
    我猜测是 reactive 返回了一个 T_RAGENCY 实例的代理 proxy 对象(响应式副本),实际上监听的是 proxy 对象 form.updateID() 更新的是原始实例的值,proxy 对象监听不到
    Kiza
        4
    Kiza  
       2022-04-07 22:25:45 +08:00 via iPhone
    我一般不这么用,我找了一个帖子,你看看是否有帮助。https://stackoverflow.com/questions/69050412/vue-composition-api-and-reactive-class
    noe132
        5
    noe132  
       2022-04-07 22:28:24 +08:00
    @ymcz852 你仔细看看代码,updateID 的 this 到底是什么
    Kawa
        6
    Kawa  
       2022-04-07 22:36:19 +08:00   ❤️ 1
    不知道你有没有了解过 Proxy 和 Vue toRaw 的原理.
    简单的用例大概是这样的:
    const ProxyObj = Proxy(obj, {
    get(target, property) {
    console.log("read: "property);
    return target[property];
    },
    set(target, property, value) {
    console.log("write: "property);
    target[property] = value;
    }
    });
    Vue 的 reactive 基本实现原理就是 Proxy.
    想象上面 Proxy 的用例, 如果直接对 obj 写入, 那显然不会触发 Proxy 里的 set handler.
    如果对 ProxyObj 写入, 那么就会触发 set handler.
    同样的道理, 如果你想触发 reactive 的更新, 那么你就需要对 reactive 包裹过的对象执行写入, 而不是对原对象.

    而且我认为 reactive 应该仅存数据, 而不应该包括方法.
    Kawa
        8
    Kawa  
       2022-04-07 22:41:00 +08:00
    当然, 如果你非要这样做, 也是有办法的.
    比较简单的方法就是做一个 factory, 通过 factory 创建裸对象, 再将其用 reactive 包裹, 最后向其注入 mutation 方法.
    非要 new 也不是不行, 可以在类的内部自行维护一个 reactive 对象, 然后定义类属性的 getter 和 setter, 将操作映射到 reactive 对象上.
    nomagick
        9
    nomagick  
       2022-04-07 22:48:33 +08:00
    assert(this instanceof T_RAGENCY)

    括号函数 this 是在构造的时候决定的,但 vue 拿到你这个对象之后是把上面的属性和方法拿走,舍弃了最初的实例。
    之前 vue 就有这个问题,没有维护原型链
    noe132
        10
    noe132  
       2022-04-07 22:58:32 +08:00   ❤️ 1
    @ymcz852 用了这么久 JS ,我居然把这个搞错了~

    这个问题我终于搞明白了。因为 class field 用的是 [[Define]] ( https://github.com/tc39/proposal-class-fields)
    class { updateId = () => console.log(this) }
    相当于
    class {
    constructor(){
    Object.defineProperty(this, 'updateId', { value: () => console.log(this), enumerable: true, configurable: true, writable: true });
    }
    }

    此时这个方法相当于是在构造函数内定义的,箭头函数内的 this 绑定成了构造函数执行时的 this 。
    当 class instance 被 proxy 包了一层后,调用 updateId 拿到的 this 是原对象而不是 proxy ,导致更新没法被检测到。

    如果把方法定义成 class method ,this 就是 proxy 。
    zhaojingfeng
        11
    zhaojingfeng  
    OP
       2022-04-08 08:52:17 +08:00
    感谢🙏@Kawa

    最后解决方案可行

    export class T_RAGENCY {
    ID:number
    updateID = () => {
    this._.ID.value++
    }
    _:UnwrapNestedRefs<any>
    }

    form._ = {...toRefs(form)}
    Curtion
        12
    Curtion  
       2022-04-08 10:09:55 +08:00
    10 楼说得很清楚了,更方便的是更改 updateID 方法的定义方式:
    export class T_RAGENCY {
    ID:number
    updateID() {
    this.ID++
    }
    }
    Kawa
        13
    Kawa  
       2022-04-08 11:14:39 +08:00 via Android
    @Curtion
    你这么说我才发现他用的方法是用属性的形式去定义的…
    这种写法正常写还真写不出来吧
    daolanfler
        14
    daolanfler  
       2022-04-08 13:50:59 +08:00
    dreamerblue
        15
    dreamerblue  
       2022-04-08 22:39:26 +08:00
    一看看到箭头函数就猜到会出问题...不知道是不是从 React 类组件带过来的习惯?写 Vue 就用最符合直觉的方式去定义方法就好了,无论是写选项式组件还是类组件 /服务都是一样的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2868 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 08:42 · PVG 16:42 · LAX 00:42 · JFK 03:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.