(二十五)、自定义事件 (父子组件)
1.A组件想让B组件给自己传递数据,那么就要给B组件绑定自定义事件。
2.自定义回调放在哪里,哪里就能够接收数据。非$emit()是自定义回调函数
1.儿子传递数据给父亲 (原始)
App.vue
<template> <div class="app"> <h2>我是App组件</h2> <!-- 2. 父亲传递给儿子 --> <Demo :receive_son="receive_one"/> <h2>{{username}}</h2> </div> </template> <script> import Demo from "./components/Demo.vue"; export default { name: "App", components: { Demo, }, data() { return { username:'111' } }, // TODO: 1.父亲创建一个接受儿子传递过来的方法 methods: { receive_one(data_one){ this.username=data_one; } } }; </script> <style> .app{ background-color: brown; padding: 10px; } </style>
Demo.vue 儿子
<template> <div class="demo"> <h2 >我是Demo2组件</h2> <button @click="send_data">点我向父类传送数据</button> </div> </template> <script> export default { name: "Demo", data() { return { name:'ckqn' } }, // TODO: 1.儿子接受数据 props:['receive_son'], // TODO: 2.儿子创建方法传递给父亲 methods:{ send_data(){ this.receive_son(this.name) } } }; </script> <style> .demo{ background-color: antiquewhite; } </style>
2.儿子传递数据给父亲 (自定义事件 $emit) ⭐
- (适用于父子组件、不适用兄弟组件):
一般自定义事件都会放在组件上
1.给子组件自定义事件:自定义事件会放在子组件的vc上 <Demo @jsxs="test"/> methods: { test(data_one){ this.username = data_one } },
App.vue
<template> <div class="app"> <h2>我是App组件</h2> <!-- 如下代码是给Demo的组件实列对象定义一个jsxs事件,只要Demo组件实列对象触发了jsxs事件,那么就调用test函数 组件->一般添加的是自定义事件 --> <Demo @jsxs="test"/> <h2>{{username}}</h2> </div> </template> <script> import Demo from "./components/Demo.vue"; export default { name: "App", components: { Demo, }, data() { return { username:'111' } }, methods: { test(data_one){ this.username = data_one } }, }; </script> <style> .app{ background-color: brown; padding: 10px; } </style>
Demo.vue
2.子组件通过$emit向父亲传递数据 : 第一个参数是自定义事件名,后面的是参数无限制 methods:{ send_data(){ // 当jsxs这个自定义的事件被触发的时候会像父App.vue传递一个数据 this.$emit('jsxs',this.name) //把子类的参数传递给父类 } }
<template> <div class="demo"> <h2 >我是Demo2组件</h2> <button @click="send_data">点我</button> </div> </template> <script> export default { name: "Demo", data() { return { name:'ckqn' } }, methods:{ send_data(){ this.$emit('jsxs',this.name) //把子类的参数传递给父类 } } }; </script> <style> .demo{ background-color: antiquewhite; } </style>
3.儿子传递数据给父亲 (ref 自定义事件实现)
1.给子组件设置ref。 在组件上设置ref就是获取的组件的实列对象 <Demo ref="demoA"/> 2.利用mounted进行挂载实现自定义 mounted() { this.$refs.demoA.$on('jsxs',this.test) //第一个参数是自定义自定义事件名和子组件emit对应,第二个是父App.vue绑定的函数 },
App.vue
<template> <div class="app"> <h2>我是App组件</h2> <!-- 如下代码是给Demo的组件实列对象定义一个jsxs事件,只要Demo组件实列对象触发了jsxs事件,那么就调用test函数 组件->一般添加的是自定义事件 --> <Demo ref="demoA"/> <h2>{{username}}</h2> </div> </template> <script> import Demo from "./components/Demo.vue"; export default { name: "App", components: { Demo, }, data() { return { username:'111' } }, methods: { test(data_one){ this.username = data_one } }, mounted() { this.$refs.demoA.$on('jsxs',this.test) }, }; </script> <style> .app{ background-color: brown; padding: 10px; } </style>
Demo.vue 不变
<template> <div class="demo"> <h2 >我是Demo2组件</h2> <button @click="send_data">点我</button> </div> </template> <script> export default { name: "Demo", data() { return { name:'ckqn' } }, methods:{ send_data(){ this.$emit('jsxs',this.name) //把子类的参数传递给父类 } } }; </script> <style> .demo{ background-color: antiquewhite; } </style>
4.todoList更换为自定义事件
App.vue
<!-- 1.头部 将父APP.VUE的addFather方法传递给子组件--> <Header @addFatherA="addFather"/>
<template> <div> <div class="todo-container"> <div class="todo-wrap"> <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件--> <Header @addFatherA="addFather"/> <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件--> <list :todosA="todos" :updateFatherA="updateFather" :deleteFatherA="deleteFather" /> <!-- 3.底部导航 --> <Footer :FooterTodos="todos" :updateAllFatherA="updateAllFather" :clearAllDoneFatherA="clearAllDoneFather" /> </div> </div> </div> </template> <script> // 1.引入组件 import Header from './components/Header.vue' import List from './components/List.vue' import Footer from './components/Footer.vue' export default { name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。 // 2.注册组件 components:{ Header, List, Footer }, data() { return { todos:[] } }, methods: { addFather(todoObj){ // 这个方法是对todos这个数组的尾部追加对象todoObj this.todos.unshift(todoObj) }, // 更新 updateFather(index,doneA){ this.todos[index].done=doneA }, // 删除 deleteFather(index){ // 根据坐标删除数据 this.todos.splice(index,1) }, // 全选或者不选 updateAllFather(doneOption){ // map():创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。 this.todos=this.todos.map((value)=>{ // 这个参数是遍历的单个对象 return {...value,done:doneOption} // 返回将done属性改编为doneOption的完整对象 }) }, // 清除已经已经勾选的 clearAllDoneFather(){ this.todos=this.todos.filter((value)=>{ // 假如说done值为false就不用过滤-保留,否则就需要过滤-不保留 return value.done===false }) } }, watch:{// 我们初步设想的是利用,mounted目的是一上来就挂载上去。但是webStorage数据不会随着新增能新增... todos:{ immediate: true, // 若immediate为true则handle会在初始化时就会调用一次,以后就看firstName的改变了 deep: true, //开启深度监视 handler(newValue,oldValue){ //假如data是方法旧值获取不到(旧址也是新值),假如data是对象就能获取到旧值 localStorage.setItem('arr_Object',JSON.stringify(newValue)); // 这里一定要转换为JSON字符串 } } } } </script> <style> /*base*/ body { background: #fff; } .btn { display: inline-block; padding: 4px 12px; margin-bottom: 0; font-size: 14px; line-height: 20px; text-align: center; vertical-align: middle; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); border-radius: 4px; } .btn-danger { color: #fff; background-color: #da4f49; border: 1px solid #bd362f; } .btn-danger:hover { color: #fff; background-color: #bd362f; } .btn:focus { outline: none; } .todo-container { width: 600px; margin: 0 auto; } .todo-container .todo-wrap { padding: 10px; border: 1px solid #ddd; border-radius: 5px; } </style>
Header.vue
$emit -> 属于vue实列的, vc是继承了vm add(){ if(this.textA!==null){ // 根据用户的输入生成一个todo对象 const todo={id:Date.now(),name:this.textA,done:false} // 通知父App.vue添加这个数据 this.$emit('addFatherA',todo) } this.textA='' }
<template> <div> <!-- 1.头部 --> <div class="todo-header"> <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="textA" @keyup.enter="add"/> </div> </div> </template> <script> export default { name:'Header', data() { return { textA:'' } }, methods: { add(){ if(this.textA!==null){ // 根据用户的输入生成一个todo对象 const todo={id:Date.now(),name:this.textA,done:false} // 通知父App.vue添加这个数据 this.$emit('addFatherA',todo) } this.textA='' } }, } </script> <style scoped> /*header*/ .todo-header input { width: 560px; height: 28px; font-size: 14px; border: 1px solid #ccc; border-radius: 4px; padding: 4px 7px; } .todo-header input:focus { outline: none; border-color: rgba(82, 168, 236, 0.8); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); } </style>