1、实例代码
首先一个测试结果:
1、在组件模版中元素定义的ref属性,会在编译阶段生成对应的props数据,作为参数传入到创建这个ref所在元素的vnode的方法中。
2、如果ref定义在一般的元素中,那么ref就指向这个元素的dom实例,如果ref定义在子组件上,那么ref就指向这个子组件实例的一个代理属性proxy上(前提是子组件没有通过expose方法指定暴露的信息,如果指定了,就只能有些暴露的属性,其他数据没有)
3、如果在setup函数返回值中有key和这个ref的名称一样的话,那么ref引用的数据就会覆盖setup返回值原来的key对应的值。
4、ref引用的值不管怎么样都会暴露到父组件(如果子组件定义了 ref)的instance.refs属性中。可以在组件定义的methods方法中通过this.$refs获取到。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<style>
.red{color:red}
</style>
<!--
<script src="https://unpkg.com/vue@3.4.30/dist/vue.global.js"></script>
-->
<script src="./vue/vuetest.global.js"></script>
</head>
<body>
<div id="app">
</div>
</body>
<script type="module">
const app = Vue.createApp({
name:"root-component",
props: {///这个定义 会赋值给组件实例instance中的propsOptions(属性的定义,数据类型,是否必填等)
rootprops1: String,
},
renderTracked:function(e){
console.log("------------renderTracked,参数e: "+e);
},
data:function(){
return {
msg:'hello vue3'
}
},
methods: {
dealChange:function(str){
alert("子组件发射了chanage事件: "+str);
},
dealPutinfo:function(str){
alert("子组件发射了putinfo事件: "+str);
},
divtestdata1:function(str){
console.log(this.$refs.mydiv);
console.log(this.$refs.tcinstance);
console.log("--------------------");
console.log(this.mydiv);
console.log(this.tcinstance);
}
},
mounted:function(){
console.log("mount--------------",this.$refs.mydiv,this.$refs.tcinstance);
},
beforeCreate:function(){console.log("root-beforeCreate");},
setup:function(props,ctx){
///执行setup函数时, 组件已经实例化了
// context中有 props, attrs, slots, emit 和 expose
通过这种方式注册的钩子函数获取不到this,为什么? vue内部创建注册这个钩子函数的时候没后指定bind()
//通过在组件定义对象定义mounted钩子函数可以获取到this,
Vue.onMounted((args) => {
console.log("onMounted--------------");
});
//这个mydiv名称要和下面元素中定义的ref的名称一致,然后在setRef函数中就会根据这个名称 把ref值绑定到mydiv中
//名称不一致的话,那么在setupState中就获取不到mydiv关联的dom或者实例代理对象了
//tcinstance 也是和mydiv一样
return {"mydiv":"","tcinstance":"",setupData:{data1:"data1",data2:"data2"}};
},
///用到了template 需要引入的vue中有 编译模块, 或者在构建阶段完成编译
//这些信息具体是在哪里解析的呢: 在执行父组件的render函数
template:`<div >
<div ref="mydiv" id="root-chidren-div-id" style="color: blue;">----</div>
<button @click="divtestdata1" >按钮</button>
<test-component ref="tcinstance" v-on:chanage='dealChange' @putinfo="dealPutinfo" >
<template v-slot:other="slotProps"><div>我是父组件的other插槽内容测试锻炼{{msg}}--{{slotProps.url}}</div></template>
</test-component>
</div>`,
},{rootprops1:"rootmsg1",rootprops2:"rootmsg2"});
//
var testComponet={
name:"test-component",
props: {
testpmsg1: String,
onPutinfo:Function
},
data:function(){
return {
cmsg:"shengbinqian",
cdata:"zhongguoren",
url:"http://www.baidu.com"
}
},
methods: {
testdata1:function(str){
console.log("testdata-"+str);
this.$emit("chanage","你好我是子组件信息chanage");
},
testdata2:function(str){
this.$emit("putinfo","你好我是子组件信息putinfo");
}
},
template:`<div class='border:solid 1px red'>
<div>我是子组件testComponent---{{cmsg}}</div>
<div><slot name="other" :url="url"></slot></div>
<div><button @click="testdata1" >chanageEmit</button></div>
<div><button @click="testdata2" >putinfoEmit</button></div>
</div>`,
setup:function(props,ctx){
let data={tname:"qianqian"};
//如果父组件的模版定义中,设置了子组件的ref属性,那么在父组件中就可以获取到这个子组件的实例代理对象
//但是如果子组件要想暴露它想暴露的数据,那么就可以适用expose()方法,如果设置了这个方法,那么父组件就只能访问这些expose的数据
//起到一个屏蔽的作用
ctx.expose(data);
console.log("testComponet-setup");
}
};
///会在app的上下文对象APPContext中的components中保存testComponet
app.component("test-component",testComponet);
app.mount('#app');
</script>
</html>
2、大概看下ref的实现原理