1. Props:父组件向子组件传递数据
当父组件需要向子组件传递数据时,可以使用 props 来实现。props 是父组件传递给子组件的属性,子组件可以通过这些属性来接收数据。
以下是一个简单的示例,演示了如何在 Vue 组件中使用 props:
<template>
<div>
<child-component :message="parentMessage"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
parentMessage: 'Hello from parent'
};
}
};
</script>
在这个示例中,parentMessage 是父组件中的一个数据,我们将它通过 :message 传递给了子组件 ChildComponent。在子组件中,可以通过 props 接收并使用这个数据:
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
props: ['message']
};
</script>
现在,子组件会显示来自父组件的消息。
使用 props 时需要注意的几点:
props 数据是单向的,即父组件传递给子组件,子组件不能直接修改 props 的值。
props 可以传递任何类型的数据,包括字符串、数字、对象、数组等。
如果需要在子组件中修改 props,通常应该在子组件内部定义一个局部的数据属性,将 props 的值复制给这个属性,然后对这个属性进行修改。
在子组件中,可以通过 this.message 来访问 props 中的数据。
在 Vue 3 中,setup 函数中使用 props 的方式有所不同,你可以查阅 Vue 3 的官方文档获取更多信息。
2. $emit():子组件向父组件发送事件
当子组件需要向父组件发送事件时,可以使用 $emit() 方法。这个方法允许子组件触发一个自定义事件,并将数据传递给父组件。
以下是一个简单的示例,演示了如何在 Vue 组件中使用 $emit() 来发送事件:
<template>
<div>
<button @click="sendEvent">Send Event</button>
</div>
</template>
<script>
export default {
methods: {
sendEvent() {
// 使用 $emit 发送自定义事件,第一个参数是事件名称,第二个参数是要传递的数据
this.$emit('custom-event', 'Hello from child');
}
}
};
</script>
在这个示例中,子组件中的 sendEvent 方法使用 $emit 发送了一个名为 'custom-event' 的自定义事件,并传递了数据 'Hello from child'。
在父组件中,可以监听这个自定义事件,并在事件处理函数中获取传递的数据:
<template>
<div>
<child-component @custom-event="handleEvent"></child-component>
<p>{{ messageFromChild }}</p>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
messageFromChild: ''
};
},
methods: {
handleEvent(data) {
// 在事件处理函数中获取传递的数据
this.messageFromChild = data;
}
}
};
</script>
在父组件中,使用 @custom-event 监听子组件发送的自定义事件,并在 handleEvent 方法中获取传递的数据。
使用 $emit() 时需要注意的几点:
第一个参数是事件名称,可以自定义,但要确保它在父组件中是唯一的。
第二个参数是要传递给父组件的数据。
父组件通过 @custom-event 来监听子组件发送的事件,并在相应的处理函数中获取数据。
在事件处理函数中,可以将子组件发送的数据赋值给父组件的属性,从而在父组件中显示。
3. $refs:使用ref属性获取子组件的实例或DOM元素
$refs 是 Vue 提供的一个属性,用于在父组件中获取子组件的实例或 DOM 元素。这可以用于直接访问子组件的属性和方法,或者操作子组件的 DOM 元素。
以下是一个使用 $refs 的示例:
<template>
<div>
<child-component ref="childRef"></child-component>
<button @click="callChildMethod">Call Child Method</button>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
callChildMethod() {
// 通过 $refs 获取子组件实例,然后调用子组件的方法
this.$refs.childRef.childMethod();
}
}
};
</script>
在这个示例中,父组件通过 ref="childRef" 将子组件的实例存储在 $refs 中。然后,在 callChildMethod 方法中,通过 $refs.childRef 获取子组件的实例,并调用了子组件的 childMethod 方法。
需要注意的是,使用 $refs 可能会有一些限制和潜在的问题:
不推荐在模板中使用 $refs,因为在模板编译完成之后才会填充 $refs。
$refs 不是响应式的,因此不适合用于数据的绑定和观察。
如果使用 v-if 或 v-for 条件渲染子组件,$refs 可能无法访问到正确的子组件实例。
如果组件在 <keep-alive> 内部,$refs 可能无法访问缓存的实例。
总之,在使用 $refs 时需要注意它的限制,并且尽量避免在模板中直接使用,而是在方法中进行处理。
4. provide/inject:祖先组件向后代组件传递数据
provide 和 inject 是 Vue 中另一种用于组件通信的方法。它允许祖先组件向后代组件传递数据,甚至可以在嵌套层级较深的组件中传递数据。
provide:在祖先组件中使用 provide 提供数据,子孙组件可以通过 inject 来接收这些数据。
inject:在子孙组件中使用 inject 来注入祖先组件提供的数据。
以下是一个简单的示例:
<!-- AncestorComponent.vue -->
<template>
<div>
<child-component></child-component>
</div>
</template>
<script>
export default {
provide: {
message: 'Hello from AncestorComponent'
}
};
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<grandchild-component></grandchild-component>
</div>
</template>
<script>
import GrandchildComponent from './GrandchildComponent.vue';
export default {
components: {
GrandchildComponent
}
};
</script>
<!-- GrandchildComponent.vue -->
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
inject: ['message']
};
</script>
在这个示例中,AncestorComponent 使用 provide 提供了一个名为 message 的数据,然后 ChildComponent 和 GrandchildComponent 都使用 inject 来接收这个数据,并在 GrandchildComponent 中显示出来。
优点:
数据在多层级组件中传递更加方便,不需要通过多层级的 props 传递。
提供了更好的封装,子孙组件不需要知道数据来自哪个祖先组件。
缺点:
使用 inject 会导致组件在依赖祖先组件的情况下变得不再具有独立性,降低了复用性。
inject 不是响应式的,所以在数据更新时可能需要手动更新视图。
需要注意的是,provide 和 inject 本质上是一种高级的用法,可能会增加组件之间的耦合性。在使用时,需要慎重考虑组件之间的关系和数据的传递方式。
5. $attrs/$listeners:父组件向子组件传递非props数据与事件
$attrs 和 $listeners 是 Vue 中用于父组件向子组件传递非 prop 数据和事件的方式。它们通常用于实现透传,即将父组件中的特性和事件传递给子组件。
$attrs:用于传递父组件中没有被子组件所声明(也就是没有被定义为 prop)的特性。
$listeners:用于传递父组件中的所有事件监听器。
以下是一个示例:
<!-- ParentComponent.vue -->
<template>
<div>
<child-component message="Hello" v-on:click="handleClick"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleClick() {
console.log('Clicked in ParentComponent');
}
}
};
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<grandchild-component v-bind="$attrs" v-on="$listeners"></grandchild-component>
</div>
</template>
<script>
import GrandchildComponent from './GrandchildComponent.vue';
export default {
components: {
GrandchildComponent
}
};
</script>
vue
Copy code
<!-- GrandchildComponent.vue -->
<template>
<div>
<button v-bind="$attrs" v-on="$listeners">{{ message }}</button>
</div>
</template>
<script>
export default {
inheritAttrs: false,
props: ['message']
};
</script>
在这个示例中,ParentComponent 向 ChildComponent 传递了一个特性 message 和一个事件监听器 click。然后,ChildComponent 使用 v-bind="$attrs" 和 v-on="$listeners" 将这些特性和事件传递给了 GrandchildComponent,从而实现了特性和事件的透传。
优点:
父组件可以将自己的特性和事件直接传递给子组件,不需要在子组件中重新声明。
提供了一种简洁的方式来实现透传,避免了重复编写相似的代码。
缺点:
在子组件中使用 $attrs 和 $listeners 可能会让组件之间的关系变得复杂,难以追踪数据流动。
在子组件中使用 $attrs 和 $listeners 时需要注意一些坑,比如事件重命名、特性的影响等。
总之,$attrs 和 $listeners 提供了一种便捷的方式来在父子组件之间传递非 prop 数据和事件,但在使用时需要谨慎考虑,确保不会影响到组件的维护和可读性。
6. EventBus:使用Vue实例作为事件总线,在组件之间传递数据
EventBus 是 Vue 中的一种事件总线模式,它允许你创建一个中央化的事件总线实例,用于在不同组件之间传递事件和数据。
以下是如何使用 EventBus 的一个简单示例:
// EventBus.js
import Vue from 'vue';
const EventBus = new Vue();
export default EventBus;
在这个示例中,我们创建了一个名为 EventBus 的新 Vue 实例,它可以用来传递事件和数据。
然后,在需要传递事件的组件中,你可以使用 EventBus 来触发和监听事件:
// ComponentA.vue
<template>
<div>
<button @click="sendMessage">Send Message</button>
</div>
</template>
<script>
import EventBus from './EventBus';
export default {
methods: {
sendMessage() {
EventBus.$emit('messageSent', 'Hello from ComponentA');
}
}
};
</script>
// ComponentB.vue
<template>
<div>
<p>{{ receivedMessage }}</p>
</div>
</template>
<script>
import EventBus from './EventBus';
export default {
data() {
return {
receivedMessage: ''
};
},
created() {
EventBus.$on('messageSent', message => {
this.receivedMessage = message;
});
}
};
</script>
在这个示例中,ComponentA 使用 EventBus.$emit 发送了一个名为 messageSent 的事件,而 ComponentB 使用 EventBus.$on 来监听该事件并更新显示的消息。
优点:
简单明了,不需要在父子组件之间传递多层嵌套的 props。
可以实现非父子关系的任意两个组件之间的通信。
缺点:
可能会导致事件的跟踪和管理变得困难,因为事件的触发和监听可以发生在任意地方。
在大型应用中,使用全局的事件总线可能会导致事件命名冲突和不易维护的情况。
总之,EventBus 是一种方便的方式来在不同组件之间进行通信,但在使用时需要谨慎考虑,以确保代码的可读性和维护性。
7. Vuex:用于管理大型应用程序中的状态管理
当涉及到 Vuex 的实际代码时,这里我可以为你提供一个简单的示例来说明如何在 Vue 应用中使用 Vuex 进行状态管理。
假设你有一个需要管理用户登录状态的应用。以下是一个使用 Vuex 的基本示例:
安装 Vuex:
首先,在你的 Vue 项目中安装 Vuex。
bash
npm install vuex --save
创建 Vuex Store:
在项目中创建一个 Vuex Store,它将管理你的应用状态。假设你的应用需要管理用户的登录状态。
javascript
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
loggedIn: false,
user: null,
},
mutations: {
login(state, user) {
state.loggedIn = true;
state.user = user;
},
logout(state) {
state.loggedIn = false;
state.user = null;
},
},
actions: {
loginUser({ commit }, user) {
// Simulate an API call
setTimeout(() => {
commit('login', user);
}, 1000);
},
logoutUser({ commit }) {
commit('logout');
},
},
getters: {
isLoggedIn: state => state.loggedIn,
currentUser: state => state.user,
},
});
export default store;
在主应用中注册 Vuex Store:
在你的 Vue 主应用中,注册创建的 Vuex Store。
javascript
// main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store';
new Vue({
render: h => h(App),
store, // 注册 Vuex Store
}).$mount('#app');
在组件中使用 Vuex 的状态和方法:
现在你可以在你的组件中使用 Vuex 的状态和方法了。
<template>
<div>
<h1>My App</h1>
<p v-if="isLoggedIn">Logged in as {{ currentUser }}</p>
<button @click="login">Login</button>
<button @click="logout">Logout</button>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
computed: {
...mapGetters(['isLoggedIn', 'currentUser']),
},
methods: {
...mapActions(['loginUser', 'logoutUser']),
login() {
this.loginUser('john_doe'); // 调用 Vuex action
},
logout() {
this.logoutUser(); // 调用 Vuex action
},
},
};
</script>
这个简单的示例展示了如何创建一个简单的 Vuex Store,并在组件中使用 Vuex 的状态和方法。在实际应用中,你可以根据需求进行更复杂的状态管理和业务逻辑处理。
8. $parent/$children:通过父/子组件实例的引用进行通讯
通过 $parent 和 $children 可以在 Vue 组件中进行父子组件之间的通信。这两个属性分别允许你在组件内部访问父组件和子组件的实例,从而实现数据和方法的传递。
这里是一个简单的示例,展示了如何使用 $parent 和 $children 进行通信:
假设你有一个父组件 ParentComponent 和一个子组件 ChildComponent,你想要在子组件中调用父组件的方法并访问父组件的数据。
<template>
<div>
<h1>Parent Component</h1>
<p>Count: {{ count }}</p>
<button @click="incrementCount">Increment Count</button>
<ChildComponent></ChildComponent>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
data() {
return {
count: 0,
};
},
methods: {
incrementCount() {
this.count++;
},
},
};
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<h2>Child Component</h2>
<button @click="callParentMethod">Call Parent Method</button>
</div>
</template>
<script>
export default {
methods: {
callParentMethod() {
// 通过 $parent 访问父组件的实例并调用方法
this.$parent.incrementCount();
},
},
};
</script>
在这个示例中,子组件通过 $parent 访问了父组件的实例,并调用了父组件的 incrementCount 方法,从而更新了父组件中的 count 数据。
尽管 $parent 和 $children 可以用来在组件之间传递数据和调用方法,但这种方式并不是最优雅和推荐的通信方式,因为它们会在组件结构发生变化时变得脆弱。更好的做法是使用 props 和 $emit 实现父子组件之间的通信,或者使用 Vuex 进行状态管理。
9. provide/inject与$attrs/$listeners配合使用:更复杂的祖先/后代之间的通讯方式
provide 和 inject 是用于在 Vue 组件树中进行祖先组件向后代组件传递数据的高级通信方式。它们通常在复杂的组件嵌套结构中使用,以避免多层级嵌套时的繁琐的数据传递。
这里是一个示例,演示了如何在祖先组件中使用 provide 提供数据,然后在后代组件中使用 inject 来接收这些数据,并配合 $attrs 和 $listeners 实现更复杂的通信。
<template>
<div>
<h1>Grandparent Component</h1>
<p>Grandparent Data: {{ grandparentData }}</p>
<ParentComponent></ParentComponent>
</div>
</template>
<script>
import ParentComponent from './ParentComponent.vue';
export default {
components: {
ParentComponent,
},
provide() {
return {
grandparentData: 'Data from Grandparent',
};
},
};
</script>
<!-- ParentComponent.vue -->
<template>
<div>
<h2>Parent Component</h2>
<p>Parent Data: {{ parentData }}</p>
<ChildComponent></ChildComponent>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
inject: ['grandparentData'], // 接收祖先组件提供的数据
data() {
return {
parentData: 'Data from Parent',
};
},
};
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<h3>Child Component</h3>
<p>Child Data: {{ childData }}</p>
<p>Grandparent Data via inject: {{ grandparentData }}</p>
<p v-bind="$attrs">Other Attributes from Parent</p>
<button v-on="$listeners">Click Me</button>
</div>
</template>
<script>
export default {
inject: ['grandparentData'], // 接收祖先组件提供的数据
props: ['childData'],
};
</script>
在这个示例中,provide 和 inject 配合使用,允许 GrandparentComponent 向其后代组件传递数据。在 ChildComponent 中,通过 inject 可以接收来自祖先组件的数据 grandparentData。此外,使用 $attrs 可以传递来自父组件的其他属性,而使用 $listeners 可以将父组件的事件监听器传递给子组件。
请注意,$attrs 和 $listeners 只在子组件的根元素上有效,而不会传递给子组件中的子组件。这种方式适用于需要在组件层次结构中传递属性和事件的情况,但在复杂通信需求时,更推荐使用 Vuex 或事件总线等更高级的通信方式。
10. Teleport:在DOM树中任意位置渲染组件
Teleport 是 Vue 3 中新增的特性,用于在组件的模板中将内容渲染到 DOM 树中的任意位置,而不受组件层次结构的限制。它适用于需要在组件之外渲染内容的场景,比如弹出框、对话框等。
以下是一个示例,演示如何使用 Teleport 渲染一个弹出框:
<template>
<div>
<button @click="showModal = true">Show Modal</button>
<Teleport to="body">
<Modal v-if="showModal" @close="showModal = false">
<!-- Modal Content -->
</Modal>
</Teleport>
</div>
</template>
<script>
import { ref } from 'vue';
import Modal from './Modal.vue';
export default {
components: {
Modal,
},
setup() {
const showModal = ref(false);
return {
showModal,
};
},
};
</script>
在这个示例中,当点击 "Show Modal" 按钮时,Teleport 将 <Modal> 组件的内容渲染到 <body> 元素下,而不受组件层次结构的影响。这样可以确保弹出框的样式和行为在不同层次的组件中都是一致的。
需要注意的是,Teleport 在渲染内容时仍然会受到 CSS 层叠上下文和层次结构的影响,因此需要在使用时注意样式的调整。
总结起来,Teleport 是一个很有用的特性,可以方便地将组件的内容渲染到任意位置,增强了 Vue 在处理 UI 布局和渲染方面的灵活性。
11. Composition API:使用setup()函数和reactive()函数等新的API编写组件
Composition API 是 Vue 3 中引入的一组新的 API,旨在更好地组织和复用代码,特别是在处理复杂逻辑的组件时。它通过 setup() 函数和一些新的函数(如 reactive()、computed()、watch() 等)来编写组件。
下面是一个简单的示例,演示了如何使用 Composition API 编写一个基本的计数器组件:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
// 创建一个响应式变量
const count = ref(0);
// 定义一个函数来增加计数
const increment = () => {
count.value++;
};
// 返回响应式数据和函数
return {
count,
increment,
};
},
};
</script>
在这个示例中,使用 ref() 函数来创建一个响应式变量 count,然后定义了一个 increment 函数来增加计数。通过 setup() 函数返回 count 和 increment,使它们在模板中可以被访问和使用。
Composition API 还引入了其他一些新的函数,例如 reactive() 用于创建响应式对象,computed() 用于创建计算属性,watch() 用于监听响应式数据的变化等等。这些 API 可以帮助你更灵活、更清晰地组织和管理组件中的逻辑。
总体而言,Composition API 是 Vue 3 中一个非常强大的功能,使得编写组件变得更加灵活、可维护和可测试。它特别适合处理复杂的业务逻辑和状态管理。
12. Directive:自定义指令,用于操作DOM元素
Vue 提供了自定义指令的机制,允许你在 DOM 元素上添加自定义行为。自定义指令可以用于操作 DOM、添加事件监听器、修改元素样式等等。
下面是一个简单的示例,展示如何创建一个自定义指令来实现双击文本选中功能:
<template>
<div>
<p v-selectable>Double click to select this text.</p>
</div>
</template>
<script>
// 注册一个全局自定义指令 `v-selectable`
app.directive('selectable', {
// 当绑定元素插入到 DOM 中时
mounted(el) {
// 添加双击事件监听
el.addEventListener('dblclick', () => {
// 选中文本
window.getSelection().selectAllChildren(el);
});
}
});
export default {
name: 'CustomDirectiveExample',
};
</script>
在这个示例中,我们使用 app.directive 来注册一个名为 selectable 的全局自定义指令。在 mounted 钩子中,我们为元素添加了双击事件监听,当元素被双击时,会选中元素中的文本内容。
通过使用自定义指令,你可以在 Vue 中实现各种自定义的 DOM 操作和交互行为,从而更好地控制页面的交互和展示。
需要注意的是,自定义指令在 Vue 3 的 Composition API 中也得到了改进,可以更灵活地定义和使用。
13. Component Events:使用Vue.extend()创建一个可重用的组件,可以通过props和emit实现组件之间的通讯。
Vue 中是一种非常常见的组件之间通信的方法。通过 Vue.extend() 创建的可重用组件可以通过 props 接收父组件传递的数据,并通过 this.$emit() 发送事件给父组件。
以下是一个简单示例,演示如何通过 props 和 emit 在父子组件之间进行通信:
首先,我们有一个可重用的子组件 ChildComponent:
<template>
<div>
<p>Child Component</p>
<button @click="sendMessage">Send Message to Parent</button>
</div>
</template>
<script>
export default {
methods: {
sendMessage() {
this.$emit('message-from-child', 'Hello from child!');
}
}
};
</script>
然后,在父组件中使用这个子组件,并通过 props 传递数据和监听事件:
<template>
<div>
<p>Parent Component</p>
<ChildComponent @message-from-child="receiveMessage" />
<p>{{ receivedMessage }}</p>
</div>
</template>
<script>
import Vue from 'vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent: Vue.extend(ChildComponent)
},
data() {
return {
receivedMessage: ''
};
},
methods: {
receiveMessage(message) {
this.receivedMessage = message;
}
}
};
</script>
在这个例子中,父组件通过 ChildComponent 组件的 @message-from-child 事件监听来接收子组件发送的消息。子组件通过 this.$emit() 发送消息给父组件。
通过这种方式,你可以在 Vue 中实现父子组件之间的数据传递和事件通信。这种方式是 Vue 组件之间通讯的基础,非常常用和有效。