https://cn.vuejs.org/guide/components/props#one-way-data-flow
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。
举一个例子,我们要实现一个header里面有一个menu的按钮,当点击了会把侧边栏滑进来,然后点击了关闭按钮又隐藏。
<script>
import { ref } from "vue";
import Header from "./components/Header.vue";
import Nav from "./components/Nav.vue";
export default {
components: {
Header,
Nav,
},
setup() {
const isOpen = ref(false);
const handToggle = () => isOpen.value = !isOpen.value;
return { isOpen, handToggle };
},
};
</script>
<template>
<Header @toggle="handToggle" />
<Nav :isOpen="isOpen" @toggle="handToggle" />
</template>
header.vue
<script>
export default {
setup(props, { emit }) {
const handleMenu = () => {
emit("toggle");
};
return { handleMenu };
},
};
</script>
<template>
<header>
<nav>
<button @click="handleMenu">menu</button>
</nav>
</header>
</template>
nav.vue
<script>
export default {
props: {
isOpen: {
type: Boolean,
default: false,
},
},
setup(props, { emit }) {
const handleMenu = () => {
emit("toggle");
};
return { props, handleMenu };
},
};
</script>
<template>
<div :class="['nav', { open: props.isOpen }]">
<a @click="handleMenu">close</a>
</div>
</template>
整体的效果如下所示:
在点击了Header.vue里面的Menu 后发送一个emit给上层的component,透过上层接收到的toggle事件去修改我们isOpen的value,然后isOpen再透过props 传入Nav.vue里面去控制侧边选单的开起,然后透过侧边选单的close按钮,在发送一个 toggle事件往上去更改 isOpen,然后props 的isOpen也会同步知道被更改,这样就完成了整个的流程!
整个流程其实也遵循了单一数据流的特性。props都是从父级修改的。但是不是感觉把简单的事情复杂化了?增加了一层事件的往返通信来修改props。有没有更方便的做法?其实我们可以直接去掉事件这一层。
<script>
// 其他省略...
export default {
setup() {
const isOpen = ref(false);
const handleOpenMenu = () => isOpen.value = !isOpen.value;
return { isOpen, handleOpenMenu };
},
};
</script>
<template>
<Header :handleOpenMenu="handleOpenMenu" />
<Nav :isOpen="isOpen" :handleOpenMenu="handleOpenMenu" />
</template>
把修改值的方法传递给子组件去调用,避免了事件的通信。调整后的数据流就简单多了。
单向数据流是一种数据流动的模式,数据从父组件流向子组件,子组件只能通过props从父组件接收数据,而不能直接修改父组件的状态。这种模式使得数据流更加清晰和可预测,有助于维护和管理大型应用。
在react中也有类似的效果"Lifting State Up"(状态提升),当多个组件需要共享同一个状态时,可以将状态提升到它们共同的父组件中。这样,子组件可以通过props从父组件获取状态,并通过回调函数通知父组件更新状态。状态提升有助于避免组件间的直接状态共享,使得状态管理更加集中和一致。