Vue父子组件数据双向绑定(父传子、子传父)及ref、$refs、is、:is的使用与区别
Nanchen_42 人气:0既然有父传子那么肯定有子传父,有子传父肯定也有两者之间相互绑定
这里我们先看一下子传父的写法:
一、子传父:$emit()
看代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>子传父</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <style type="text/css"> button { margin-left: 5px; } </style> <body> <div id="app"> <cpn1 @itemclick="cpnclick"></cpn1> </div> <template id="cpn1"> <div> <button type="button" v-for="item in menu" :key="item.id" @click="btnclick(item)">{{item.name}}</button> </div> </template> <script type="text/javascript"> const cpn = { template: "#cpn1", data() { return { menu: [{ id: 'one', name: '首页' }, { id: 'two', name: '分类' }, { id: 'three', name: '购物' }, { id: 'four', name: '我的' }, ], } }, methods: { btnclick(item) { this.$emit('itemclick', item) // 子传父 在子组件中做一个点击事件通过$emit派发出 给父组件 同时可以携带参数 } } }; const vm = new Vue({ el: '#app', methods: { cpnclick(item) { console.log('cpnclick' + item.name); } }, components: { "cpn1": cpn } }) </script> </body> </html>
打印效果:
两者之间的关系:
1、父组件可以使用 props 把数据传给子组件。
2、子组件可以使用 $emit 触发父组件的自定义事件。
二、监听原生点击事件:.native
不加.native时,不会触发原生的点击事件
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>不加native修饰符</title> </head> <style> div{ cursor: pointer; } </style> <body> <div id="app"> <cpn @click="handelClick"></cpn> // 这里没有加native修饰符 </div> <!-- 子组件 --> <template id="cpn"> <div> 我是子组件 </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script> const cpn = { template:'#cpn', } const app = new Vue({ el: "#app", methods: { handelClick(){ console.log('click'); } }, components:{ cpn } }) </script> </body> </html>
效果如下:
不加修饰符是不会监听到原生点击事件的。
如果是加了.native修饰符时:
添加方法:
<cpn @click.native="handelClick"></cpn>
效果如下图所示:
三、组件通信的案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id="app"> <cpn :number1="num1" :number2="num2"></cpn> </div> <template id="cpn"> <div> <h2>{{datanum1}}</h2> <input type="text" v-model="datanum1"/> <h2>{{datanum2}}</h2> <input type="text" v-model="datanum2"/> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script> const cpn = { template:'#cpn', data(){ return { datanum1:this.number1, datanum2:this.number2, } }, props:{ number1:{ type:[String,Number] }, number2:{ type:[String,Number] }, } } const app = new Vue({ el: "#app", data() { return { num1:1, num2:2 } }, components:{ cpn } }) </script> </body> </html>
效果如下:
四、实现父子之间的值的双向绑定
在子组件中添加监听器,利用props和$emit来进行父子之间的双向绑定
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id="app"> <cpn :number1="num1" :number2="num2" @dataclick1="changeClick1" @dataclick2="changeClick2"></cpn> </div> <template id="cpn"> <div> <h2>{{datanum1}}</h2> <h3>number1:{{number1}}</h3> <input type="text" v-model="datanum1" /> <h2>{{datanum2}}</h2> <h3>number2:{{number2}}</h3> <input type="text" v-model="datanum2" /> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script> const cpn = { template: '#cpn', data() { return { datanum1: this.number1, datanum2: this.number2 } }, props: { number1: { type: [String, Number] }, number2: { type: [String, Number] } }, watch: { datanum1(n) { console.log('datanum1被监听了'); this.$emit('dataclick1', n / 100) }, datanum2(n) { console.log('datanum2被监听了'); this.$emit('dataclick2', n * 100) } } } const app = new Vue({ el: "#app", data() { return { num1: 1, num2: 2 } }, methods: { changeClick1(value) { this.num1 = value }, changeClick2(value) { this.num2 = value } }, computed: { }, components:{ cpn } }) </script> </body> </html>
效果
一个是除10一个是
五、父访问子 $refs
JavaScript中获取元素可以使用document.querySelector,那可以在Vue中使用吗?
我们可以测试一下
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <p id="bb" @click="handelClick" ref="aa">Nanchen</p> </div> <script type="text/javascript"> const vm = new Vue({ el: '#app', data() { return { } }, methods: { handelClick(){ var bb = document.querySelector('bb'); console.log(bb); } } }) </script> </body> </html>
打印结果:
答案是可以的,但是如果使用原生JS获取元素的话,那么用Vue就没有意义了,Vue中有特定的语法
官网解释:
$refs方式:ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <p id="bb" @click="handelClick" ref="aa">Nanchen</p> </div> <script type="text/javascript"> const vm = new Vue({ el: '#app', data() { return { } }, methods: { /* handelClick(){ var bb = document.querySelector('bb'); console.log(bb); } */ handelClick() { console.log(this.$refs.aa); } } }) </script> </body> </html>
效果与上图一致:
六、使用$refs获取组件中的值
既然可以获取普通元素那么也可以获得组件中的元素或者值
看这个例子:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id="app"> <cpn ref="aaa"></cpn> <button @click="handelClick">点击</button> </div> <!-- 子组件 --> <template id="cpn"> <div> 我是子组件 </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script> const cpn = { template:'#cpn', data(){ return { name:'我是子组件的name' //获取子组件的属性 } }, } const app = new Vue({ el: "#app", methods: { handelClick(){ console.log(this.$refs.aaa.name); } }, components:{ cpn } }) </script> </body> </html>
效果如下:
下面看一个扩展:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <count ref="one" @change="handclick"></count> <count ref="two" @change="handclick"></count> <h2>{{total}}</h2> </div> <script type="text/javascript"> Vue.component('count',{ template: `<div @click="handclick"> {{number}} </div>`, data(){ return{ number:0 } }, methods:{ handclick(){ this.number++; this.$emit('change') } } }) const vm = new Vue({ el:'#app', data(){ return{ total:0 } }, methods:{ handclick(){ this.total= this.$refs.one.number + this.$refs.two.number } } }) </script> </body> </html>
效果如下:
不仅如此,ref还可以调用组件中的方法:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id="app"> <hello-world ref="hello"></hello-world> <button @click="getHello">获取helloworld组件中的值</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script> Vue.component('helloWorld',{ template:`<div>helloWorld</div>`, data(){ return { number:0 } }, methods:{ /*handelClick(){ console.log('我是子组件的方法'); }*/ } }) const app = new Vue({ el: "#app", data: { }, methods: { getHello(){ /* this.$refs.hello.handelClick(); */ console.log(this.$refs.hello.$el.innerHTML); } }, }) </script> </body> </html>
效果如下:
六、is与:is
is
作用:解决了html模板的限制。
看下面这段代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <table> <row></row> </table> </div> <script type="text/javascript"> Vue.component('row',{ template: '<tr><td>111</td></tr>' }) const vm = new Vue({ el:'#app', data(){ return{ } }, }) </script> </body> </html>
会正常输出
但是:
会发现tr并不在table中,
解决办法:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id="app"> <table> <tr is="row"></tr> </table> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script> Vue.component('row', { template: '<tr><td>111</td></tr>' }) const app = new Vue({ el: "#app", }) </script> </body> </html>
打印结果:
用:is还可以用来绑定动态组件
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <component :is="type"></component> <button type="button" @click="changeClick">切换</button> </div> <script type="text/javascript"> // 这里要定义两个全局组件 Vue.component('child-one',{ template:'<div>child-one</div>' }), Vue.component('child-two',{ template:'<div>child-two</div>' }) const vm = new Vue({ el:'#app', data(){ return{ type:'child-one' } }, methods:{ changeClick(){ this.type = this.type === 'child-one' ? 'child-two' :'child-one' } } }) </script> </body> </html>
效果如下:
加载全部内容