组件间通信方式
组件间通信方式有三种:
- props 父组件向子组件传递数据
- $emit 自定义事件(子组件向父组件传递数据)
- slot 插槽分发内容
组件间通信要遵循以下的规则:
- 不要在子组件中直接修改父组件传递的数据
- 数据初始化时,应当看初始化的数据是否用于多个组件中,如果需要被用于多个组件中,则初始化在父组件中;如果只在 一个组件中使用,那就初始化在这个要使用的组件中。
- 数据初始化在哪个组件,更新数据的方法(函数) 就应该定义在哪个组件。
props向子组件传递数据
1.在声明组件对象中使用props选项指定
const MyComponent = { template: `<div></div>`, props: 此处值有以下三种方式, components: { } }
方式一:指定传递属性名,注意是数组形式。
props: ['id','name','salary', 'isPublished', 'commentIds', 'author', 'getEmp']
方式二:指定传递属性名和数据类型,注意是对象形式。
props: { id: Number, name: String, salary: Number, isPublished: Boolean, commentIds: Array, author: Object, getEmp: Function }
方式三:指定属性名,数据类型,必要性,默认值
props: { name: { type: String, required: true, default: 'zou' } ...... }
2.在引用组件时,通过v-bind动态赋值
<my-component v-bind:id="2" :name="meng" :salary="9999" :is-published="true" :comment-ids="[1, 2]" :author="{name: 'alan'}" :get-emp="getEmp" > </my-component>
先来看看父组件的内容
;(function () { const template = `<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> // 通过 v-bind把父组件的内容传给子组件 ,:前面的名称可以自定义,为了方便理解,一般和传递的变量名一样 <dashboard :hobbies="hobbies" @delete_hobby="deleteHobby"></dashboard> </div>` window.AppHome = { template, // template: template, data () { // 定义父组件往子组件里传的内容 return { hobbies: ['coding', '睡觉', '打豆豆', '看书'], } }, components: { //Dashboard ,HomeList 作为AppHome 的子组件 Dashboard, // Dashboard: Dashboard HomeList // HomeList:HomeList } } })()
这样父组件就写好了,在来看看子组件的内容
;(function () { const template = `<div class="row placeholders"> // 在子组件中使用父组件传过来的数据 <div v-for="(hobby, index) in hobbies" :key="index" class="col-xs-6 col-sm-3 placeholder"> <h4>{{hobby}}</h4> <span class="text-muted"> <a href="#" @click="deleteHobby(index)">删除</a> </span> </div> </div>` window.Dashboard = { // 声明当前子组件接收父组件传递的属性 props: ['hobbies'], template // template: template } })()
上面的是使用方式一的方法传值,接下来看看方式二和方式三
父组件
;(function () { const template = `<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> // 往子组件home-list里传值 <home-list :empList="empList" :deleteEmp="deleteEmp"></home-list> </div>` window.AppHome = { template, // template: template, data () { // 定义父组件往子组件里传的内容 return { empList: [ {id: 1, name: '小1', salary: '80001'}, {id: 2, name: '小2', salary: '80002'}, {id: 3, name: '小3', salary: '80003'}, {id: 4, name: '小4', salary: '80004'}, {id: 5, name: '小5', salary: '80005'} ] } }, components: { //Dashboard,HomeList 作为AppHome 的子组件 Dashboard, // Dashboard: Dashboard HomeList // HomeList:HomeList } } })()
子组件HomeList
;(function () { const template = `<div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>工资</th> <th>操作</th> </tr> </thead> <tbody> <!-- 这里还有一个子组件Item --> <Item v-for="(emp, index) in empList" :key="emp.id" :emp="emp" :deleteEmp="deleteEmp" :index="index"/> </tbody> </table> </div>` window.HomeList = { <!-- 声明当前子组件接收父组件传递的属性 --> props: { empList: Array, deleteEmp: Function }, template, components: { <!-- 子组件Item --> Item } } })()
子组件Item
;(function () { const template = `<tr> <td>{{emp.id}}</td> <td>{{emp.name}}</td> <td>{{emp.salary}}</td> </tr>` window.Item = { props: { emp: { // 指定属性名/数据类型/是否必须 type: Object, required: true }, deleteEmp: Function, index: Number }, template } })()
自定义事件
在父组件中定义事件监听函数,并引用子组件标签上 v-on 绑定事件监听。
<!-- 通过 v-on 绑定 --> <!-- @自定义事件名=事件监听函数 --> <!-- 在子组件 dashboard中触发 delete_hobby 事件来调用 deleteHobby函数 --> <dashboard @delete_hobby ="deleteHobby"></dashboard>
在子组件中触发父组件的监听事件函数调用
<!-- 子组件触发事件函数调用 --> <!-- this.$emit(自定义事件名,data) --> this.$emit("delete_hobby",name)
注意:
自定义事件只用于子组件向父组件发送数据
隔代组件或兄弟组件间通信此种方式不合适,使用 props
案例
父组件
<!-- 父组件,其余的代码省略--> <!-- @自定义事件名=事件监听函数 --> <!-- 在子组件 dashboard 中触发了 delete_hobby 事件来调用 deleteHobby 函数 --> <dashboard @delete_hobby="deleteHobby" :hobbies="hobbies"></dashboard> <script> new Vue({ data(){ return { hobbies: ["吃饭","睡觉","打豆豆"] } }, methods: { deleteHobby(){ } } }) </script>
子组件
<!-- 子组件,其余的代码省略--> <div @click="deleteHobby(index)"></div> <script> new Vue({ data(){ return { } }, prrops: ["hobbies"] methods: { deleteHobby(index){ <!-- 触发父组件 delete_hobby 事件进行删除操作--> this.$emit('delete_hobby',index) } } }) </script>