MST

星途 面试题库

面试题:Vue 项目中使用 TypeScript 时,如何更好地处理组件间通信的类型定义

在一个 Vue 项目中,有父子组件通信、兄弟组件通信和跨层级组件通信等场景,使用 TypeScript 时,怎样清晰准确地定义这些通信过程中传递数据的类型,以提高代码的健壮性和可维护性,请结合具体的代码示例阐述。
48.6万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试

父子组件通信

  1. 父传子
    • 在父组件中,定义传递给子组件的数据。在子组件中,使用 props 接收并定义类型。
    • 示例:
      • 定义数据类型接口:
        // 定义一个接口用于描述父传子的数据类型
        interface ParentToChildProps {
          message: string;
          count: number;
        }
        
      • 子组件:
        <template>
          <div>
            <p>接收到的消息: {{ message }}</p>
            <p>接收到的数字: {{ count }}</p>
          </div>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import { ParentToChildProps } from './types';// 假设接口定义在 types.ts 文件
        
        export default defineComponent({
          name: 'ChildComponent',
          props: {
            message: {
              type: String,
              required: true
            },
            count: {
              type: Number,
              default: 0
            }
          } as ParentToChildProps
        });
        </script>
        
      • 父组件:
        <template>
          <div>
            <ChildComponent :message="parentMessage" :count="parentCount"/>
          </div>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import ChildComponent from './ChildComponent.vue';
        
        export default defineComponent({
          name: 'ParentComponent',
          components: {
            ChildComponent
          },
          data() {
            return {
              parentMessage: 'Hello from parent',
              parentCount: 10
            };
          }
        });
        </script>
        
  2. 子传父
    • 子组件通过 $emit 触发事件并传递数据,父组件监听事件接收数据。需要定义事件传递数据的类型。
    • 示例:
      • 定义事件传递数据类型接口:
        // 定义子传父事件传递数据的接口
        interface ChildToParentEvent {
          (newValue: string): void;
        }
        
      • 子组件:
        <template>
          <button @click="sendDataToParent">发送数据给父组件</button>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import { ChildToParentEvent } from './types';// 假设接口定义在 types.ts 文件
        
        export default defineComponent({
          name: 'ChildComponent',
          methods: {
            sendDataToParent() {
              const data = 'Data from child';
              this.$emit('child - to - parent', data);
            }
          }
        });
        </script>
        
      • 父组件:
        <template>
          <div>
            <ChildComponent @child - to - parent="handleChildData"/>
            <p>接收到子组件的数据: {{ receivedData }}</p>
          </div>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import ChildComponent from './ChildComponent.vue';
        
        export default defineComponent({
          name: 'ParentComponent',
          components: {
            ChildComponent
          },
          data() {
            return {
              receivedData: ''
            };
          },
          methods: {
            handleChildData(data: string) {
              this.receivedData = data;
            }
          }
        });
        </script>
        

兄弟组件通信

  1. 通过事件总线(mitt 等库)
    • 安装 mittnpm install mitt
    • 定义事件传递数据类型。
    • 示例:
      • 定义事件类型接口:
        // 定义兄弟组件间事件传递数据的接口
        import mitt from'mitt';
        
        type BroEvent = {
          'brother - to - brother': string;
        };
        
        const emitter = mitt<BroEvent>();
        export default emitter;
        
      • 发送数据的兄弟组件:
        <template>
          <button @click="sendDataToBrother">发送数据给兄弟组件</button>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import emitter from './emitter';// 假设 emitter 定义在 emitter.ts 文件
        
        export default defineComponent({
          name: 'BrotherComponent1',
          methods: {
            sendDataToBrother() {
              const data = 'Data from brother 1';
              emitter.emit('brother - to - brother', data);
            }
          }
        });
        </script>
        
      • 接收数据的兄弟组件:
        <template>
          <div>
            <p>接收到兄弟组件的数据: {{ receivedData }}</p>
          </div>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import emitter from './emitter';// 假设 emitter 定义在 emitter.ts 文件
        
        export default defineComponent({
          name: 'BrotherComponent2',
          data() {
            return {
              receivedData: ''
            };
          },
          mounted() {
            emitter.on('brother - to - brother', (data) => {
              this.receivedData = data;
            });
          },
          beforeUnmount() {
            emitter.off('brother - to - brother');
          }
        });
        </script>
        

跨层级组件通信

  1. 使用 Vuex
    • 安装 vuexnpm install vuex
    • 定义 statemutationsactions 等的类型。
    • 示例:
      • 定义 state 类型接口:
        // 定义 Vuex state 的类型
        interface GlobalState {
          globalMessage: string;
        }
        
      • 定义 mutations 类型接口:
        // 定义 Vuex mutations 的类型
        import { MutationTree } from 'vuex';
        import { GlobalState } from './types';// 假设 GlobalState 定义在 types.ts 文件
        
        const mutations: MutationTree<GlobalState> = {
          SET_GLOBAL_MESSAGE(state, newMessage: string) {
            state.globalMessage = newMessage;
          }
        };
        export default mutations;
        
      • 定义 actions 类型接口(如果有):
        // 定义 Vuex actions 的类型
        import { ActionTree } from 'vuex';
        import { GlobalState } from './types';// 假设 GlobalState 定义在 types.ts 文件
        
        const actions: ActionTree<GlobalState, GlobalState> = {
          updateGlobalMessage({ commit }, newMessage: string) {
            commit('SET_GLOBAL_MESSAGE', newMessage);
          }
        };
        export default actions;
        
      • store 配置:
        import { createStore } from 'vuex';
        import mutations from './mutations';
        import actions from './actions';
        import { GlobalState } from './types';// 假设 GlobalState 定义在 types.ts 文件
        
        const state: GlobalState = {
          globalMessage: 'Initial global message'
        };
        
        const store = createStore({
          state,
          mutations,
          actions
        });
        
        export default store;
        
      • 在组件中使用:
        <template>
          <div>
            <p>全局消息: {{ globalMessage }}</p>
            <button @click="updateGlobalMessage">更新全局消息</button>
          </div>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import { useStore } from 'vuex';
        
        export default defineComponent({
          name: 'DeepComponent',
          computed: {
            globalMessage() {
              const store = useStore();
              return store.state.globalMessage;
            }
          },
          methods: {
            updateGlobalMessage() {
              const store = useStore();
              store.dispatch('updateGlobalMessage', 'New global message');
            }
          }
        });
        </script>
        
  2. 使用 provide 和 inject
    • 在祖先组件中 provide 数据,后代组件中 inject 数据,并定义好数据类型。
    • 示例:
      • 定义提供数据的类型接口:
        // 定义 provide 数据的类型
        interface ProvidedData {
          sharedValue: string;
        }
        
      • 祖先组件:
        <template>
          <div>
            <slot/>
          </div>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import { ProvidedData } from './types';// 假设接口定义在 types.ts 文件
        
        export default defineComponent({
          name: 'AncestorComponent',
          provide() {
            return {
              sharedValue: 'Shared value from ancestor'
            } as ProvidedData;
          }
        });
        </script>
        
      • 后代组件:
        <template>
          <div>
            <p>注入的数据: {{ injectedValue }}</p>
          </div>
        </template>
        
        <script lang="ts">
        import { defineComponent } from 'vue';
        import { ProvidedData } from './types';// 假设接口定义在 types.ts 文件
        
        export default defineComponent({
          name: 'DescendantComponent',
          inject: {
            injectedValue: {
              from:'sharedValue',
              default: ''
            }
          } as ProvidedData
        });
        </script>