1.自定义事件-$emit
子组件用 $emit() 来触发事件,父组件用 $on() 来监听子组件的事件。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app">
<p>总数:{{total}}</p>
<my-component @increase="handleGetTotal" @reduce="handleGetTotal">
</my-component>
</div>
<script src = "../lib/vue.min.js" ></script>
<script>
Vue.component('my-component', {
template:
`
<div>
<button @click="handleIncrease">+1</button>
<button @click="handleReduce">-1</button>
</div>
`,
data() {
return {
counter: 0,
}
},
methods: {
handleIncrease() {
this.counter++;
this.$emit('increase', this.counter);
},
handleReduce() {
this.counter--;
this.$emit('reduce', this.counter);
},
}
})
var app = new Vue({
el: '#app',
data: {
total: 0,
},
methods: {
handleGetTotal(total) {
this.total = total;
}
},
})
</script>
</body>
</html>
2.使用v-model
还是点击按钮+1的效果,不过这次组件 $emit() 的事件名是特殊的 input ,在使用组件的父级,并没有在<my-component>上使用@input="handler",而是直接用了 v-model 绑定的一个数据 total。
源码如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app">
<p>总数:{{total}}</p>
<my-component v-model="total"></my-component>
</div>
<script src = "../lib/vue.min.js" ></script>
<script>
Vue.component('my-component', {
template:'<button @click="handleClick">+1</button>',
data(){
return {
counter:0,
}
},
methods:{
handleClick(){
this.counter++;
this.$emit('input',this.counter);
},
}
})
var app = new Vue({
el: '#app',
data: {
total: 0,
},
methods: {
handleGetTotal(total) {
this.total = total;
}
},
})
</script>
</body>
</html>
v-model 还可以用来创建自定义的表单输入组件,进行数据双向绑定。
代码如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app">
<p>总数:{{total}}</p>
<my-component v-model="total"></my-component>
<button @click="handleReduce">-1</button>
</div>
<script src = "../lib/vue.min.js" ></script>
<script>
Vue.component('my-component', {
props:['value'],
template:'<input :value="value" @input="updateValue" >',
methods:{
updateValue(event){
this.$emit('input',event.target.value);
}
}
})
var app = new Vue({
el: '#app',
data: {
total: 0,
},
methods: {
handleReduce(){
this.total--;
}
},
})
</script>
</body>
</html>
3.非父子组件通信
推荐使用一个空的 Vue 实例作为中央事件总线(bus),也就是中介。
点击传递事件,
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app">
{{message}}
<component-a></component-a>
</div>
<script src = "../lib/vue.min.js" ></script>
<script>
var bus = new Vue();
Vue.component('component-a', {
template: '<button @click="handleEvent">传递事件</button>',
methods: {
handleEvent(){
bus.$emit('on-message','来自组件component-a的内容');
}
}
})
var app = new Vue({
el: '#app',
data: {
message: '',
},
mounted(){
//监听来自 bus 的事件 on-message
bus.$on('on-message',(msg)=>{
this.message = msg;
})
}
})
</script>
</body>
</html>
4.父链
在子组件中,使用 this.$parent可以直接访问该组件的父实例或组件,父组件也可以通过 this.$children 访问它所有的子组件,而且可以递归向上或向下无限访问,直到根实例或最内层的组件。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app">
{{message}}
<component-a></component-a>
</div>
<script src = "../lib/vue.min.js" ></script>
<script>
Vue.component('component-a', {
template: '<button @click="handleEvent">通过父链直接修改数据</button>',
methods: {
handleEvent(){
//访问到父链后,可以做任何操作,比如直接修改数据
this.$parent.message = '来自组件 component-a 的内容';
}
}
})
var app = new Vue({
el: '#app',
data: {
message: '',
},
})
</script>
</body>
</html>
尽管 Vue 允许这样操作,但在业务中,子组件应该尽可能的避免依赖父组件的数据,更不应该去主动修改它的数据,因为这样会使得父子组件紧耦合。父子组件最好还是通过 props 和 $emit 来通信。
5.子组件索引(ref)
Vue提供了子组件索引的方法,用特殊的属性 ref 来为子组件指定一个索引名称,
代码如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app">
{{message}}
<button @click="handleRef">通过 ref 获取子组件实例</button>
<component-a ref="comA"></component-a>
</div>
<script src = "../lib/vue.min.js" ></script>
<script>
Vue.component('component-a', {
template: '<div>子组件</div>',
data(){
return{
message: '我是子组件内容'
}
}
})
var app = new Vue({
el: '#app',
data: {
message: '',
},
methods:{
handleRef(){
var msg = this.$refs.comA.message;
this.message = msg;
}
}
})
</script>
</body>
</html>
在父组件模板中,子组件标签上使用 ref 指定一个名称,并在父组件内通过 this.$refs 来访问指定名称的组件。
$refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案,应当避免在模板或计算属性中使用 $refs。