<span :="refApi">区域</span>
<span ref="ref1">位置</span>
<span :="{ ref: 'ref2' }">操作</span>
const ref1 = ref();
const ref2 = ref();
const ref3 = ref();
const refApi = {
ref: 'ref3'
};
console.log(ref1, ref2, ref3);
在开发环境下 ref1, ref2, ref3
皆符合预期指向 span
。
但是打包后的生产环境就只有 ref1
正常指向 span
, ref2, ref3
皆为 undefined
。
为什么表现不一致?
因为
:="data"
简写语法的意思是将data
中的每一个属性都当做prop
传入,比如data
是这么一个对象{name: ref1, age: ref2}
等同于:name="ref1" :age="ref2"
,注意script
中的ref
和template
中的ref
不是同一个概念,模板中是实例引用,编译的时候会将实例绑定到对应的data
上,然后因为你是用的:=
对象绑定,而你绑定的对象里并没有ref
(注意这里的ref
指的是模版上那个ref
),所以2、3实际是没有任何数据绑定的,而ref1
因为你是固定的属性绑定,不是动态的,能对应上,也就能绑上了。这时候你可能会疑惑,
:={ref: 'ref2'}
以及refApi = {ref: 'ref3'}
这不是符合我上述说的吗?因为你这里
ref
虽然对应是模版的ref
但是你绑定的是字符串,不是某一个ref变量
这就好比是ref="'ref2'"
【注意有两引号】知道了以上的内容那么就可以改代码了:
const ref3 = ref(); const refApi = {ref: ref3}
;这么改ref3
就有值了但如果你将模版中的
:={ref: ref2}
你会发现还是没值,为什么呢?vue
的const r = ref()
返回的其实是一个对象,当我们在script
中要访问真正的值的时候是要r.value
的方式访问的,但是为了方便,在模版里其实我们不需要.value
,vue
实际会帮我们去做这件事,所以:={ref: ref2}
实际上相当于ref="ref2.value"
,绑定原始值肯定是不行的,所以我们这里需要用到函数的方式来绑定即:={ref: el => ref2 = el}
完整修改如下:
然后我建议你去VuePlayground看看编译后的代码,有助于你理解我上面所说的
https://cn.vuejs.org/guide/essentials/template-syntax.html#at...
https://cn.vuejs.org/guide/essentials/template-refs.html#func...
由于昨晚太晚了,只回答了为什么生产不行的原因,今天补充说明下为什么开发环境是可以的:
首先,
vue
的script
支持setup
属性从而简写一些代码,所以就会存在两种编译结果:这是没有
setup
的script
:这是有
setup
的script
:关键原因就出在这两种不同结果上,实际上除了昨天我说的那种改法,还有一种改法是改成没有
setup
的script
形式,也就是上面的图1那样,然后你就会发现,不管开发环境还是生产环境(即build
之后)都有值了。有没有察觉到什么?没错,在dev
模式下,vite
编译的结果就是没有setup
的结果!!实际上你的确可以将
ref
定义成字符串,我们知道在vue
实例上其实有这么个属性——refs
,它其实就是所有模版中ref
的集合,在vue2
时代我们都是this.$refs.ref1
这么访问的,在vue3
中虽然不太需要了,但是实例上依然还是有这么个属性,其实你定义的字符串代表的就是这个refs
的key
当你绑定的是一个
ref对象
的时候则会多一步,将这个节点赋值给ref
,上图中ref1
也存在于refs
中;当ref
是字符串的时候,除了绑定refs
,vue
还会检查data
【vue2叫法,vue3中就是setup() { return data }
里return
出来的对象】中是否有同名的key
,有的话也会给该属性绑定:这也就解释了为什么改成无
setup
的script
版本就可以了。有的同学可能会疑惑那为什么<script setup>
的版本不行呢?不也是定义了同样的变量名const ref2 ref3
吗?这是因为它们只是变量,build
后是通过闭包访问的,并不是vue
实例上的state