本文主要说两件事
1. 如何实现父子组件之间的双向绑定
2. 父组件如何访问子组件的data,method, 子组件如何访问父组件的data,method等
一. 如何实现父子组件之间的双向绑定
案例描述:
父子组件双向绑定
父组件有一个message,
子组件有一个文本框
让他们两个同步变化
实现思路:
1. 子组件接收父组件传递过来的参数 2. 先实现子组件的双向绑定 3. 子组件将数据传给父组件
实现步骤:
第一步: 子组件接收父组件的data
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../../js/vue.js"></script> </head> <body> <div id="app"> 父组件的值: {{message}} <br> <input type="text" v-model="message"></input> <comp1 :cmessage="message" ></comp1> </div> <template id="comp1"> <div style=" 600px; background-color: #085e7d; color: antiquewhite"> <h2>子组件cmessage的值:{{cmessage}}</h2> <br> </div> </template> <script> Vue.component("comp1", { template: "#comp1", props: ["cmessage"], }) const app = new Vue({ el: "#app", data: { message: "hello" } }); </script> </body> </html>
子组件通过属性props: ["cmessage"], 来接收父组件的message属性. 并且父组件修改message的值, 子组件跟随改变
效果如下:
第二步: 实现子组件属性的双向绑定
组件的数据绑定, 使用的也是data属性.但在组件中, data定义为一个方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../../js/vue.js"></script> </head> <body> <div id="app"> 父组件的值: {{message}} <br> <input type="text" v-model="message"></input> <comp1 :cmessage="message" ></comp1> </div> <template id="comp1"> <div style=" 600px; background-color: #085e7d; color: antiquewhite"> <h2>子组件cmessage的值:{{cmessage}}</h2> <h2>子组件cmess的值: {{cmess}}</h2> <br> cmess:<input type="text" v-model="cmess" ></input> <br> </div> </template> <script> Vue.component("comp1", { template: "#comp1", props: ["cmessage"], data() { return { "cmess": this.cmessage } } }) const app = new Vue({ el: "#app", data: { message: "hello" } }); </script> </body> </html>
data中定义了属性cmess, 其值是属性cmessage的值. 我们实现cmess属性的双向绑定.cmess:<input type="text" v-model="cmess" ></input>
效果如下:
这样子组件cmess的双向绑定实现了, 但是我们发现修改父组件的时候,子组件没有变化. 修改子组件的时候, 父组件也没有变化
第三步: 子组件属性变化同步给父组件
子组件属性的改变同步给父组件, 使用的是自定义事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../../js/vue.js"></script> </head> <body> <div id="app"> 父组件的值: {{message}} <br> <input type="text" v-model="message"></input> <comp1 :cmessage="message" @csyncchange="syncchange"></comp1> </div> <template id="comp1"> <div style=" 600px; background-color: #085e7d; color: antiquewhite"> <h2>子组件cmessage的值:{{cmessage}}</h2> <h2>子组件cmess的值: {{cmess}}</h2> <br> cmess:<input type="text" v-model="cmess" @input="changeMessage"></input> <br> </div> </template> <script> Vue.component("comp1", { template: "#comp1", props: ["cmessage"], data() { return { "cmess": this.cmessage } }, methods: { changeMessage(event) { console.log(event.target.value) this.$emit("csyncchange", event.target.value) } }, watch: { cmessage(val, oldval) { console.log(val, oldval) console.log() this.cmess = val } } }) const app = new Vue({ el: "#app", data: { message: "hello" }, methods: { syncchange(value) { this.message = value } } }); </script> </body> </html>
添加子组件的input事件: @input="changeMessage".
changeMessage(event) { console.log(event.target.value) this.$emit("csyncchange", event.target.value) }
然后自定义一个csyncchange事件, 父组件监听这个事件的变化
<comp1 :cmessage="message" @csyncchange="syncchange"></comp1>
父组件自定义一个method方法, 接收事件传递的数据
methods: { syncchange(value) { this.message = value } }
这样就实现了子组件修改cmess的值, 同步给父组件. 效果如下:
但是, 我们发现,在组建同步给父组件没问题, 组件只同步数据给了props属性, 而没有同步给cmess
第四步: 使用watch方法监听props属性的变化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../../js/vue.js"></script> </head> <body> <div id="app"> 父组件的值: {{message}} <br> <input type="text" v-model="message"></input> <comp1 :cmessage="message" @csyncchange="syncchange"></comp1> </div> <template id="comp1"> <div style=" 600px; background-color: #085e7d; color: antiquewhite"> <h2>子组件cmessage的值:{{cmessage}}</h2> <h2>子组件cmess的值: {{cmess}}</h2> <br> cmess:<input type="text" v-model="cmess" @input="changeMessage"></input> <br> </div> </template> <script> Vue.component("comp1", { template: "#comp1", props: ["cmessage"], data() { return { "cmess": this.cmessage } }, methods: { changeMessage(event) { console.log(event.target.value) this.$emit("csyncchange", event.target.value) } }, watch: { cmessage(val, oldval) { console.log(val, oldval) console.log() this.cmess = val } } }) const app = new Vue({ el: "#app", data: { message: "hello" }, methods: { syncchange(value) { this.message = value } } }); </script> </body> </html>
这一步的重点是watch方法. 同步cmessage的值给cmess. 看看效果
以上,完美实现了,父子组件的双向数据绑定.
二. 父子组件的相互访问
如果父组件想要访问子组件的属性和方法, 或者子组件想要访问父组件的属性和方法怎么办呢? 下面来看看:
1. 父组件访问子组件
父组件访问子组件有两种方式
- 1. 使用$children
- 2. 使用@refs
案例: 现在有一个父组件, 想要拿到子组件的方法或者变量.
-
使用$children获取
获取所有的子组件: this.$children 获取某个子组件的属性: this.$children.属性名 获取某个子组件的方法: this.$children.方法名()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h1>第一种方法: 使用children访问子组件</h1> <app1-comp></app1-comp> <app1-comp></app1-comp> <app1-comp></app1-comp> <button @click="btnclick">按钮</button> </div> <template id="comp1"> <div> <p>只有app1才能使用的组件</p> <h2>{{name}}</h2> </div> </template> <script src="../../js/vue.js"></script> <script> const app1Comp = Vue.extend({ template: comp1, data() { return { name : "name名称" } }, methods: { getchange() { console.log("getchange方法") } } }) let app = new Vue({ el: "#app", data: { message: "hello" }, components:{ app1Comp: app1Comp }, methods: { btnclick() { console.log("点击事件", this.$children) console.log("父组件访问子组件的data数据: ",this.$children[1].name) console.log("父组件访问子组件的方法: ",this.$children[1].getchange()) } } }); </script> </body> </html>
在dom中使用了三个comp1组件. 我们可以使用this.$children来获取所有的组件
这里获取到了3个组件, 并打印了第二个组件的名称和方法
-
使用@refs获取属性
使用refs的好处是可以根据组件名称获取. 而不是遍历, 因为遍历的下标时可能修改的.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 父组件访问子组件有两种方式 1. 使用$children 2. 使用@refs 需求: 现在有一个父组件, 想要拿到子组件的方法或者变量. 所以, 我们先定义一个组件. --> <div id="app"> <h1>第二种方法: 使用refs访问子组件</h1> <app2-comp ref="app21"></app2-comp> <app2-comp ref="app22"></app2-comp> <app2-comp ref="app23"></app2-comp> <button @click="btnclick">按钮</button> </div> <template id="comp1"> <div> <p>只有app1才能使用的组件</p> <h2>{{name}}</h2> </div> </template> <script src="../../js/vue.js"></script> <script> const app1Comp = Vue.extend({ template: comp1, data() { return { name : "name名称" } }, methods: { getchange() { console.log("getchange方法") } } }) let app = new Vue({ el: "#app", data: { message: "hello" }, components:{ app1Comp: app1Comp, app2Comp: app1Comp }, methods: { btnclick() { console.log(this.$refs.app21.name) console.log(this.$refs.app21.getchange()) } } }); </script> </body> </html>
这一次我们给组件起了名字, 通过$refs可以指定组件名,获取属性和方法
2. 子组件访问父组件
- 子组件访问父组件使用的是$parent
- 子组件访问根组件使用$root
通常new Vue()也是一个组件, 他是根组件. 如果子组件想要获取根组件的属性和方法,使用@root
下面这个例子, 是子组件comp1里面引用了另一个组件comp2. 在comp2中获取comp1的属性和方法, 使用@parent, 这就是子组件获取父组件的属性和方法
comp2要想获取new Vue()对象的属性和方法, 使用的是$root.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 父组件访问子组件有两种方式 1. 使用$children 2. 使用@refs 需求: 现在有一个父组件, 想要拿到子组件的方法或者变量. 所以, 我们先定义一个组件. --> <div id="app"> <h1>子组件访问父组件</h1> <comp1></comp1> </div> <template id="comp1"> <div> <comp2></comp2> </div> </template> <template id="comp2"> <div> <p>组件comp2</p> <button type="text" @click="btnClick">按钮</button> </div> </template> <script src="../../js/vue.js"></script> <script> const app1Comp = Vue.extend({ template: comp1, data() { return { name: "name名称" } }, components: { comp2: { template: comp2, methods: { btnClick() { console.log(this.$parent) console.log(this.$parent.name) // 获取root元素, 也就是vue元素 console.log(this.$root) console.log(this.$root.message) } } } } }) let app = new Vue({ el: "#app", data: { message: "hello" }, components:{ comp1: app1Comp }, methods: { btnclick() { } } }); </script> </body> </html>
以上就是父子组件之间相互访问的情况