普通视图

发现新文章,点击刷新页面。
昨天 — 2025年10月24日首页

vue3组件通信

作者 东华帝君
2025年10月24日 10:40

在 Vue 3 中,父子组件之间的通信方式非常丰富,可以根据组件的关系、数据流方向、复杂程度来选择。下面我帮你整理成一个 清晰体系,包括 优缺点、使用场景、示例


🧩 一、Props + Event(最常用、单向数据流)

1️⃣ 父组件 → 子组件:props

  • 父组件通过 props 向子组件传递数据
  • 数据是响应式的,子组件可读不可直接修改父组件的值(可通过 v-model 或自定义事件来实现双向绑定)
<!-- Parent.vue -->
<template>
  <Child :msg="message" @change="handleChange" />
</template>

<script>
import Child from './Child.vue'
import { ref } from 'vue'

export default {
  components: { Child },
  setup() {
    const message = ref('Hello Vue 3')
    const handleChange = (val) => { console.log('子组件通知:', val) }
    return { message, handleChange }
  }
}
</script>
<!-- Child.vue -->
<template>
  <button @click="$emit('change', '子组件事件')">{{ msg }}</button>
</template>

<script>
export default {
  props: ['msg']
}
</script>

2️⃣ 子组件 → 父组件:emit

  • 子组件通过 $emit 发事件通知父组件
  • 常用于按钮点击、数据修改、事件触发等

🧩 二、v-model 双向绑定(语法糖)

  • Vue 3 支持在子组件上自定义 v-model 的 prop 名和事件名
  • 常用于表单组件或数据需要双向绑定的场景
<!-- Parent.vue -->
<template>
  <Child v-model:count="count" />
  <p>{{ count }}</p>
</template>

<script>
import { ref } from 'vue'
import Child from './Child.vue'
export default { components: { Child }, setup() { const count = ref(0); return { count } } }
</script>

<!-- Child.vue -->
<template>
  <button @click="$emit('update:count', count+1)">+1</button>
</template>
<script>
import { ref } from 'vue'
export default {
  props: { count: Number },
  setup(props) { return { count: props.count } }
}
</script>

🧩 三、Provide / Inject(跨级组件传递)

  • 父组件使用 provide 提供数据
  • 子组件及深层组件使用 inject 注入数据
  • 用于多级组件通信、避免层层传递 props
<!-- GrandParent.vue -->
<script setup>
import { provide } from 'vue'
const theme = 'dark'
provide('theme', theme)
</script>

<!-- Child.vue -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
console.log(theme) // 'dark'
</script>

⚠️ 注意:Provide / Inject 不是响应式的,如果需要响应式,提供的是 refreactive


🧩 四、通过 ref 获取子组件实例

  • 父组件通过 ref 获取子组件实例,调用子组件的方法
  • 常用于非数据通信,比如调用子组件的内部方法、重置表单、播放视频等
<!-- Parent.vue -->
<template>
  <Child ref="childRef" />
  <button @click="callChild">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const childRef = ref(null)
const callChild = () => { childRef.value.sayHello() }
</script>

<!-- Child.vue -->
<script setup>
const sayHello = () => { console.log('Hello from child') }
</script>

🧩 五、全局状态管理(Vuex / Pinia / reactive store)

  • 父子组件通过共享 全局 store 实现通信
  • 适合跨组件、跨页面共享状态
  • Vue 3 推荐 Pinia 作为轻量化状态管理
// store.js
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
  state: () => ({ count: 0 }),
  actions: { increment() { this.count++ } }
})

// Parent.vue / Child.vue
import { useStore } from './store'
const store = useStore()
store.increment() // 任意组件都可以修改

🧩 六、Event Bus(自定义事件总线)

  • Vue 3 不再内置 EventBus,但可以用 mitt 创建事件总线
  • 常用于兄弟组件通信,或者父子组件通信也可
  • 不推荐大量使用,容易造成维护困难
// bus.js
import mitt from 'mitt'
export const bus = mitt()

// Parent.vue
import { bus } from './bus.js'
bus.on('myEvent', val => console.log(val))

// Child.vue
import { bus } from './bus.js'
bus.emit('myEvent', 'hello bus')

🧠 总结

通信方式 方向 场景 备注
Props + emit 父 → 子 / 子 → 父 常规父子通信 最推荐
v-model 父 ↔ 子 表单 / 双向绑定 Vue 3 支持自定义 prop 名
provide / inject 父 → 多级子 跨级传值 避免层层传递 props
ref 调用子组件方法 父 → 子 调用方法 非数据通信
Pinia / Vuex 全局 跨组件 / 跨页面 状态管理必备
Event Bus (mitt) 全局 / 任意方向 兄弟组件 / 临时通信 不推荐大量使用

总结一句话:

一般情况用 Props + emit / v-model;跨级用 provide/inject;跨组件用 Pinia;调用方法用 ref;EventBus 仅作补充。

昨天以前首页

react组件常见的性能优化

作者 东华帝君
2025年10月18日 16:06

React 组件的性能优化是构建流畅用户体验的关键。下面我为你梳理了常见的优化方法、适用场景及核心原理,并附上一个总结表格,方便你快速概览。下表汇总了主要的优化策略及其核心目标:

优化策略 核心目标 典型方法或工具
减少渲染量 减少需要渲染的组件或 DOM 节点数量 组件懒加载、虚拟列表、条件渲染
减少渲染次数 避免不必要的组件重新渲染 React.memoPureComponentuseCallbackuseMemo
提升渲染效率 降低单次渲染的耗时和复杂度 使用不可变数据、简化组件逻辑、优化状态结构

🔧 核心优化方法详解

1. 使用 React.memo 和 PureComponent 避免重复渲染

这是最直接有效的优化手段之一。

  • React.memo (用于函数组件):对组件 props 进行浅比较,仅在 props 发生变化时重新渲染。

    import { memo } from 'react';
    
    const MyExpensiveComponent = memo(function MyExpensiveComponent({ data }) {
      // 组件逻辑
      return <div>{/* 渲染内容 */}</div>;
    });
    
  • PureComponent (用于类组件):通过浅比较 props 和 state 来自动判断是否需要重新渲染。

2. 使用 useCallback 和 useMemo 缓存记忆化

用于缓存那些在多次渲染间需要保持稳定的函数或计算结果。

  • useCallback:缓存函数,避免因函数引用变化导致子组件不必要的重渲染。

    import { useCallback, useState } from 'react';
    
    function ParentComponent() {
      const [count, setCount] = useState(0);
      // 使用 useCallback 缓存函数
      const handleClick = useCallback(() => {
        setCount(c => c + 1);
      }, []); // 依赖数组为空,表示该函数不会重建
    
      return <ChildComponent onClick={handleClick} />;
    }
    
  • useMemo:缓存计算结果,避免每次渲染都进行复杂的计算。

    import { useMemo } from 'react';
    
    function ExpensiveCalculationComponent({ items }) {
      const computedValue = useMemo(() => {
        return items.reduce((acc, item) => {
          // 复杂的计算逻辑
          return acc + item.value;
        }, 0);
      }, [items]); // 当 items 变化时重新计算
    
      return <div>{computedValue}</div>;
    }
    

3. 代码分割与懒加载 (Code Splitting & Lazy Loading)

通过动态导入(dynamic imports)将代码分割成不同的块(chunks),按需加载,显著降低应用初始加载体积。

import { lazy, Suspense } from 'react';

// 使用 React.lazy 进行动态导入
const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <div>
      {/* 使用 Suspense 提供加载中的回退界面 */}
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  );
}

4. 虚拟列表 (Virtualization)

当渲染超长列表时,虚拟列表技术可以极大提升性能。它只渲染当前可视区域(viewport)内的列表项,而不是整个列表。

import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const VirtualizedList = () => (
  <List
    height={400} // 列表容器高度
    itemCount={10000} // 列表项总数
    itemSize={35} // 每个列表项的高度
    width={600} // 列表容器宽度
  >
    {Row}
  </List>
);

5. 优化事件处理与避免内联对象

在 JSX 中直接定义函数或对象,会导致每次渲染都创建一个新的引用,可能引发子组件不必要的重渲染。

  • 优化前

    // 不推荐:内联函数和内联对象
    <MyComponent 
      onClick={() => { /* 处理逻辑 */ }}
      style={{ color: 'red' }}
    />
    
  • 优化后

    // 推荐:使用 useCallback 和 useMemo/外部定义
    const handleClick = useCallback(() => { /* 处理逻辑 */ }, []);
    const style = useMemo(() => ({ color: 'red' }), []);
    
    <MyComponent onClick={handleClick} style={style} />
    

6. 优化列表的 Key 属性

为列表项提供稳定、唯一的 key属性,帮助 React 更准确地识别哪些项发生了变化、被添加或移除,从而高效地更新 DOM。避免使用数组索引作为 key,尤其是在列表会发生重排序的情况下。

// 推荐:使用唯一ID
{items.map(item => (
  <li key={item.id}>{item.name}</li>
))}
❌
❌