当你能找到这个文章的时候,代表你的页面已经崩溃了,并且很遗憾的告诉你,当我们本地调试代码的时候会发现没问题,当时当你build之后运行......(试试就逝世)
这个黄色警告什么意思啊,翻译过来就是,"组件<Render>
中超过了最大递归更新
。这意味着您有一个反应性效果,它正在改变自己的依赖关系,从而递归
地触发自己。可能的源包括组件模板、渲染函数、更新的钩子或观察者源函数。"
这个问题怎么产生的?
我写一个简单的demo,下面的代码是否存在问题
xml
<template>
<render />
</template>
<script setup lang="tsx">
import {ref} from 'vue'
interface option {
label:string,
value:number
}
const list:option[] = []
for (let i=0;i<10;i++){
list.push({
label:'我是'+i,
value:i
})
}
const listRef = ref<HTMLDivElement[]>([])
const Render = () => {
return (
<>
{
list.map((row:option)=>{
return (
<div
key={row.value}
ref={e=>e&&listRef.value.push(e as HTMLDivElement)}>
{row.label}
</div>
)
})
}
</>
)
}
</script>
如果你把这段代码copy去执行,发现一点问题都没有,并且能够正常运行,那这段代码究竟有没有问题,我们一点点分析
vue2获取ref
vue2中ref获取方式应该不陌生,我这里直接使用vue2.x官方的例子
csharp
// html
<base-input ref="usernameInput"></base-input>
// js
this.$refs.usernameInput
从vue2的官网文档可以看出当单个ref
就是直接访问的对象,结合v-for的时候就是一个数组
那vue2官方都有说,vue3应该
也是有的
vue3获取ref
在vue3的文档上面很遗憾没有ref
结合v-for
的使用。那既然没有结合的例子,那我们就找下jsx的创始人------react16
在react文档中找到应该也满足vue写法例子------回调Refs
原来ref是可以传递回调函数的,通过尝试发现在vue中也是能使用的,那就应该得到我们一开始写的代码。很好,解决获取多个ref的问题了,下班。
但是如果我把这个简单的demo复杂那么一点点会不会出现不一样的问题,请看demo
vue
<template>
<render />
</template>
<script setup lang="tsx">
import {ref} from 'vue'
interface option {
label:string,
value:number
}
const list:option[] = []
for (let i=0;i<10;i++){
list.push({
label:'我是'+i,
value:i
})
}
const listRef = ref<HTMLDivElement[]>([])
const count = ref<number>(0);
const Render = () => {
return (
<>
{
list.map((row:option)=>{
return (
<div
key={row.value}
ref={e=>e&&listRef.value.push(e as HTMLDivElement)}>
{row.label}
</div>
)
})
}
{count.value}
<button onClick={()=>count.value+=1}>添加</button>
</>
)
}
</script>
添加一个累加的按钮,在chrome打开vue
的插件看下setup的变量
很好,数据没有没问题。当我们点击一下按钮,让那个count
进行累加之后再看一遍setup的数据
就会发现,原本listRef
的长度是10,现在是20并且里面也是div
,那这个20 - 10 = 10
的这个差距怎么来的。那就再点按钮,重复我们的操作,就能发现这个长度10的数据也正是div
标签。简单的思考一下,这个问题就是回调Refs的问题。
查看一下vue的生命周期,当组件 mounted
之前执行了一次render
(这里的render
不是demo中的render组件)然后当有数据更新就会再执行一次render
(这一次的render
就额外添加多了一次数据就导致了listRef数据越来越长)
结合以上原因得到修改后的代码
vue
<template>
<render />
</template>
<script setup lang="tsx">
import {ref} from 'vue'
interface option {
label:string,
value:number
}
const list:option[] = []
for (let i=0;i<10;i++){
list.push({
label:'我是'+i,
value:i
})
}
const listRef = ref<HTMLDivElement[]>([])
const count = ref<number>(0);
const Render = () => {
return (
<>
{
list.map((row:option)=>{
return (
<div
key={row.value}
ref={e=>listRef.value[index] = e as HTMLDivElement}>
{row.label}
</div>
)
})
}
{count.value}
<button onClick={()=>count.value+=1}>添加</button>
</>
)
}
// 下面这种方式我不太建议,更改了listRef.value原本的数据原型链,回触发额外不必要的数据响应
// const Render = () => {
// listRef.value = []
// return (
// <>
// {
// list.map((row:option)=>{
// return (
// <div
// key={row.value}
// ref={e=>e&&listRef.value.push(e as HTMLDivElement)}>
// {row.label}
// </div>
// )
// })
// }
// {count.value}
// <button onClick={()=>count.value+=1}>添加</button>
// </>
// )
// }
</script>
以上就是vue3+tsx/jsx+setup语法糖的全部内容,如果你有更好的办法可以评论区留言,我也会虚心的学习