上一篇:饮冰三年-人工智能-Vue-66 Vue组件化
很久以前我对Vue2的组件间数据交互做过学习,兜兜转转再用Vue已经是Vue3版本。
Vue3组件间数据交互
1、准备工作
环境准备
功能介绍
- 头部组件(MyHeader)
主要是一个input框,用于收集用户输入内容,
当用户输入完成,并按下enter键后,将数据添加到 MyList 列表组件中
- 列表组件(MyList)
- 任务组件(MyItem)
- 底部组件(MyFooter)
2、props emit 版本
<template><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="add" /></div> </template><script setup> // 引入nanoid import { nanoid } from "nanoid"; // 定义 emits const emit = defineEmits(['addTodo']) const add = (e) => {// 判断用户是否输入了内容if (e.target.value.trim().length === 0) {alert("输入的内容不能为空");return;}// 将用户的输入,包装成为一个todo对象const todoObj = {id: nanoid(),title: e.target.value,done: false,};// 将todo对象传递给App组件emit('addTodo', todoObj)// 清空用户的输入e.target.value = ""; }</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>
源码解析
emit 是一个用于触发事件的方法,允许子组件向父组件发送消息。这是 Vue 的一种组件通信机制,通常用于子组件向父组件传递数据或通知父组件发生了某个事件。
作用和用法
-
事件传递: 当子组件需要通知父组件某个事件发生时,可以使用 emit 方法。例如,当用户在输入框中输入任务并按下回车键时,你想要将这个任务传递给父组件。
-
自定义事件: emit 允许你创建自定义事件,父组件可以通过监听这些事件来响应子组件的行为。例如,在示例中,子组件使用 emit('addTodo', todoObj) 来触发名为 addTodo 的事件,并将 todoObj 作为参数传递给父组件。
如何在父组件中监听事件
-
在父组件中,你可以通过 @ 符号来监听子组件发出的事件
-
<MyHeader @addTodo="addTodo"></MyHeader>
<template><ul class="todo-main"><!-- v-for 指令用于遍历 todos 数组,生成多个 MyItem 组件。:todo="todoObj": 将当前的 todoObj 传递给 MyItem 组件的 todo prop。:checkTodo="checkTodo" 和 :deleteTodo="deleteTodo": 将父组件的方法 checkTodo 和 deleteTodo 作为 prop 传递给 MyItem 组件,允许子组件在适当的时候调用这些方法 --><MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyItem></ul> </template><script setup>import MyItem from './MyItem.vue'; // defineProps 是一个 Vue 3 的编译宏,用于在 <script setup> 中定义组件接收的 props。 const props = defineProps({todos: Array, // todos: 一个数组,用于存储待办事项列表。checkTodo: Function, // checkTodo: 一个函数,用于处理勾选或取消勾选待办事项的操作。deleteTodo: Function // deleteTodo: 一个函数,用于处理删除待办事项的操作。 }); </script><style scoped> /*main*/ .todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px; }.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px; } </style>
源码解析
-
父组件通过 props 向子组件传递数据和方法。
作用和用法
- 父组件传:父组件 APPVue 通过 : 符号绑定 todos、checkTodo 和 deleteTodo到子组件 MyList 组件。 Vue 会将这些数据和方法作为 props 传递给 MyList 组件。
<MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyList>
-
子组件接:在 MyList.vue 组件中,使用 defineProps 来定义和接收这些 props:
<template><li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)" /><span>{{ todo.title }}</span></label><button class="btn btn-danger" @click="handleTodo(todo.id)">删除</button></li> </template><script setup>// 接收 props const props = defineProps({todo: Object,checkTodo: Function,deleteTodo: Function }) // 处理勾选事件 const handleCheck = (id) => {// 通知App组件,修改todo的done状态 props.checkTodo(id) }// 处理删除事件 const handleTodo = (id) => {if (window.confirm('确定删除吗?')) {props.deleteTodo(id)} } </script><style scoped> /*item*/ li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd; }li label {float: left;cursor: pointer; }li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px; }li button {float: right;display: none;margin-top: 3px; }li:before {content: initial; }li:last-child {border-bottom: none; }li:hover {background-color: #ddd; }li:hover button {display: block; } </style>
源码解析
有了上面的基础,这个比较好理解,父组件通过 props 向子组件传递数据和方法。不过不同的是,子组件在这里调用了props传的方法。
// 处理勾选事件 const handleCheck = (id) => {// 通知App组件,修改todo的done状态 props.checkTodo(id) }// 处理删除事件 const handleTodo = (id) => {if (window.confirm('确定删除吗?')) {props.deleteTodo(id)} }
<template><div class="todo-footer" v-show="total"><label><input type="checkbox" :checked="isAll" @change="checkAll" /></label><span> <span>已完成{{ doneTotal }}</span> / 全部{{ todos.length }} </span><button class="btn btn-danger" @click="clearAll">清除已完成任务</button></div> </template><script setup> import { computed} from 'vue'// 接收 props const props = defineProps({todos: {type: Array,required: true} }); // 定义 emits const emit = defineEmits(['checkAllTodo', 'clearAllTodo']);// 计算属性 const total = computed(() => props.todos.length)const doneTotal = computed(() => {return props.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0); })const isAll = computed(() => doneTotal.value === total.value && total.value > 0)const checkAll = (e) => {emit('checkAllTodo', e.target.checked); }const clearAll = () => {emit('clearAllTodo') }</script><style scoped> /*footer*/ .todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px; }.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer; }.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px; }.todo-footer button {float: right;margin-top: 5px; } </style>
源码解析
-
定义 props:
-
props 接收一个 todos 数组,表示所有的待办事项。
-
defineProps的时候可以为属性设置必填项
-
- 定义 emits:
-
-
emit 用于向父组件发送事件。定义了两个事件:checkAllTodo 和 clearAllTodo。
-
checkAll: 当复选框状态改变时,触发 checkAllTodo 事件,并传递复选框的勾选状态。
-
clearAll: 当点击清除按钮时,触发 clearAllTodo 事件。
-
<script setup> import { ref, watch } from 'vue' import MyHeader from './components/MyHeader.vue' import MyList from './components/MyList .vue' import MyFooter from './components/MyFooter.vue' // 初始化 todos const todos = ref(JSON.parse(localStorage.getItem('todos')) || []) // 添加todo const addTodo = (todo) => {todos.value.unshift(todo); };// 勾选或者取消勾选一个todo const checkTodo = (id) => {todos.value.forEach((todo) => {if (todo.id === id) {todo.done = !todo.done;}}); }; // 删除一个todo const deleteTodo = (id) => {todos.value = todos.value.filter((todo) => todo.id !== id); };// 全选或者全不选 const checkAllTodo = (done) => {todos.value.forEach(todo => todo.done = done) };// 清除所有已经完成的todo const clearAllTodo = () => {todos.value = todos.value.filter((todo) => !todo.done) } // 监听 todos 的变化并同步到 localStorage watch(todos,(newValue) => {localStorage.setItem('todos', JSON.stringify(newValue));},{ deep: true } ); </script><template><div id="root"><div class="todo-container"><div class="todo-wrap"><!-- 在父组件中,你可以通过 @ 符号来监听子组件发出的事件 --><MyHeader @addTodo="addTodo"></MyHeader><MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyList><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"></MyFooter></div></div></div> </template><style scoped> .logo {height: 6em;padding: 1.5em;will-change: filter;transition: filter 300ms; }.logo:hover {filter: drop-shadow(0 0 2em #646cffaa); }.logo.vue:hover {filter: drop-shadow(0 0 2em #42b883aa); } </style>
总结
2、全局事件总线版本
npm install mitt
npm install mitt