这是我参与更文挑战的第2天,活动详情查看: 更文挑战
子组件向父组件传值
- 子向父传值需要通过自定义事件实现。
举例
- 商品为子组件,购物车为父组件,父组件需要统计商品个数,就需要在子组件个数变化时传值给父组件。
- 子组件数据变化时,通过$emit()触发自定义事件。
- 自定义事件名称建议使用kebab-case.
- 父组件监听子组件的自定义事件,并设置处理程序。
<!-- 父组件:购物车 子组件:商品 -->
<div id="app">
<h3>购物车</h3>
<product-item v-for="item in products"
:key = "item.id"
:item-title = "item.title"
@count-change="totalCount++"></product-item>
<p>总数为:{{totalCount}}</p>
</div>
Vue.component('productItem',{
props:['itemTitle'],
template:`
<div>
<span>{{itemTitle}}, 商品个数 {{count}}</span>
<button @click="countIns">+1</button>
</div>
`,
data(){
return {
count:0
}
},
methods:{
countIns(){
// 子组件设置自定义事件
this.$emit('count-change');
this.count++;
}
}
});
// 创建根实例
new Vue({
el:'#app',
data:{
products:[
{id:1,title:'苹果1斤'},
{id:2,title:'桃子2斤'},
{id:3,title:'西瓜2斤'}
],
totalCount:0
}
});
自定义事件传值
- $event表示事件对象,事件对象中存储的就是:需要传递的数据
<!-- 父组件:购物车 子组件:商品 -->
<div id="app">
<h3>购物车</h3>
<product-item v-for="item in products"
:key = "item.id"
:item-title = "item.title"
@count-change="onCountChange"></product-item>
<!-- @count-change="totalCount += $event" -->
<p>总数为:{{totalCount}}</p>
</div>
Vue.component('productItem',{
props:['itemTitle'],
template:`
<div>
<span>{{itemTitle}}, 商品个数 {{count}}</span>
<button @click="countIns1">+1</button>
<button @click="countIns5">+5</button>
</div>
`,
data(){
return {
count:0
}
},
methods:{
countIns1(){
// 子组件设置自定义事件
this.$emit('count-change',1);
this.count++;
},
countIns5(){
// 子组件设置自定义事件
this.$emit('count-change',5);
this.count += 5;
}
}
});
// 创建根实例
new Vue({
el:'#app',
data:{
products:[
{id:1,title:'苹果1斤'},
{id:2,title:'桃子2斤'},
{id:3,title:'西瓜2斤'}
],
totalCount:0
},
methods:{
onCountChange(productNum){
// console.log(productNum);
this.totalCount += productNum;
}
}
});
组件与v-model
- $emit()不仅可以触发自定义事件还可以触发固有事件
非父子组件传值
- 非父子组件指的是兄弟组件或完全无关的两个组件。
兄弟组件传值
- 兄弟组件可以通过父组件进行数据中转
<body>
<div id="app">
<!-- 组件a -->
<my-com-a @achuanzhi="value = $event"></my-com-a>
<!-- 组件b -->
<my-com-b :value="value"></my-com-b>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('myComA',{
template:`
<div>
<button @click="$emit('achuanzhi',value)">点击</button>
</div>
`,
data(){
return {
value:'a组件中的新信息'
}
}
});
Vue.component('myComB',{
props:['value'],
template:`
<div>组件B接收到:{{value}}</div>
`
});
new Vue({
el:'#app',
data:{
value:''
}
});
</script>
</body>
EventBus(父子组件、兄弟组件、非兄弟组件之间都可以进行传值)
- EventBus (事件总线)是一个独立的事件中心,用于管理不同组 件间的传值操作。
- EventBus 通过一个新的 Vue 实例来管理组件传值操作,组件通 过给实例注册事件、调用事件来实现数据传递。
总结之前的组件间传值方式的优缺点
- 当组件嵌套关系复杂时,根据组件关系传值会较为繁琐。
- 组件为了数据中转,data中会存在许多与当前组件功能无关的数据。
实际应用
- 发送数据的组件触发bus事件,接收的组件给bus注册对应事件。
- 之前this.$emit(),this指代的是当前组件的示例,现在将组件实例更改为bus实例。而bus实例可以被任意组件访问,从而实现数据的中转。
- **created(注册事件)**是生命周期函数,实例创建完成后触发的。通常用于数据的处理。
- 实例
<div id="app">购物车
<!-- 单个的组件 -->
<product-item></product-item>
<!-- 计算总数的组件 -->
<product-total></product-total>
</div>
<script src="lib/vue.js"></script>
<!-- 引入bus实例 -->
<script src="lib/EventBus.js"></script>
<script>
// 创建单个的组件
Vue.component('product-item',{
template:`
<div>
<span>商品名称:苹果,商品个数:{{count}}</span>
<button @click="countIns">+1</button>
</div>
`,
data(){
return {
count:0
}
},
methods:{
countIns(){
// 触发事件
bus.$emit('countChange',1);
this.count++;
}
}
});
// 创建总的个数的组件
Vue.component('product-total',{
template:`
<div>
<p>总的商品个数:{{totalCount}}</p>
</div>
`,
data(){
return {
totalCount:0
}
},
created(){
// 注册事件
bus.$on('countChange',(productCount)=> {
this.totalCount += productCount;
});
}
});
new Vue({
el:'#app',
data:{
}
});
</script>
(扩展内容)其他传值方式
- 下面这两种并不是推荐方式,仅仅适合在简单的Vue应用中去开发。
- 因为这些方式都可以直接访问到其他的组件,对组件内部数据直接处理,当通过一个组件去操作另一个组件时,那么在出现问题的时候,就无法准确的断定是哪里出现了问题。不便于维护。
$root
- 用于访问当前组件树根实例,设置简单的Vue应用时可以通过此方式进行组件传值。
- 除了 parent 与 $children 用于便捷访问父子组件。
<body>
<div id="app">
<p>父组件数据:{{count}}</p>
<com-a></com-a>
<com-b></com-b>
</div>
<script src="lib/vue.js"></script>
<script>
// $root
var ComA = {
template:`
<div>
组件A的数据:{{$root.count}}
<button @click='clickFn'>+1</button>
</div>
`,
methods:{
clickFn(){
this.$root.count++;
}
}
};
var ComB = {
template:`
<div>
组件B的数据:{{$root.count}}
<button @click='clickFn'>+1</button>
</div>
`,
methods:{
clickFn(){
this.$root.count++;
}
}
};
new Vue({
el:'#app',
data:{
count:0
},
components:{
ComA,
ComB
}
});
</script>
</body>
$refs
- 用于获取设置了ref属性的HTML标签或子组件。
- 给普通 HTML 标签设置 ref 属性,$refs 可以获取 DOM 对象。
<div id="app">
<input type="text" ref="item">
<button @click="clickFn">获取焦点</button>
</div>
new Vue({
el:'#app',
methods:{
clickFn(){
this.$refs.item.focus();
}
}
});
- 给子组件设置 ref 属性,渲染后可通过 $refs获取子组件实例。
- 注意:这里使用mounted而不要使用created,因为created执行的时候还没有执行挂载元素。
- 在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素。
- 而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点。
<div id="app">
<com-a ref="item"></com-a>
</div>
var ComA = {
template:`
<div>
组件A的数据:{{count}}
</div>
`,data(){
return {
count:'组件A的原始数据'
}
}
};
new Vue({
el:'#app',
data:{
},
components:{
ComA
},
mounted(){
console.log(this.$refs);
this.$refs.item.count = '修改子组件的数据';
}
});