V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
leaveeel
V2EX  ›  Vue.js

vue 指令更新问题

  •  
  •   leaveeel · 2 天前 · 1545 次点击
    昨天下班前碰到一个问题,我是自己写的 loading 组件,注册了全局指令。在指令引用的时候如果 loading 是对象在更新属性时指令方法 updated 不会捕获到修改,v-loading 用扩展运算或者在 template 里引用则能正确更新状态。能在哪里做些调整吗?

    ![code]( https://imgur.com/a/1GmgAgO)

    ![direactive]( https://imgur.com/a/mLmEw9I)



    另外,不确定自己封装通用组件的必要性,现在的是 C 端项目有 ui 要求没用模板,但也有像 form 、table 、dialog 等模块,因为工作量不大我现在都单独抽出了组件库写了文档。越优化越觉得和 element 的组件功能是差不多的只是 ui 不同,引用 element 然后自定义样式,不满足的功能也可以二次封装自己添加,按需加载打包可能更方便?
    15 条回复    2025-04-01 18:23:28 +08:00
    wangtian2020
        1
    wangtian2020  
       2 天前   ❤️ 1
    let loading = ref(false)
    ljl024
        2
    ljl024  
       2 天前   ❤️ 1
    在 mounted 和 updated 中 watch 一下 bindingValue ,根据这边变量的用法,属性变化不会触发 updated 。用 element-plus 覆盖样式也挺香,或者 fork 下来改改
    ooo4
        3
    ooo4  
       2 天前   ❤️ 3
    https://github.com/vuejs/core/blob/f6e84af30aeffd281aebbab02b0e623e5fc159e0/packages/runtime-core/src/directives.ts#L95

    有一个 deep 的配置,如果加一个 deep:ture 就可以了,因为在 mount 时,默认没有对这个响应式数据的各个 key 进行依赖收集,所以才没有触发对应的 update 钩子,

    如果你在模板中使用使用{{loadingValueObj}},是直接编译成_toDisplayString($setup.loadingValueObj),里面是使用的 JSON.stringify ,就对各个属性进行了依赖收集了
    leaveeel
        4
    leaveeel  
    OP
       2 天前
    @wangtian2020 boolean 是正常的,用 object 是因为要设置其他属性,已经按 3#的方法修改了
    leaveeel
        5
    leaveeel  
    OP
       2 天前
    @ljl024 原本建项目的时候就在想要不要引 elementplus ,因为以前也一直用这个,原本二次封装的也能直接拿来用。最后还是感觉太重了就自己撸,现在想想可能草率了。XD
    leaveeel
        6
    leaveeel  
    OP
       2 天前
    @ooo4 非常感谢!这是不爱读源码的坏处吗 XD
    NerbraskaGuy
        7
    NerbraskaGuy  
       2 天前
    ref 也能处理 object 啊,reactive 就是因为使用中容易出现像你这种丢失响应性的问题,其实官方更推荐 ref
    ooo4
        8
    ooo4  
       2 天前   ❤️ 1
    @NerbraskaGuy 这个问题的本质是,渲染函数的副作用没有对这种情况进行依赖收集,这就会导致组件始终不会更新了,那么自定义指令的 update 钩子也当然无法执行了
    leaveeel
        9
    leaveeel  
    OP
       2 天前
    @NerbraskaGuy ref 之前试过了也不会更新, @ooo4 说的是对的
    wangtian2020
        10
    wangtian2020  
       2 天前
    ref 和 reactive 的本质区别就是 ref 一定不会出错 reactive 就是会出现意料之外的情况
    只要 ref.value 去重新赋值,一定触发更新。我从来不在项目里写 reactive
    leaveeel
        11
    leaveeel  
    OP
       2 天前
    @NerbraskaGuy
    @wangtian2020
    修正一下我 9#的错误回复,我又试了一下 ref 定义 loading 变量,在对 loading.value 整体重新赋值后确实会触发更新,我的结论是基于对 loading.value.xx 赋值,这是不能捕获修改的,原因就是 @ooo4 说的创建时默认没有对 object key 监听,进行引用和扩展类似创建新对象则会触发 update ,deep 和`watch binding`则是主动激活监听。

    ```
    const loading = ref(false)
    loading.value = { loading: true, text: 'loading...' }
    // 状态更新正常

    const loading = ref({ loading: false })
    loading.value = { loading: true }
    // 状态更新正常

    const loading = ref({ loading: false })
    loading.value.loading = true
    // 无法更新
    ```

    不过从组件的角度肯定是不限制 ref 和 reactive 更合适,因为没有严格的代码规范并不能要求所有人都用 ref.value 赋值。
    wangtian2020
        12
    wangtian2020  
       2 天前
    loading.value.loading = true 按照道理是会触发更新的,我一直所有代码都是这么写的,肯定是哪里写错了引用变了,Vue devtools 你点进去看看就知道了
    喜欢用 reactive 就用,就像有些人喜欢父子传值传进去再 watch 写的复杂的不得了,我是感觉不如 expose 个方法出去让父组件 ref 调用方法一次性传值。这种写法出来的程序只能在中午运行,早晚出问题
    sakura1988
        13
    sakura1988  
       2 天前
    还 ref 一定不出错,对象用 ref 声明,ref.value 就是个 reactive ,用得不好一样出错
    Tokin
        14
    Tokin  
       2 天前
    @leaveeel watch 默认下不会深入观察 ref ,要加 deep 参数:
    ```javascript
    watch(loading, () => {
    console.log('ref deep watch')
    }, { deep: true })
    ```
    SanjinGG
        15
    SanjinGG  
       2 天前
    @leaveeel 这句话并不对啊,确实没有规范要求用 ref 还是 reactive ,但作为组件,我只需要规定 binding.value 是一个 bool 类型就好了,不管你是基本类型还是对象类型,如果用对象,就应该吧 loading 这单个属性传递给我。参考 element ui ,你的其他 text ,color ,size 属性,应该通过 el 的 dataset 获取,不应该在 binding.value 中。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1569 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:29 · PVG 00:29 · LAX 09:29 · JFK 12:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.