Vue.js组件简介及组件通信(2)

240 阅读1分钟

这是我参与更文挑战的第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应用时可以通过此方式进行组件传值。
  • 除了 root,Vue.js中还提供了root , Vue.js 中还提供了 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 = '修改子组件的数据';
    }
});