面试题答案
一键面试常见通信方式
- 事件总线(Event Bus)
- 原理:创建一个空的Vue实例作为事件总线,通过
$on
方法监听事件,$emit
方法触发事件。非父子组件都可以访问这个事件总线实例,从而实现通信。 - 适用场景:适用于组件间简单的、临时性的通信场景,特别是在小型项目或局部组件间通信。
- 实际项目使用示例:
- 创建事件总线实例(假设在
main.js
中):
- 创建事件总线实例(假设在
- 原理:创建一个空的Vue实例作为事件总线,通过
import Vue from 'vue';
// 创建事件总线实例
export const eventBus = new Vue();
- 在发送数据的组件中:
<template>
<button @click="sendData">发送数据</button>
</template>
<script>
import { eventBus } from '@/main';
export default {
methods: {
sendData() {
const data = 'Hello, from non - parent component';
eventBus.$emit('custom - event', data);
}
}
};
</script>
- 在接收数据的组件中:
<template>
<div>{{ receivedData }}</div>
</template>
<script>
import { eventBus } from '@/main';
export default {
data() {
return {
receivedData: ''
};
},
created() {
eventBus.$on('custom - event', (data) => {
this.receivedData = data;
});
}
};
</script>
- **在大型项目中的优缺点**:
- **优点**:简单易用,不需要复杂的状态管理配置,对于局部组件通信可以快速实现。
- **缺点**:随着项目规模扩大,事件总线可能会变得难以维护。事件的触发和监听可能分散在不同组件,追踪和调试困难,而且可能会出现命名冲突等问题。
2. Vuex
- 原理:Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。组件通过commit
提交mutation
来修改状态,通过dispatch
触发action
来处理异步操作等。
- 适用场景:适用于大型单页应用,当多个组件需要共享状态,并且状态变化较为复杂时,Vuex可以更好地管理状态。
- 实际项目使用示例:
- 首先配置Vuex(假设在store.js
中):
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
sharedData: ''
},
mutations: {
updateSharedData(state, data) {
state.sharedData = data;
}
},
actions: {
updateSharedDataAsync({ commit }, data) {
setTimeout(() => {
commit('updateSharedData', data);
}, 1000);
}
}
});
export default store;
- 在发送数据的组件中:
<template>
<button @click="sendData">发送数据</button>
</template>
<script>
export default {
methods: {
sendData() {
const data = 'Hello, from non - parent component';
// 提交mutation
this.$store.commit('updateSharedData', data);
// 或者触发action
this.$store.dispatch('updateSharedDataAsync', data);
}
}
};
</script>
- 在接收数据的组件中:
<template>
<div>{{ sharedData }}</div>
</template>
<script>
export default {
computed: {
sharedData() {
return this.$store.state.sharedData;
}
}
};
</script>
- **在大型项目中的优缺点**:
- **优点**:状态管理集中化,便于维护和调试。所有状态变化都有迹可循,便于追踪和理解应用的数据流。同时,适合多人协作开发,能够更好地规范状态修改的方式。
- **缺点**:引入了一定的学习成本,对于简单项目可能过于复杂。配置和使用相对繁琐,需要遵循严格的规则。而且如果滥用,可能会导致状态管理过度复杂,增加维护难度。
3. $attrs 和 $listeners
- 原理:$attrs
包含了父作用域中不作为props
被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何props
时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过v - bind="$attrs"
传入内部组件。$listeners
包含了父作用域中的 (不含.native 修饰器的) v - on 事件监听器,可以通过v - on="$listeners"
传入内部组件。这使得非父子组件通过中间组件间接传递数据和事件。
- 适用场景:适用于存在多层嵌套的组件结构,且需要跨级传递数据的场景,避免在中间组件逐一传递数据。
- 实际项目使用示例:假设存在A -> B -> C三层组件嵌套,A与C是非父子关系。
- 在A组件中:
<template>
<BComponent :data - from - a="message" @custom - event - from - a="handleEvent"></BComponent>
</template>
<script>
import BComponent from './BComponent.vue';
export default {
components: {
BComponent
},
data() {
return {
message: 'Data from A'
};
},
methods: {
handleEvent() {
console.log('Event received from C through B');
}
}
};
</script>
- 在B组件中:
<template>
<CComponent v - bind="$attrs" v - on="$listeners"></CComponent>
</template>
<script>
import CComponent from './CComponent.vue';
export default {
components: {
CComponent
}
};
</script>
- 在C组件中:
<template>
<div>
<p>{{ data - from - a }}</p>
<button @click="$emit('custom - event - from - a')">触发事件到A</button>
</div>
</template>
<script>
export default {
inheritAttrs: false
};
</script>
- **在大型项目中的优缺点**:
- **优点**:不需要额外的状态管理库,在多层嵌套组件间传递数据较为方便,减少了中间组件逐一传递数据的繁琐。
- **缺点**:数据传递不够直观,容易在嵌套层级较深时造成数据流向混乱,增加调试难度。而且这种方式主要用于传递数据和事件,对于复杂的状态管理能力有限。
4. Vue Router
- 原理:通过路由参数传递数据。当路由切换时,可以在路由配置中设置参数,不同组件根据当前路由参数获取数据,从而实现非父子组件间通信。
- 适用场景:适用于不同页面组件间的通信,尤其是在页面跳转过程中需要传递数据的场景。
- 实际项目使用示例:
- 在路由配置(假设在router.js
中):
import Vue from 'vue';
import Router from 'vue-router';
import ComponentA from './components/ComponentA.vue';
import ComponentB from './components/ComponentB.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/component - a',
name: 'ComponentA',
component: ComponentA
},
{
path: '/component - b/:data',
name: 'ComponentB',
component: ComponentB
}
]
});
- 在ComponentA组件中:
<template>
<button @click="goToComponentB">跳转到ComponentB并传递数据</button>
</template>
<script>
export default {
methods: {
goToComponentB() {
const data = 'Data from ComponentA';
this.$router.push({ name: 'ComponentB', params: { data } });
}
}
};
</script>
- 在ComponentB组件中:
<template>
<div>{{ dataFromA }}</div>
</template>
<script>
export default {
data() {
return {
dataFromA: ''
};
},
created() {
this.dataFromA = this.$route.params.data;
}
};
</script>
- **在大型项目中的优缺点**:
- **优点**:适用于页面级别的数据传递,结合路由功能,在页面跳转时传递数据很方便,符合页面间导航的业务逻辑。
- **缺点**:只适用于路由切换场景下的数据传递,局限性较大。而且如果参数过多或复杂,路由配置和组件获取参数的逻辑会变得复杂,不利于维护。