Vue第二部分(2):组件的嵌套与通信

组件的嵌套

在Vue的设计中,一切都可以看作组件。整个页面可以看作是一个根组件,内部的各块组件可以看作子组件。组件之间自然的会发生嵌套关系。
在这里插入图片描述
接下来,我们看一下组件嵌套的示例:
在这里插入图片描述

<div id="app">
    <my-table></my-table>
</div>

<template id="table-head">
    <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>age</th>
            <th>gender</th>
        </tr>
    </thead>
</template>

<template id="table-body">
    <tbody>
        <tr v-for="u in users" :key="u.id">
            <td>{{u.id}}</td>
            <td>{{u.name}}</td>
            <td>{{u.age}}</td>
            <td>{{u.gender}}</td>
        </tr>
    </tbody>
</template>

<template id="my-table">
    <table border="1" align="center">
        <thead is="table-head"></thead>
        <tbody is="table-body"></tbody>
    </table>
</template>
<script>
    var tableHead = {
        template:"#table-head"
    }
    var tableBody = {
        template:"#table-body",
        data:function(){
            return {
                users: [
                    {"id":1, "name": "小明", "age": 13, "gender": "男"},
                    {"id":2, "name": "小红", "age": 13, "gender": "女"},
                    {"id":3, "name": "小绿", "age": 4, "gender": "男"}
                ]
            }
        }
    }

    //使用局部注册,需要将子组件注册到父组件中
    var myTable ={
        template:"#my-table",
        components:{
            "table-head":tableHead,
            "table-body":tableBody
        }
    }

    const vm = new Vue({
        el:"#app",
        components:{
            "my-table":myTable
        }
    });
</script>

说明:事实上,虽然 new Vue() 没有显式的使用组件的语法,但它本质上也是一个父组件。

组件通信

通常一个较为复杂的页面,一定会出现组件的嵌套。各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。

1. 父传子:props

通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据或参数,子组件接收到后根据参数的不同来渲染不同的内容或执行操作。这个正向传递数据的过程就是通过props来实现的。

比如在之前的表格案例中,table-body子组件展示的数据是定义在子组件自个身上的,这么做虽有效果,但降低了该组件的复用价值,更好的做法:子组件中不定义数据,而是由使用它的父组件传递。此时,需要使用 props完成父组件向子组件的数据传递。

语法:
//1.定义子组件中添加props属性
const 组件 = {
template:“html片段”,
props:[“自定义参数名”,…]
}
//2 使用组件时,为组件添加 自定义参数名 同名的属性
<组件 :自定义参数名=”值"></组件>

例子:

<div id="app">
    <my-table :us="users"></my-table>
</div>

<template id="my-table">
    <table border="1" align="center">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>age</th>
                <th>gender</th>
            </tr>
        </thead>

        <tbody>
            <tr v-for="u in us" :key="u.id">
                <td>{{u.id}}</td>
                <td>{{u.name}}</td>
                <td>{{u.age}}</td>
                <td>{{u.gender}}</td>
            </tr>
        </tbody>
    </table>
    
</template>


<script>

    var myTable = {
        template:"#my-table",
        props:["us"]
    }
    const vm = new Vue({
        el:"#app",
        data:{
            users: [
                {"id":1, "name": "小明", "age": 13, "gender": "男"},
                {"id":2, "name": "小红", "age": 13, "gender": "女"},
                {"id":3, "name": "小绿", "age": 4, "gender": "男"}
            ]
        },
        components:{
            "myTable":myTable
        }
    });
</script>

2. 子传父:$emit

父组件的模板中包含子组件,那么经常会出现子组件的状态发生变化时,要通知到父组件。所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。如下所示:
在这里插入图片描述
代码如下:

<div id="app">
    <h2>{{num}}</h2>
    <!-- 
父组件向子组件传递数据 count=num
子组件中修改count值,不能修改父组件的num
-->
    <counter :count="num"></counter>
</div>

<script>
    Vue.component("counter",{
        template:`
<div>
<button @click="increment">点我自增</button>
<span>{{count}}</span>
<button @click="decrement">点我自减</button>
    </div>`,
        props:["count"],
        methods:{
            increment(){
                this.count++;
            },
            decrement(){
                this.count--;
            }
        }
    })

    const vm = new Vue({
        el:"#app",
        data:{
            num:0
        }
    })
</script>

所以,当需要子传父的时候,Vue采用事件放射的方式完成。

  • 在子组件中执行 $emit(“父组件的自定义事件”)通知父组件,并发送数据
  • 父组件中定义自定义事件处理函数,并接收数据
<div id="app">
    <h2>{{num}}</h2>
    <!-- 2 绑定自定义事件处理函数,监听子组件的事件触发 change-count
		当子组件触发事件后,执行handleChange事件处理函数,
		$emit发送的数据将成为handleChange的实参
	-->
    <counter :count="num" @change-count="handleChange"></counter>
</div>

<script>
    Vue.component("counter",{
        template:`
            <div>
                <button @click="increment">点我自增</button>
                <span>{{count}}</span>
                <button @click="decrement">点我自减</button>
             </div>`,
        props:["count"],
        methods:{
            increment(){
                this.count++;
                //1 子组件中通过$emit触发父组件中的自定义事件,并发送数据
                this.$emit("change-count",this.count);
            },
            decrement(){
                this.count--;
                //1 子组件中通过$emit触发父组件中的自定义事件,并发送数据
                this.$emit("change-count",this.count);
            }
        }
    })

    const vm = new Vue({
        el:"#app",
        data:{
            num:0
        },
        methods:{
            handleChange(value){
                this.num = value;
            }
        }
    })
</script>

实战案例

在这里插入图片描述

代码如下:

<div id="app">
    <div>
        <my-table :us="users" @show-user="handleShowUser"></my-table>
        <hr>
        <update-form :u="user" @update-user="handleUpdateUser"></update-form>
    </div>
</div>
<template id="my-table">
    <table border="1">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>age</th>
                <th>operation</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="(u,index) in us" :key="u.id">
                <td>{{u.id}}</td>
                <td>{{u.name}}</td>
                <td>{{u.age}}</td>
                <td>
                    <button @click="emitShowUser(u)">更新</button>
                </td>
            </tr>
        </tbody>
    </table>
</template>
<template id="update-form">
    <form action="" @submit.prevent="emitUpdateUser">
        <input type="hidden" v-model="u.id" >
        用户名: <input type="text" v-model="u.name"> <br>
        年龄: <input type="number" v-model="u.age"> <br>
        <input type="submit" value="更新">
    </form>
</template>

<script>
    const updateForm = {
        template:"#update-form",
        props:["u"],
        methods:{
            emitUpdateUser(){
                this.$emit("update-user",this.u);
            }
        }
    };

    const myTable = {
        template:"#my-table",
        props:["us"],
        methods:{
            emitShowUser(u){
                this.$emit("show-user",u);
            }
        }
    };



    const vm = new Vue({
        el:"#app",
        data:{
            users:[
                {id:1,name:"xiao1hei",age:18},
                {id:2,name:"xiao2hei",age:20},
                {id:3,name:"xiao3hei",age:22}
            ],
            user:{}
        },
        components:{
            myTable,updateForm
        },
        methods:{
            handleShowUser(u){
                this.user = JSON.parse(JSON.stringify(u));
            },
            handleUpdateUser(u){
                this.users.forEach((item,i,users)=>{
                    if(item.id == u.id){
                        //由于Vue2.x无法直接检测到数组中元素的变化,可以使用vue提供的$set方法
                        this.$set(this.users,i,JSON.parse(JSON.stringify(u))g);
                    }
                })
            }
        }
    })
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值