vue3 里面使用 jsx 解构属性,响应式丢失了,怎么处理

2022-12-31 21:54:17 +08:00
 daiwenzh5

如:

const someprops = useAnyHooks();
// someprops 值
const getOne = computed(() => anyvalue);
someprops = {
	one: getOne.value,
}
// 因为存在多个属性,所以直接解构方便一点,但是 one 的响应式好像没了,anyvalue 不会触发更新
return () => (
	<component {...someprops} ></component>
)
// 这样是正常的
return () => (
	<component one={one.value} ></component>
)

这个属性 someprops 里面 one 属性,丢失了响应式,这个怎么处理优雅一点?

5547 次点击
所在节点    Vue.js
78 条回复
shakukansp
2023-01-02 21:57:05 +08:00
@johnkiller 主要问题还是引用链断裂而不是没有在 render 里面触发 getter

原段代码就算把 someprops 改成 reactive ,依然无法通过修改 getOne.value 的值来触发视图更新,就算 getOne.value 里面的值是个对象也一样,具体会表现为修改 getOne.value 中对象的子属性可以触发更新,但是给 getOne.value 赋值一个不同引用的对象就会没法更新视图。

reactive(someprops)
someprops.one = getOne.value 和 someprops.one = getOne ,是两码事,后者 vue 会在解包 ref 时额外进行数据代理的链接
shakukansp
2023-01-02 22:03:55 +08:00
OP 不用很迷惑

网上的文章说的 vue3 中失去响应式的陷阱

无一例外都不是陷阱,是符合 JS 基础特性的预期内行为

只要你理解好引用传递,面对这个现象就不会感觉奇怪
johnkiller
2023-01-02 23:02:02 +08:00
@shakukansp 你所阐述的引用逻辑没问题,但还是只说对了 js 特性,没说 Vue 特性,没解决问题。

OP 这里提的问题的根本原因还是 Vue 依赖收集。要解决 OP 的问题,就是需要在 render 里触发对代理变量的 getter ,不然你没有任何解法。

“主要问题还是引用链断裂而不是没有在 render 里面触发 getter”,对于这句话,可以上最简单的例子:
https://codesandbox.io/s/vue3-jsx-demo-forked-biscpk?file=/src/components/Demo1.vue

引用链断裂不是导致失去响应式的原因,在以上的例子里,我创建了一个全新的和 count 毫不相干的对象,依然能够保持响应式。

我给 Vue3 的 @vue/reactivity 模块贡献过 PR ,还是能唠几句的。
shakukansp
2023-01-02 23:10:11 +08:00
@johnkiller 你在说啥,大哥,你这是在 return 的函数里面声明的对象,本身这个函数就是对值的引用了,
你在 return 函数外面声明试试?
shakukansp
2023-01-02 23:11:15 +08:00
@johnkiller 你发的这个例子和楼主的原代码根本就是两码事了

本质就是引用问题
agileago
2023-01-02 23:12:44 +08:00
忘记什么 ref,reactive ,来用 vue3-oop 体验自然的编程
shakukansp
2023-01-02 23:13:53 +08:00
你这个代码能生效是因为在函数里面的 count.value 是引用传递,而不是值传递啊

传 () => string 能和直接传 string 一样吗
johnkiller
2023-01-02 23:19:16 +08:00
@shakukansp 请看 OP 的需求,是解决问题。他的源代码在 #15

我在他的原代码里,创建了一个全新、互不相干的引用对象,不是同样解决了 OP 的问题?
你不需要纠结引用问题,你说的都对。但这里讨论的是 Vue 特性,以及解决 OP 的问题:如何优雅的解构传值。
johnkiller
2023-01-02 23:20:17 +08:00
@daiwenzh5 #27 那你说里面的 count 和 doubleCount 和原先 ref 的还有什么联系?
johnkiller
2023-01-02 23:24:57 +08:00
@shakukansp 我用 inc 函数改了原先的 count = ref(0),为什么我新创建的 count: count.value 还能拿到新值,不是一个完全不相干的 “基础值” 吗?

所以本质还是 render 函数作为原先 count = ref(0) 的依赖,发生变更 => render 重新被执行 => 才拿到的新值 => 返回 vdom => diff => 渲染真实 dom 。
johnkiller
2023-01-02 23:30:15 +08:00
@shakukansp #27 强行强调你的“引用”毫无意义,建议多看看 Vue 源码再来。
shakukansp
2023-01-02 23:32:12 +08:00
@johnkiller 我的意思是解决这个问题并不需要懂 vue 的响应式原理,丢失响应式就是个引用链断裂的问题

你把变量声明写在函数里,也是建立了对 js 对象值的引用,这和 vue 并没有什么关系

要让它按楼主想的一样运行实际操作就是像官方文档一样照本宣科用 reactive 包一下 ref ,这样 vue 自然有内部操作去数据代理

const a = reacvtive({ c:1 })
const b = ref(3)
a.c = b.value
a.c = b
console.log(a)得到的结果都是 a 是 { c:3 } 用脚想都知道直接传包装对象 ref 的时候 vue 会有特殊的操作
不需要懂什么响应式原理,要的就是一句官方文档的说明

OP 写过 golang 就能对 js 这个基本特性有更深的理解
shakukansp
2023-01-02 23:33:57 +08:00
@johnkiller 回你 30 楼 我都说了,你写在函数里面当然没问题,是引用传递,你写函数外面还能这样?你再想想?还要我多看看源码,自己多提升一下 js 基础吧
shakukansp
2023-01-02 23:39:33 +08:00
写函数外面然后在函数里面{...props}
是不是也调用 props 的 getter 了?那为什么写函数外面运行 inc 触发不了视图更新?不就是引用没了吗
johnkiller
2023-01-02 23:44:20 +08:00
@shakukansp 把变量声明写在函数里,就是为了正常收集依赖。函数内创建的都是全新的独立的对象,除了 inc 函数其它都是独立的值,没有任何引用。

“你把变量声明写在函数里,也是建立了对 js 对象值的引用”,暴露了你的水平。
至于你说 props 的 getter ,我指的是原先 ref 的 getter ,而不是新创建的对象的 getter ,况且它也没有 getter 。

真回去好好补下基础吧,你的无效阐述解决不了任何问题。
johnkiller
2023-01-02 23:45:16 +08:00
@shakukansp 仅存的一点“大概,可能,应该是,绝对是这样”,在你的脑子里支撑着你继续说下去。
shakukansp
2023-01-02 23:47:26 +08:00
@johnkiller emmm 只能说得亏是 setup 里面 return 是个函数你才能得瑟,那你倒是讲讲 vue 为什么要你 return 一个函数啊
shakukansp
2023-01-02 23:53:47 +08:00
let a = 1
const fn = () => { console.log(a) }
fn()
a = 2
fn()

为啥两次打印的结果不一样啊
楼主你不要听楼上那个 pr 过 vue/reactive 的
johnkiller
2023-01-02 23:59:09 +08:00
@shakukansp 因为 setup 用来初始化,创建内部的变量,render 函数产生 vdom 。试想若不返回函数,把 setup 直接当 render 函数,返回 jsx ,vdom 。那每次重新渲染 => 调用 setup => 里面的变量(ref)被重新创建,那就没法保持状态。

React 它那套 hook 方案就解决了这个问题,所以可以在函数里声明状态,并立即返回 jsx ,而不是函数。

@shakukansp 至于#38 ,那么把你的 fn 当作 render 函数,怎么让 render 函数在 a 被改变的时候自动再次调用,保证响应式不丢失?是不是又回到了依赖收集问题?怎么收集?还是回到了原点:要在函数内触发 getter 。你别无他法。

强行解释毫无意义,全靠你那点 JS 第六感撑着。
johnkiller
2023-01-03 00:04:36 +08:00
@shakukansp 兄弟,俺睡觉了。也真不用急眼,没说你那套引用逻辑有问题,我你所有的 js 逻辑都没问题,但并无法支撑本楼的主题,并没有说到 Vue 的点子上。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/905838

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX