欢迎光临
我们一直在努力

2026年 React/Vue 框架级性能优化实战:从源码原理到业务落地

2026年 React/Vue 框架级性能优化实战:从源码原理到业务落地

前言

在之前的文章中,我们按照"基础优化 → 性能监控 → 框架优化 → 工程化管控 → 跨端优化"的顺序,逐步深入前端性能优化领域。前两篇文章分别介绍了通用的前端性能优化策略和性能监控体系,为我们打下了坚实的基础。

但在实际开发中,很多性能问题是框架特有的,即使做了通用优化,框架层面的性能瓶颈仍然会导致应用卡顿。作为一名前端架构师,我曾遇到过这样的挑战:

  • 一个React电商应用,在商品列表页滚动时卡顿严重,即使做了代码分割和图片懒加载,问题依然存在。最终发现是React的重渲染机制导致的——每次滚动都会触发大量组件的不必要渲染。
  • 一个Vue企业管理系统,大数据表格渲染时页面直接崩溃,通过分析Vue的响应式原理,我找到了问题的根源并成功优化。
  • 一个混合使用React和Vue的大型应用,两个框架之间的通信和状态管理导致了严重的性能问题。

本文作为系列的第三篇,将深入React和Vue的源码原理,分享框架级的性能优化技巧,帮助你解决那些通用优化无法解决的性能瓶颈。同时,我会结合最新的框架特性和实战经验,提供更加全面和深入的优化方案。

一、React 框架级性能优化

1. React 渲染原理与性能瓶颈

1.1 React 渲染机制

React 的渲染过程分为两个阶段:

  • 渲染阶段:React 计算哪些组件需要更新,生成新的虚拟 DOM(Fiber 树)
  • 提交阶段:React 将变更应用到真实 DOM
  • React 18 并发特性深度解析:

    • 自动批处理:

      • 不仅在事件处理器中,还在 Promise、setTimeout、原生事件等场景中自动批处理状态更新
      • 减少渲染次数,提高应用响应速度
      • 示例:多个 setState 调用被合并为一次渲染
    • 时间切片:

      • 将渲染工作分成小块(通常不超过 5ms)
      • 在浏览器空闲时执行,避免阻塞主线程
      • 提高用户交互的流畅度,特别是在复杂计算场景
    • Suspense:

      • 允许组件在数据加载完成前"挂起"
      • 显示加载状态,改善用户体验
      • 支持服务器组件和流式渲染
    • 优先级调度:

      • 使用 Lane 模型区分不同优先级的更新
      • 高优先级更新(如用户输入)优先执行
      • 低优先级更新(如数据加载)可以被中断

    性能瓶颈:

    • 组件不必要的重渲染
    • 大列表渲染导致的虚拟 DOM 计算开销
    • 复杂计算在渲染过程中重复执行
    • 状态更新导致的连锁反应
    • 长任务阻塞主线程
    • 数据加载时的白屏或闪烁
    1.2 源码级理解

    // React 核心渲染逻辑简化版(React 18+)
    function renderWithHooks(current, workInProgress, Component, props) {
    // 设置当前 fiber
    currentlyRenderingFiber = workInProgress;

    // 重置 hook 索引
    workInProgress.memoizedState = null;

    // 调用组件函数
    const children = Component(props);

    // 完成渲染
    currentlyRenderingFiber = null;

    return children;
    }

    // 状态更新触发重渲染
    function updateState(queue, action) {
    // 创建更新对象
    const update = { action, next: null, lane: getCurrentLane() };

    // 将更新加入队列
    if (queue.lastUpdate === null) {
    queue.firstUpdate = queue.lastUpdate = update;
    } else {
    queue.lastUpdate.next = update;
    queue.lastUpdate = update;
    }

    // 调度更新(React 18 支持并发调度)
    scheduleUpdateOnFiber(workInProgress, lane);
    }

    // 批量更新逻辑(React 18 增强版)
    function batchUpdates(callback) {
    const previousIsBatchingUpdates = isBatchingUpdates;
    isBatchingUpdates = true;

    try {
    return callback();
    } finally {
    isBatchingUpdates = previousIsBatchingUpdates;
    // 如果是最外层批处理,执行更新
    if (!isBatchingUpdates) {
    flushSyncCallbackQueue();
    }
    }
    }

    // 并发调度核心
    function scheduleUpdateOnFiber(fiber, lane) {
    // 标记 fiber 为需要更新
    markRootUpdated(root, lane);

    // 调度更新
    if (isConcurrentRoot(root)) {
    // 并发模式:使用时间切片
    ensureRootIsScheduled(root);
    } else {
    // 同步模式:立即执行
    scheduleSyncCallback(() => {
    performSyncWorkOnRoot(root);
    });
    }
    }

    1.3 React 18 并发特性的性能影响与实战

    React 18 的并发特性为性能优化带来了新的机遇和挑战:

    性能提升:

  • 自动批处理:减少渲染次数,提高响应速度
  • 时间切片:避免长任务阻塞主线程,提高交互流畅度
  • Suspense:改善数据加载体验,减少白屏时间
  • 优先级调度:确保重要更新优先执行
  • 实战应用:

    // 使用 useTransition 处理低优先级更新
    import { useState, useTransition } from 'react';

    const SearchComponent = ({ items }) => {
    const [query, setQuery] = useState('');
    const [filteredItems, setFilteredItems] = useState(items);
    const [isPending, startTransition] = useTransition();

    const handleSearch = (e) => {
    const newQuery = e.target.value;
    setQuery(newQuery);

    // 将过滤操作标记为低优先级
    startTransition(() => {
    const filtered = items.filter(item =>
    item.name.toLowerCase().includes(newQuery.toLowerCase())
    );
    setFilteredItems(filtered);
    });
    };

    return (
    <div>
    <input type="text" value={query} onChange={handleSearch} />
    {isPending && <div>搜索中…</div>}
    <ul>
    {filteredItems.map(item => (
    <li key={item.id}>{item.name}</li>
    ))}
    </ul>
    </div>
    );
    };

    // 使用 useDeferredValue 延迟处理
    import { useDeferredValue } from 'react';

    const ListComponent = ({ items, filter }) => {
    // 延迟处理 filter,优先更新其他内容
    const deferredFilter = useDeferredValue(filter);

    // 基于延迟的 filter 进行过滤
    const filteredItems = useMemo(() => {
    return items.filter(item =>
    item.name.toLowerCase().includes(deferredFilter.toLowerCase())
    );
    }, [items, deferredFilter]);

    return (
    <ul>
    {filteredItems.map(item => (
    <li key={item.id}>{item.name}</li>
    ))}
    </ul>
    );
    };

    // 使用 Suspense 和 lazy 实现组件懒加载
    import { Suspense, lazy } from 'react';

    const HeavyComponent = lazy(() => import('./HeavyComponent'));

    const App = () => {
    return (
    <Suspense fallback={<div>加载中…</div>}>
    <HeavyComponent />
    </Suspense>
    );
    };

    最佳实践:

    • 合理使用 useTransition 和 useDeferredValue 处理不同优先级的更新
    • 结合 Suspense 和 lazy 实现组件懒加载
    • 注意并发特性对现有代码的影响,特别是依赖于渲染顺序的逻辑
    • 利用优先级调度优化用户交互体验
    • 监控并发模式下的性能指标,如 INP(Interaction to Next Paint)

    2. React.memo、useMemo、useCallback 的正确使用

    2.1 什么是 memoization?

    Memoization 是一种缓存技术,用于存储函数调用的结果,当再次使用相同的参数调用时直接返回缓存的结果。

    2.2 React.memo:组件级缓存

    使用场景:纯展示组件,props 变化不频繁

    错误用法:

    // 错误:传递内联对象作为 props
    const BadExample = () => {
    return (
    <MemoizedComponent
    data={{ name: 'test', value: 123 }}
    onUpdate={() => console.log('updated')}
    />
    );
    };

    正确用法:

    // 正确:使用 useMemo 和 useCallback
    const GoodExample = () => {
    const data = useMemo(() => ({ name: 'test', value: 123 }), []);
    const handleUpdate = useCallback(() => {
    console.log('updated');
    }, []);

    return (
    <MemoizedComponent data={data} onUpdate={handleUpdate} />
    );
    };

    // 定义 memo 组件
    const MemoizedComponent = React.memo(({ data, onUpdate }) => {
    console.log('MemoizedComponent rendered');
    return (
    <div>
    <h1>{data.name}</h1>
    <button onClick={onUpdate}>Update</button>
    </div>
    );
    });

    2.3 useMemo:计算结果缓存

    使用场景:复杂计算、大数据处理

    实战案例:

    // 商品列表筛选和排序
    const ProductList = ({ products, filters, sortBy }) => {
    // 缓存筛选和排序结果
    const filteredAndSortedProducts = useMemo(() => {
    console.log('Computing filtered and sorted products');

    // 复杂的筛选逻辑
    let result = […products];

    if (filters.category) {
    result = result.filter(p => p.category === filters.category);
    }

    if (filters.priceRange) {
    result = result.filter(p =>
    p.price >= filters.priceRange.min &&
    p.price <= filters.priceRange.max
    );
    }

    // 排序
    if (sortBy === 'price') {
    result.sort((a, b) => a.price – b.price);
    } else if (sortBy === 'name') {
    result.sort((a, b) => a.name.localeCompare(b.name));
    }

    return result;
    }, [products, filters, sortBy]); // 依赖项数组

    return (
    <div className="product-list">
    {filteredAndSortedProducts.map(product => (
    <ProductItem key={product.id} product={product} />
    ))}
    </div>
    );
    };

    2.4 useCallback:函数引用缓存

    使用场景:传递给子组件的回调函数

    实战案例:

    const ParentComponent = () => {
    const [count, setCount] = useState(0);
    const [text, setText] = useState('');

    // 缓存回调函数
    const handleClick = useCallback(() => {
    setCount(prev => prev + 1);
    }, []); // 空依赖数组,函数引用永久缓存

    const handleTextChange = useCallback((e) => {
    setText(e.target.value);
    }, []);

    return (
    <div>
    <h1>Count: {count}</h1>
    <input
    type="text"
    value={text}
    onChange={handleTextChange}
    placeholder="Enter text"
    />
    {/* 即使 text 变化,ChildComponent 也不会重渲染 */}
    <ChildComponent onButtonClick={handleClick} />
    </div>
    );
    };

    // 子组件
    const ChildComponent = React.memo(({ onButtonClick }) => {
    console.log('ChildComponent rendered');
    return (
    <button onClick={onButtonClick}>
    Increment Count
    </button>
    );
    });

    3. 虚拟列表:处理大数据渲染

    3.1 为什么需要虚拟列表?

    当渲染成千上万条数据时,React 会创建大量 DOM 节点,导致:

    • 内存占用过高
    • 渲染时间过长
    • 滚动卡顿
    3.2 实现原理

    虚拟列表只渲染可视区域内的元素,通过计算滚动位置来确定需要渲染的元素范围。

    3.3 第三方库使用

    使用 react-window:

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

    const VirtualizedList = ({ items }) => {
    const Row = ({ index, style }) => (
    <div style={style} className="list-item">
    <div className="item-id">{items[index].id}</div>
    <div className="item-name">{items[index].name}</div>
    <div className="item-price">${items[index].price}</div>
    </div>
    );

    return (
    <List
    height={600}
    itemCount={items.length}
    itemSize={50}
    width="100%"
    >
    {Row}
    </List>
    );
    };

    // 使用
    <VirtualizedList items={products} /> {/* 10000 条数据也不卡顿 */}

    3.4 自定义虚拟列表

    适合特殊场景的自定义实现:

    const CustomVirtualList = ({ items, itemHeight, containerHeight }) => {
    const [scrollTop, setScrollTop] = useState(0);
    const containerRef = useRef(null);

    // 计算可见区域的元素范围
    const visibleCount = Math.ceil(containerHeight / itemHeight);
    const startIndex = Math.floor(scrollTop / itemHeight);
    const endIndex = Math.min(startIndex + visibleCount + 1, items.length);

    // 计算偏移量
    const offsetY = startIndex * itemHeight;

    // 只渲染可见区域的元素
    const visibleItems = items.slice(startIndex, endIndex);

    const handleScroll = (e) => {
    setScrollTop(e.target.scrollTop);
    };

    return (
    <div
    ref={containerRef}
    style={{
    height: containerHeight,
    overflow: 'auto',
    position: 'relative',
    border: '1px solid #ccc'
    }}
    onScroll={handleScroll}
    >
    {/* 占位元素,保持滚动条高度 */}
    <div
    style={{
    height: items.length * itemHeight,
    width: '100%',
    position: 'absolute',
    top: 0,
    left: 0
    }}
    />
    {/* 可见元素 */}
    <div
    style={{
    position: 'absolute',
    top: offsetY,
    left: 0,
    width: '100%'
    }}
    >
    {visibleItems.map((item, index) => (
    <div
    key={item.id}
    style={{
    height: itemHeight,
    padding: '10px',
    borderBottom: '1px solid #eee'
    }}
    >
    {item.name}
    </div>
    ))}
    </div>
    </div>
    );
    };

    4. React Server Components:下一代性能优化

    4.1 什么是 React Server Components?

    React Server Components (RSC) 允许组件在服务器端渲染,无需发送到客户端,减少客户端的 JavaScript 体积。

    4.2 适用场景
    • 静态内容组件
    • 数据获取组件
    • 重型计算组件
    4.3 实战案例

    // Server Component: ProductList.server.jsx
    import { db } from './database';

    // 服务器组件,无需客户端 JavaScript
    export default async function ProductList({ category }) {
    // 直接在服务器端查询数据库
    const products = await db.products.find({ category }).toArray();

    return (
    <div className="product-list">
    <h1>{category} 产品</h1>
    <div className="products">
    {products.map(product => (
    <div key={product.id} className="product">
    <h2>{product.name}</h2>
    <p>{product.description}</p>
    <p>${product.price}</p>
    </div>
    ))}
    </div>
    </div>
    );
    }

    // Client Component: ProductPage.jsx
    'use client';

    import { useState } from 'react';
    import ProductList from './ProductList.server';

    export default function ProductPage() {
    const [category, setCategory] = useState('electronics');

    return (
    <div>
    <div className="category-selector">
    <button onClick={() => setCategory('electronics')}>电子产品</button>
    <button onClick={() => setCategory('clothing')}>服装</button>
    <button onClick={() => setCategory('books')}>图书</button>
    </div>

    {/* 服务器组件,每次 category 变化时重新渲染 */}
    <ProductList category={category} />
    </div>
    );
    }

    5. React 性能分析与调试

    5.1 使用 React DevTools Profiler
  • 安装 React DevTools:浏览器扩展
  • 打开 Profiler 标签:记录组件渲染
  • 分析火焰图:识别渲染瓶颈
  • 5.2 自定义性能分析 Hook

    const useRenderCount = (componentName) => {
    const renderCount = useRef(0);

    useEffect(() => {
    renderCount.current += 1;
    console.log(`${componentName} rendered ${renderCount.current} times`);
    });

    return renderCount.current;
    };

    // 使用
    const MyComponent = () => {
    const renderCount = useRenderCount('MyComponent');

    return (
    <div>
    <h1>My Component</h1>
    <p>Render count: {renderCount}</p>
    </div>
    );
    };

    5.3 重渲染排查

    常见原因:

    • Props 引用变化
    • 状态更新
    • 父组件重渲染
    • Context 变化

    排查工具:

    const WhyDidYouRender = require('@welldone-software/why-did-you-render');

    // 启用监控
    WhyDidYouRender(React, {
    trackAllPureComponents: true,
    trackHooks: true,
    logOnDifferentValues: true,
    });

    // 组件会在控制台显示重渲染原因

    二、Vue 框架级性能优化

    1. Vue 3 响应式系统深度解析

    1.1 Vue 3 响应式原理

    Vue 3 使用 Proxy 实现响应式,相比 Vue 2 的 Object.defineProperty 有以下优势:

    • 全面的响应式支持:支持数组索引、新增属性、Map、Set 等
    • 性能提升:避免了 Vue 2 中数组方法的重写和对象属性的遍历
    • 代码简洁:使用 Reflect 统一操作,减少了代码复杂度
    • 懒代理:只在访问属性时才进行代理,提高初始化性能

    Vue 3 响应式系统架构:

  • Reactive:创建响应式对象
  • Ref:创建响应式引用
  • Computed:创建计算属性
  • Effect:副作用函数
  • Track:依赖收集
  • Trigger:触发更新
  • 1.2 源码级理解与性能优化

    // Vue 3 响应式核心
    function reactive(target) {
    return createReactiveObject(target, false, mutableHandlers);
    }

    // 浅层响应式
    function shallowReactive(target) {
    return createReactiveObject(target, false, shallowMutableHandlers);
    }

    // 只读响应式
    function readonly(target) {
    return createReactiveObject(target, true, readonlyHandlers);
    }

    // 浅层只读
    function shallowReadonly(target) {
    return createReactiveObject(target, true, shallowReadonlyHandlers);
    }

    const mutableHandlers = {
    get(target, key, receiver) {
    // 依赖收集
    track(target, TrackOpTypes.GET, key);
    const result = Reflect.get(target, key, receiver);
    // 深度响应(懒代理)
    if (isObject(result)) {
    return reactive(result);
    }
    return result;
    },
    set(target, key, value, receiver) {
    const oldValue = target[key];
    const result = Reflect.set(target, key, value, receiver);
    // 触发更新
    if (oldValue !== value) {
    trigger(target, TriggerOpTypes.SET, key, value, oldValue);
    }
    return result;
    },
    // 其他操作:deleteProperty, has, ownKeys 等
    };

    // 依赖收集
    function track(target, type, key) {
    const effect = activeEffect;
    if (effect) {
    let depsMap = targetMap.get(target);
    if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
    }
    let dep = depsMap.get(key);
    if (!dep) {
    depsMap.set(key, (dep = createDep()));
    }
    // 将当前 effect 添加到依赖中
    trackEffects(dep, effect);
    }
    }

    // 触发更新
    function trigger(target, type, key, newValue, oldValue) {
    const depsMap = targetMap.get(target);
    if (!depsMap) return;

    const effects = new Set();
    // 收集需要执行的 effect
    if (key !== undefined) {
    const dep = depsMap.get(key);
    if (dep) {
    addEffects(effects, dep);
    }
    }

    // 执行 effect
    effects.forEach(effect => {
    if (effect !== activeEffect) {
    triggerEffect(effect);
    }
    });
    }

    1.3 Vue 3 编译时优化

    Vue 3 引入了编译时优化,通过静态分析减少运行时开销:

    编译时优化特性:

  • 静态提升:

    • 静态节点和属性被提升到渲染函数外部
    • 避免每次渲染时重新创建
  • 补丁标志:

    • 为动态节点添加补丁标志
    • 运行时只更新有变化的部分
  • 缓存事件处理函数:

    • 自动缓存内联事件处理函数
    • 减少不必要的重渲染
  • 树摇优化:

    • 只打包使用的特性
    • 减小包体积
  • 编译时优化示例:

    <template>
    <div>
    <!– 静态节点:被提升 –>
    <h1>静态标题</h1>

    <!– 动态节点:添加补丁标志 –>
    <p :class="className">动态内容:{{ message }}</p>

    <!– 事件处理:自动缓存 –>
    <button @click="handleClick">点击按钮</button>
    </div>
    </template>

    <script setup>
    import { ref } from 'vue';

    const message = ref('Hello');
    const className = ref('text-red');

    const handleClick = () => {
    console.log('Button clicked');
    };
    </script>

    <!– 编译后的渲染函数 –>
    function render() {
    // 静态节点提升
    const _hoisted_1 = /*#__PURE__*/ createElementVNode("h1", null, "静态标题", -1 /* HOISTED */);

    return (
    createElementVNode("div", null, [
    _hoisted_1,
    createElementVNode("p",
    // 补丁标志:只更新 class 和 text
    { class: _ctx.className },
    "动态内容:" + _toDisplayString(_ctx.message),
    2 /* CLASS, TEXT */
    ),
    createElementVNode("button",
    // 事件处理函数缓存
    { onClick: _ctx.handleClick },
    "点击按钮",
    8 /* PROPS */,
    ["onClick"]
    )
    ])
    );
    }

    1.4 Vue 3 性能优化实战

    响应式系统优化:

    <template>
    <div>
    <h1>大型数据优化</h1>
    <div v-for="item in largeList" :key="item.id">
    {{ item.name }}
    </div>
    </div>
    </template>

    <script setup>
    import { shallowRef, onMounted } from 'vue';

    // 使用 shallowRef 处理大型数据,减少依赖收集
    const largeList = shallowRef([]);

    onMounted(() => {
    // 模拟加载大型数据
    largeList.value = Array.from({ length: 10000 }, (_, index) => ({
    id: index,
    name: `Item ${index}`,
    // 大量其他属性
    data: { /* 复杂数据 */ }
    }));
    });
    </script>

    <template>
    <div>
    <h1>计算属性优化</h1>
    <div>{{ expensiveValue }}</div>
    </div>
    </template>

    <script setup>
    import { computed, ref } from 'vue';

    const count = ref(0);

    // 计算属性会缓存结果
    const expensiveValue = computed(() => {
    console.log('Computing expensive value');
    // 模拟复杂计算
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
    result += i;
    }
    return result;
    });
    </script>

    编译时优化最佳实践:

  • 合理使用静态节点:将不变的内容提取为静态节点
  • 避免复杂的内联表达式:将复杂计算移到 computed 中
  • 使用 v-on:click 而非 @click:虽然功能相同,但编译时处理方式一致
  • 合理使用 key:帮助 Vue 识别节点,提高 diff 性能
  • Vue 3 性能监控:

    <template>
    <div>
    <h1>性能监控示例</h1>
    <button @click="increment">Increment</button>
    <div>Count: {{ count }}</div>
    </div>
    </template>

    <script setup>
    import { ref, onRenderTracked, onRenderTriggered } from 'vue';

    const count = ref(0);

    const increment = () => {
    count.value++;
    };

    // 跟踪依赖收集
    onRenderTracked((event) => {
    console.log('依赖收集:', {
    effect: event.effect,
    target: event.target,
    key: event.key,
    type: event.type
    });
    });

    // 跟踪更新触发
    onRenderTriggered((event) => {
    console.log('更新触发:', {
    effect: event.effect,
    target: event.target,
    key: event.key,
    type: event.type,
    newValue: event.newValue,
    oldValue: event.oldValue
    });
    });
    </script>

    2. computed 与 watch 的优化

    2.1 computed 的缓存机制

    computed 优势:

    • 缓存计算结果
    • 只有依赖变化时才重新计算
    • 惰性求值

    使用场景:

    <template>
    <div>
    <h1>购物车</h1>
    <div v-for="item in cart" :key="item.id">
    {{ item.name }} – ¥{{ item.price }} x {{ item.quantity }}
    </div>
    <div class="total">
    总计: ¥{{ totalPrice }}
    </div>
    </div>
    </template>

    <script setup>
    import { ref, computed } from 'vue';

    const cart = ref([
    { id: 1, name: '商品1', price: 100, quantity: 1 },
    { id: 2, name: '商品2', price: 200, quantity: 2 }
    ]);

    // 使用 computed 缓存计算结果
    const totalPrice = computed(() => {
    console.log('计算总价格');
    return cart.value.reduce((total, item) => {
    return total + item.price * item.quantity;
    }, 0);
    });

    // 点击按钮不会重新计算 totalPrice
    const handleClick = () => {
    console.log('按钮点击');
    };
    </script>

    2.2 watch 的优化策略

    优化点:

    • 使用 immediate 控制是否立即执行
    • 使用 deep 控制是否深度监听
    • 使用 flush 控制执行时机
    • 使用回调函数接收新旧值

    实战案例:

    <template>
    <div>
    <input v-model="user.name" placeholder="姓名">
    <input v-model="user.age" placeholder="年龄">
    <input v-model="user.address.city" placeholder="城市">
    </div>
    </template>

    <script setup>
    import { ref, watch } from 'vue';

    const user = ref({
    name: '',
    age: '',
    address: {
    city: '',
    street: ''
    }
    });

    // 1. 监听单个属性
    watch(
    () => user.value.name,
    (newName, oldName) => {
    console.log(`姓名从 ${oldName} 变为 ${newName}`);
    }
    );

    // 2. 深度监听对象
    watch(
    user,
    (newUser, oldUser) => {
    console.log('用户信息变化', newUser);
    },
    { deep: true }
    );

    // 3. 监听对象的特定嵌套属性
    watch(
    () => user.value.address.city,
    (newCity) => {
    console.log(`城市变为 ${newCity}`);
    }
    );

    // 4. 立即执行
    watch(
    () => user.value.age,
    (age) => {
    console.log(`年龄: ${age}`);
    },
    { immediate: true }
    );
    </script>

    3. keep-alive 的高级用法

    3.1 什么是 keep-alive?

    keep-alive 是 Vue 的内置组件,用于缓存组件实例,避免重复创建和销毁。

    3.2 基本用法

    <template>
    <div>
    <button @click="currentComponent = 'ComponentA'">组件 A</button>
    <button @click="currentComponent = 'ComponentB'">组件 B</button>

    <keep-alive>
    <component :is="currentComponent" />
    </keep-alive>
    </div>
    </template>

    <script setup>
    import { ref } from 'vue';
    import ComponentA from './ComponentA.vue';
    import ComponentB from './ComponentB.vue';

    const currentComponent = ref('ComponentA');
    </script>

    3.3 高级配置

    包含和排除:

    <keep-alive :include="['ComponentA', 'ComponentB']" :exclude="['ComponentC']">
    <component :is="currentComponent" />
    </keep-alive>

    最大缓存数量:

    <keep-alive :max="10">
    <router-view v-slot="{ Component }">
    <component :is="Component" />
    </router-view>
    </keep-alive>

    3.4 生命周期钩子

    <script setup>
    import { onMounted, onUnmounted, onActivated, onDeactivated } from 'vue';

    onMounted(() => {
    console.log('组件挂载');
    });

    onUnmounted(() => {
    console.log('组件卸载');
    });

    onActivated(() => {
    console.log('组件激活(从缓存中恢复)');
    // 可以在这里执行需要每次激活时执行的逻辑
    startTimer();
    });

    onDeactivated(() => {
    console.log('组件失活(被缓存)');
    // 可以在这里执行需要每次失活时执行的逻辑
    stopTimer();
    });

    const startTimer = () => {
    console.log('开始计时');
    };

    const stopTimer = () => {
    console.log('停止计时');
    };
    </script>

    4. Vue 虚拟列表实现

    4.1 使用第三方库

    使用 vue-virtual-scroller:

    <template>
    <RecycleScroller
    class="scroller"
    :items="items"
    :item-size="54"
    key-field="id"
    v-slot="{ item }"
    >
    <div class="item">
    <img :src="item.avatar" alt="">
    <div class="info">
    <div class="name">{{ item.name }}</div>
    <div class="message">{{ item.message }}</div>
    </div>
    </div>
    </RecycleScroller>
    </template>

    <script setup>
    import { ref } from 'vue';
    import { RecycleScroller } from 'vue-virtual-scroller';
    import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';

    // 生成 10000 条数据
    const items = ref(
    Array.from({ length: 10000 }, (_, index) => ({
    id: index,
    name: `用户${index}`,
    message: `这是一条消息 ${index}`,
    avatar: `https://randomuser.me/api/portraits/men/${index % 100}.jpg`
    }))
    );
    </script>

    <style scoped>
    .scroller {
    height: 600px;
    width: 100%;
    }

    .item {
    height: 50px;
    border-bottom: 1px solid #eee;
    display: flex;
    align-items: center;
    padding: 0 10px;
    }

    .avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    margin-right: 10px;
    }

    .info {
    flex: 1;
    }

    .name {
    font-weight: bold;
    margin-bottom: 4px;
    }

    .message {
    font-size: 12px;
    color: #666;
    }
    </style>

    4.2 自定义虚拟列表

    适合 Vue 3 Composition API 的实现:

    <template>
    <div
    ref="containerRef"
    class="virtual-list"
    @scroll="handleScroll"
    >
    <div
    class="virtual-list__placeholder"
    :style="{ height: totalHeight + 'px' }"
    ></div>
    <div
    class="virtual-list__content"
    :style="{ transform: `translateY(${offsetTop}px)` }"
    >
    <div
    v-for="item in visibleItems"
    :key="item.id"
    class="virtual-list__item"
    :style="{ height: itemHeight + 'px' }"
    >
    {{ item.name }}
    </div>
    </div>
    </div>
    </template>

    <script setup>
    import { ref, computed, onMounted } from 'vue';

    const props = defineProps({
    items: {
    type: Array,
    default: () => []
    },
    itemHeight: {
    type: Number,
    default: 50
    },
    containerHeight: {
    type: Number,
    default: 500
    }
    });

    const containerRef = ref(null);
    const scrollTop = ref(0);

    // 计算总高度
    const totalHeight = computed(() => {
    return props.items.length * props.itemHeight;
    });

    // 计算可见区域的元素范围
    const startIndex = computed(() => {
    return Math.floor(scrollTop.value / props.itemHeight);
    });

    const endIndex = computed(() => {
    const visibleCount = Math.ceil(props.containerHeight / props.itemHeight);
    return Math.min(startIndex.value + visibleCount + 1, props.items.length);
    });

    // 计算偏移量
    const offsetTop = computed(() => {
    return startIndex.value * props.itemHeight;
    });

    // 计算可见元素
    const visibleItems = computed(() => {
    return props.items.slice(startIndex.value, endIndex.value);
    });

    const handleScroll = (e) => {
    scrollTop.value = e.target.scrollTop;
    };

    onMounted(() => {
    if (containerRef.value) {
    containerRef.value.style.height = props.containerHeight + 'px';
    }
    });
    </script>

    <style scoped>
    .virtual-list {
    position: relative;
    overflow: auto;
    border: 1px solid #ccc;
    }

    .virtual-list__placeholder {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1;
    }

    .virtual-list__content {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 2;
    }

    .virtual-list__item {
    padding: 10px;
    border-bottom: 1px solid #eee;
    box-sizing: border-box;
    }
    </style>

    5. Vue 性能分析与调试

    5.1 使用 Vue DevTools

    性能面板:

    • 记录组件渲染时间
    • 分析响应式依赖
    • 查看组件更新原因
    5.2 自定义性能监控

    <script setup>
    import { onRenderTracked, onRenderTriggered } from 'vue';

    // 跟踪依赖收集
    onRenderTracked((event) => {
    console.log('依赖收集:', {
    effect: event.effect,
    target: event.target,
    key: event.key,
    type: event.type
    });
    });

    // 跟踪更新触发
    onRenderTriggered((event) => {
    console.log('更新触发:', {
    effect: event.effect,
    target: event.target,
    key: event.key,
    type: event.type,
    newValue: event.newValue,
    oldValue: event.oldValue
    });
    });
    </script>

    5.3 组件渲染次数监控

    <script setup>
    import { ref, onRendered } from 'vue';

    const renderCount = ref(0);

    onRendered(() => {
    renderCount.value++;
    console.log(`组件渲染次数: ${renderCount.value}`);
    });
    </script>

    三、双框架通用性能优化

    1. 重渲染排查与优化

    1.1 React 重渲染排查

    常见原因:

    • 父组件重渲染导致子组件重渲染
    • 内联对象/函数作为 props
    • 状态管理库导致的不必要更新
    • Context 变化

    解决方案:

    • 使用 React.memo
    • 使用 useMemo/useCallback
    • 合理使用状态管理
    • 拆分 Context
    1.2 Vue 重渲染排查

    常见原因:

    • 响应式数据频繁变化
    • 计算属性依赖过多
    • 监听器触发过多
    • 组件嵌套过深

    解决方案:

    • 使用 shallowRef/shallowReactive
    • 合理设计计算属性
    • 优化监听器
    • 组件拆分

    2. 大数据渲染优化

    2.1 分页加载

    React 实现:

    const PaginatedList = ({ items, pageSize = 20 }) => {
    const [currentPage, setCurrentPage] = useState(1);
    const [filteredItems, setFilteredItems] = useState(items);

    // 计算分页数据
    const totalPages = Math.ceil(filteredItems.length / pageSize);
    const currentItems = useMemo(() => {
    const start = (currentPage – 1) * pageSize;
    const end = start + pageSize;
    return filteredItems.slice(start, end);
    }, [filteredItems, currentPage, pageSize]);

    return (
    <div>
    <div className="list">
    {currentItems.map(item => (
    <div key={item.id} className="item">
    {item.name}
    </div>
    ))}
    </div>
    <div className="pagination">
    <button
    onClick={() => setCurrentPage(prev => Math.max(prev – 1, 1))}
    disabled={currentPage === 1}
    >
    上一页
    </button>
    <span>{currentPage} / {totalPages}</span>
    <button
    onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
    disabled={currentPage === totalPages}
    >
    下一页
    </button>
    </div>
    </div>
    );
    };

    Vue 实现:

    <template>
    <div>
    <div class="list">
    <div v-for="item in currentItems" :key="item.id" class="item">
    {{ item.name }}
    </div>
    </div>
    <div class="pagination">
    <button
    @click="currentPage–"
    :disabled="currentPage === 1"
    >
    上一页
    </button>
    <span>{{ currentPage }} / {{ totalPages }}</span>
    <button
    @click="currentPage++"
    :disabled="currentPage === totalPages"
    >
    下一页
    </button>
    </div>
    </div>
    </template>

    <script setup>
    import { ref, computed } from 'vue';

    const props = defineProps({
    items: {
    type: Array,
    default: () => []
    },
    pageSize: {
    type: Number,
    default: 20
    }
    });

    const currentPage = ref(1);

    // 计算总页数
    const totalPages = computed(() => {
    return Math.ceil(props.items.length / props.pageSize);
    });

    // 计算当前页数据
    const currentItems = computed(() => {
    const start = (currentPage.value – 1) * props.pageSize;
    const end = start + props.pageSize;
    return props.items.slice(start, end);
    });
    </script>

    2.2 虚拟滚动

    已在框架部分详细介绍

    2.3 批量处理

    React 实现:

    const BatchUpdateExample = () => {
    const [items, setItems] = useState([]);

    const handleBatchAdd = () => {
    // 批量添加 1000 条数据
    setItems(prev => {
    const newItems = […prev];
    // 批量生成数据
    for (let i = 0; i < 1000; i++) {
    newItems.push({ id: Date.now() + i, name: `Item ${Date.now() + i}` });
    }
    return newItems;
    });
    };

    return (
    <div>
    <button onClick={handleBatchAdd}>批量添加 1000 条数据</button>
    <div>{items.length} 条数据</div>
    {/* 虚拟列表渲染 */}
    </div>
    );
    };

    Vue 实现:

    <template>
    <div>
    <button @click="handleBatchAdd">批量添加 1000 条数据</button>
    <div>{{ items.length }} 条数据</div>
    <!– 虚拟列表渲染 –>
    </div>
    </template>

    <script setup>
    import { ref } from 'vue';

    const items = ref([]);

    const handleBatchAdd = () => {
    // 批量添加 1000 条数据
    const newItems = […items.value];
    for (let i = 0; i < 1000; i++) {
    newItems.push({ id: Date.now() + i, name: `Item ${Date.now() + i}` });
    }
    items.value = newItems;
    };
    </script>

    3. 内存泄漏防护

    3.1 React 内存泄漏

    常见原因:

    • 未清理的定时器
    • 未取消的网络请求
    • 未清理的事件监听器
    • 未清理的订阅

    解决方案:

    const MemorySafeComponent = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
    // 设置定时器
    const timer = setInterval(() => {
    setCount(prev => prev + 1);
    }, 1000);

    // 添加事件监听器
    const handleResize = () => {
    console.log('Window resized');
    };
    window.addEventListener('resize', handleResize);

    // 清理函数
    return () => {
    clearInterval(timer);
    window.removeEventListener('resize', handleResize);
    };
    }, []);

    return <div>Count: {count}</div>;
    };

    3.2 Vue 内存泄漏

    常见原因:

    • 未清理的定时器
    • 未取消的网络请求
    • 未清理的事件监听器
    • 循环引用

    解决方案:

    <template>
    <div>{{ count }}</div>
    </template>

    <script setup>
    import { ref, onMounted, onUnmounted } from 'vue';

    const count = ref(0);
    let timer = null;

    onMounted(() => {
    // 设置定时器
    timer = setInterval(() => {
    count.value++;
    }, 1000);

    // 添加事件监听器
    window.addEventListener('resize', handleResize);
    });

    onUnmounted(() => {
    // 清理定时器
    if (timer) {
    clearInterval(timer);
    }
    // 清理事件监听器
    window.removeEventListener('resize', handleResize);
    });

    const handleResize = () => {
    console.log('Window resized');
    };
    </script>

    4. 网络请求优化

    4.1 缓存策略

    React 实现:

    const DataComponent = ({ userId }) => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    // 缓存
    const cacheRef = useRef({});

    useEffect(() => {
    const fetchData = async () => {
    // 检查缓存
    if (cacheRef.current[userId]) {
    setData(cacheRef.current[userId]);
    setLoading(false);
    return;
    }

    try {
    setLoading(true);
    const response = await fetch(`/api/user/${userId}`);
    const result = await response.json();

    // 存入缓存
    cacheRef.current[userId] = result;
    setData(result);
    } catch (error) {
    console.error('Error fetching data:', error);
    } finally {
    setLoading(false);
    }
    };

    fetchData();
    }, [userId]);

    if (loading) return <div>Loading…</div>;
    return <div>{data.name}</div>;
    };

    Vue 实现:

    <template>
    <div>
    <div v-if="loading">Loading…</div>
    <div v-else>{{ data.name }}</div>
    </div>
    </template>

    <script setup>
    import { ref, watch, onMounted } from 'vue';

    const props = defineProps({
    userId: {
    type: String,
    required: true
    }
    });

    const data = ref(null);
    const loading = ref(true);

    // 缓存
    const cache = ref({});

    const fetchData = async () => {
    // 检查缓存
    if (cache.value[props.userId]) {
    data.value = cache.value[props.userId];
    loading.value = false;
    return;
    }

    try {
    loading.value = true;
    const response = await fetch(`/api/user/${props.userId}`);
    const result = await response.json();

    // 存入缓存
    cache.value[props.userId] = result;
    data.value = result;
    } catch (error) {
    console.error('Error fetching data:', error);
    } finally {
    loading.value = false;
    }
    };

    onMounted(() => {
    fetchData();
    });

    watch(() => props.userId, () => {
    fetchData();
    });
    </script>

    4.2 防抖与节流

    通用实现:

    // 防抖
    function debounce(func, wait) {
    let timeout;
    return function executedFunction(args) {
    const later = () => {
    clearTimeout(timeout);
    func(args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    };
    }

    // 节流
    function throttle(func, limit) {
    let inThrottle;
    return function executedFunction(args) {
    if (!inThrottle) {
    func.apply(this, args);
    inThrottle = true;
    setTimeout(() => inThrottle = false, limit);
    }
    };
    }

    // 使用
    const debouncedSearch = debounce((query) => {
    fetch(`/api/search?q=${query}`)
    .then(response => response.json())
    .then(data => {
    console.log(data);
    });
    }, 300);

    const throttledScroll = throttle(() => {
    console.log('Scrolled');
    }, 100);

    四、真实案例分析

    1. React 电商商品列表优化

    问题描述

    一个电商应用的商品列表页,滚动时卡顿严重,每次滚动都会触发大量组件的不必要渲染。

    根因分析
  • 组件结构问题:每个商品项都是独立组件,父组件状态变化导致所有子组件重渲染
  • Props 传递问题:传递了内联函数和对象作为 props
  • 状态管理问题:商品数据存储在父组件状态中,任何变化都会触发重渲染
  • 解决方案
  • 使用 React.memo:缓存商品项组件
  • 使用 useMemo/useCallback:缓存计算结果和回调函数
  • 虚拟滚动:只渲染可视区域的商品
  • 状态管理优化:使用更高效的状态管理方案
  • 代码实现

    // 优化后的商品列表
    const OptimizedProductList = ({ products, onAddToCart }) => {
    const [sortBy, setSortBy] = useState('price');

    // 缓存排序函数
    const sortedProducts = useMemo(() => {
    return […products].sort((a, b) => {
    if (sortBy === 'price') {
    return a.price – b.price;
    } else if (sortBy === 'name') {
    return a.name.localeCompare(b.name);
    }
    return 0;
    });
    }, [products, sortBy]);

    // 缓存添加到购物车的回调
    const handleAddToCart = useCallback((productId) => {
    onAddToCart(productId);
    }, [onAddToCart]);

    return (
    <div>
    <div className="sort-controls">
    <button onClick={() => setSortBy('price')}>按价格排序</button>
    <button onClick={() => setSortBy('name')}>按名称排序</button>
    </div>

    {/* 使用虚拟滚动 */}
    <List
    height={600}
    itemCount={sortedProducts.length}
    itemSize={200}
    width="100%"
    >
    {({ index, style }) => (
    <ProductItem
    key={sortedProducts[index].id}
    product={sortedProducts[index]}
    onAddToCart={handleAddToCart}
    style={style}
    />
    )}
    </List>
    </div>
    );
    };

    // 缓存商品项组件
    const ProductItem = React.memo(({ product, onAddToCart, style }) => {
    return (
    <div style={style} className="product-item">
    <img src={product.image} alt={product.name} />
    <h3>{product.name}</h3>
    <p>${product.price}</p>
    <button onClick={() => onAddToCart(product.id)}>
    添加到购物车
    </button>
    </div>
    );
    });

    2. Vue 企业管理系统表格优化

    问题描述

    一个 Vue 企业管理系统,大数据表格渲染时页面直接崩溃,表格包含 1000+ 行数据,每行有多个计算字段。

    根因分析
  • 响应式数据问题:所有数据都是响应式的,导致大量依赖收集
  • 计算字段问题:每行都有多个计算字段,导致大量计算
  • DOM 节点问题:渲染了 1000+ DOM 节点
  • 事件监听问题:每行都有多个事件监听器
  • 解决方案
  • 虚拟滚动:只渲染可视区域的行
  • 非响应式数据:使用 shallowRef 减少依赖收集
  • 计算优化:使用缓存和批量计算
  • 事件委托:减少事件监听器数量
  • 代码实现

    <template>
    <div class="data-table">
    <div class="table-header">
    <div class="header-cell">ID</div>
    <div class="header-cell">姓名</div>
    <div class="header-cell">部门</div>
    <div class="header-cell">工资</div>
    <div class="header-cell">操作</div>
    </div>

    <!– 虚拟滚动表格 –>
    <RecycleScroller
    class="table-body"
    :items="employees"
    :item-size="50"
    key-field="id"
    v-slot="{ item }"
    >
    <div class="table-row">
    <div class="table-cell">{{ item.id }}</div>
    <div class="table-cell">{{ item.name }}</div>
    <div class="table-cell">{{ item.department }}</div>
    <div class="table-cell">{{ formatSalary(item.salary) }}</div>
    <div class="table-cell">
    <button @click="editEmployee(item.id)">编辑</button>
    <button @click="deleteEmployee(item.id)">删除</button>
    </div>
    </div>
    </RecycleScroller>
    </div>
    </template>

    <script setup>
    import { shallowRef, computed } from 'vue';
    import { RecycleScroller } from 'vue-virtual-scroller';

    // 使用 shallowRef 减少依赖收集
    const employees = shallowRef([]);

    // 模拟加载数据
    const loadEmployees = async () => {
    const response = await fetch('/api/employees');
    const data = await response.json();
    employees.value = data;
    };

    // 格式化工资(使用计算缓存)
    const salaryCache = new Map();
    const formatSalary = (salary) => {
    if (salaryCache.has(salary)) {
    return salaryCache.get(salary);
    }

    const formatted = new Intl.NumberFormat('zh-CN', {
    style: 'currency',
    currency: 'CNY'
    }).format(salary);

    salaryCache.set(salary, formatted);
    return formatted;
    };

    const editEmployee = (id) => {
    console.log('编辑员工:', id);
    };

    const deleteEmployee = (id) => {
    console.log('删除员工:', id);
    };

    // 初始加载数据
    loadEmployees();
    </script>

    <style scoped>
    .data-table {
    border: 1px solid #ccc;
    width: 100%;
    }

    .table-header {
    display: flex;
    background-color: #f0f0f0;
    font-weight: bold;
    }

    .header-cell {
    flex: 1;
    padding: 10px;
    border-right: 1px solid #ccc;
    }

    .table-body {
    height: 600px;
    }

    .table-row {
    display: flex;
    border-bottom: 1px solid #eee;
    }

    .table-cell {
    flex: 1;
    padding: 10px;
    border-right: 1px solid #eee;
    }

    button {
    margin: 0 5px;
    }
    </style>

    五、框架级性能优化最佳实践

    1. React 最佳实践

  • 组件设计:

    • 拆分组件为纯展示和逻辑组件
    • 使用 React.memo 缓存纯展示组件
    • 合理使用 useMemo 和 useCallback
  • 状态管理:

    • 状态尽可能靠近使用它的组件
    • 使用 Context API 时拆分多个 Context
    • 考虑使用更轻量的状态管理方案
  • 渲染优化:

    • 避免内联对象和函数作为 props
    • 使用虚拟滚动处理大数据
    • 考虑使用 React Server Components
  • 性能监控:

    • 使用 React DevTools Profiler
    • 集成 @welldone-software/why-did-you-render
    • 建立性能预算
  • 2. Vue 最佳实践

  • 组件设计:

    • 合理拆分组件
    • 使用 keep-alive 缓存频繁切换的组件
    • 利用 Vue 3 的 Tree Shaking
  • 响应式系统:

    • 合理使用 ref 和 reactive
    • 对于大型数据使用 shallowRef/shallowReactive
    • 避免不必要的深度响应
  • 计算与监听:

    • 优先使用 computed 而非 watch
    • 合理设置 watch 的 deep 和 flush
    • 避免在模板中进行复杂计算
  • 性能监控:

    • 使用 Vue DevTools 性能面板
    • 集成 Vue Performance DevTools
    • 建立性能基准
  • 3. 通用最佳实践

  • 代码组织:

    • 组件拆分合理
    • 逻辑与 UI 分离
    • 代码可读性和可维护性
  • 数据处理:

    • 批量处理数据
    • 合理使用缓存
    • 避免不必要的数据转换
  • 网络请求:

    • 防抖和节流
    • 请求缓存
    • 批量请求
  • 内存管理:

    • 清理定时器和事件监听器
    • 避免循环引用
    • 合理使用 WeakMap 和 WeakSet
  • 构建优化:

    • Tree Shaking
    • 代码分割
    • 按需加载
  • 六、总结与展望

    1. 框架级性能优化的价值

    • 用户体验提升:减少卡顿,提高响应速度
    • 开发效率提升:减少调试时间,提高代码质量
    • 业务价值提升:提高用户留存,增加转化率
    • 技术债务减少:避免性能问题积累

    2. 未来趋势

  • Server Components:React 和 Vue 都在探索服务端组件
  • 编译时优化:更多优化在编译时完成
  • 智能优化:AI 辅助性能优化
  • WebAssembly:使用 Wasm 处理计算密集型任务
  • 边缘计算:将计算下沉到边缘节点
  • 3. 结语

    框架级性能优化是前端开发的高级技能,需要深入理解框架原理,结合业务场景,采用合适的优化策略。

    记住:

    • 性能优化是一个持续的过程,不是一次性任务
    • 优化应该基于数据,而不是猜测
    • 不同场景需要不同的优化策略
    • 团队协作和规范比个人英雄主义更重要

    希望本文分享的框架级性能优化技巧能够帮助你解决实际开发中的性能问题,构建更快、更稳定的前端应用。


    作者: 十六咲子
    发布时间: 2026-02-04
    更新时间: 2026-02-04

    本文为原创实战经验分享,转载请注明出处。

    后续计划:

    • 发布框架级性能优化工具包
    • 分享更多真实项目的性能优化案例
    • 开设框架级性能优化实战课程

    期待与你一起,构建更快、更智能的前端应用!

    赞(0)
    未经允许不得转载:171主机测评 » 2026年 React/Vue 框架级性能优化实战:从源码原理到业务落地
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址