刷刷题49(react中几个常见的性能优化问题)

一、React Server Components中如何防止客户端组件暴露敏感信息?

  1. 通过构建工具隔离环境变量(Webpack/Vite/Rspack)
  2. 使用.server.js后缀强制服务端组件标识
  3. 构建时静态分析依赖关系树

Webpack配置方案‌:

arduino 复制代码
// next.config.js
module.exports = {
  webpack(config) {
    config.plugins.push(new webpack.DefinePlugin({
      'process.env.API_KEY': JSON.stringify(process.env.SERVER_SIDE_API_KEY)
    }));
    config.externals = [...config.externals, 'aws-sdk']; // 排除客户端不需要的模块
    return config;
  }
}

Vite配置方案‌:

php 复制代码
// vite.config.js
export default defineConfig({
  define: {
    'import.meta.env.SERVER_KEY': JSON.stringify(process.env.SERVER_KEY)
  },
  build: {
    rollupOptions: {
      external: ['node-fetch'] // 排除客户端打包
    }
  }
})

关键差异‌:

  • Webpack通过externals字段显式排除,Vite使用Rollup的external配置
  • 环境变量注入方式不同,Vite使用import.meta.env,Webpack使用process.env
  • 服务端组件检测机制不同,Next.js通过文件约定,Vite需手动配置插件

二、使用useTransition和Suspense实现平滑页面过渡

实现方案‌:

ini 复制代码
function App() {
  const [tab, setTab] = useState('home');
  const [isPending, startTransition] = useTransition();
  const router = useRouter();

  const handleNavigation = (path) => {
    startTransition(() => {
      router.push(path);
    });
  };

  return (
    <div className={isPending ? 'navigation-pending' : ''}>
      <nav>
        <button onClick={() => handleNavigation('/')}>Home</button>
        <button onClick={() => handleNavigation('/dashboard')}>Dashboard</button>
      </nav>
      
      <Suspense fallback={<PageSkeleton />}>
        <main className={`page-transition ${isPending ? 'fade-out' : 'fade-in'}`}>
          <Outlet /> {/* React Router的出口组件 */}
        </main>
      </Suspense>
    </div>
  );
}

优化技巧‌:

  1. 动画协调‌:通过CSS变量控制过渡时长
css 复制代码
:root {
  --transition-duration: 300ms;
}

.page-transition {
  transition: opacity var(--transition-duration) ease;
}

.fade-out {
  opacity: 0.5;
  pointer-events: none;
}

.fade-in {
  opacity: 1;
}
  1. 竞态处理‌:使用AbortController取消旧请求
ini 复制代码
const abortControllerRef = useRef();

const loadData = async (url) => {
  abortControllerRef.current?.abort();
  abortControllerRef.current = new AbortController();
  
  const res = await fetch(url, { 
    signal: abortControllerRef.current.signal 
  });
  // ...处理数据
}

三、大型应用Context性能优化方案

分层解决方案‌:

javascript 复制代码
// 一级Context:用户认证信息(高频变更)
const AuthContext = createContext();
export const useAuth = () => useContext(AuthContext);

// 二级Context:UI主题配置(低频变更)  
const ThemeContext = createContext();
export const useTheme = () => useContext(ThemeContext);

// 三级Context:本地化配置(极少变更)
const LocaleContext = createContext();
export const useLocale = () => useContext(LocaleContext);

function AppProvider({ children }) {
  return (
    <AuthProvider>
      <ThemeProvider>
        <LocaleProvider>
          {children}
        </LocaleProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

优化策略对比‌:

方案 适用场景 实现复杂度 性能增益
Context分层 不同类型状态隔离 40%-60%
use-context-selector 精确订阅特定状态字段 70%-80%
Zustand 复杂跨组件状态管理 90%+

代码示例(使用use-context-selector) ‌:

javascript 复制代码
import { createContext, useContextSelector } from 'use-context-selector';

const UserContext = createContext();

function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  const value = useMemo(() => ({ user, setUser }), [user]);
  
  return (
    <UserContext.Provider value={value}>
      {children}
    </UserContext.Provider>
  );
}

// 组件精确订阅
function Profile() {
  const username = useContextSelector(UserContext, 
    (ctx) => ctx.user?.name
  );
  return <div>{username}</div>;
}

四、支持撤销/重做的自定义Hook

核心实现‌:

ini 复制代码
import { useCallback, useRef } from 'react';

type HistoryAction<T> = {
  past: T[];
  present: T;
  future: T[];
};

export function useHistory<T>(initialState: T) {
  const history = useRef<HistoryAction<T>>({
    past: [],
    present: initialState,
    future: []
  });

  const canUndo = useCallback(() => history.current.past.length > 0, []);
  const canRedo = useCallback(() => history.current.future.length > 0, []);

  const undo = useCallback(() => {
    if (!canUndo()) return;
    
    const { past, present, future } = history.current;
    const newPresent = past[past.length - 1];
    const newPast = past.slice(0, -1);
    
    history.current = {
      past: newPast,
      present: newPresent,
      future: [present, ...future]
    };
    
    return newPresent;
  }, [canUndo]);

  const redo = useCallback(() => {
    if (!canRedo()) return;
    
    const { past, present, future } = history.current;
    const newPresent = future;
    const newFuture = future.slice(1);
    
    history.current = {
      past: [...past, present],
      present: newPresent,
      future: newFuture
    };
    
    return newPresent;
  }, [canRedo]);

  const update = useCallback((newState: T) => {
    history.current = {
      past: [...history.current.past, history.current.present],
      present: newState,
      future: []
    };
  }, []);

  return {
    state: history.current.present,
    undo,
    redo,
    canUndo,
    canRedo,
    update
  };
}

扩展(防抖记录) ‌:

ini 复制代码
function useDebouncedHistory<T>(initialState: T, delay = 500) {
  const { update, ...rest } = useHistory<T>(initialState);
  const debouncedUpdate = useDebounce(update, delay);
  
  return {
    ...rest,
    update: debouncedUpdate
  };
}

// 使用示例
const { state, update } = useDebouncedHistory(0);
<input 
  type="number"
  value={state}
  onChange={(e) => update(Number(e.target.value))} 
/>

五、React Query与SWR联合数据管理

混合策略实现‌:

php 复制代码
import { useQuery } from 'react-query';
import useSWR from 'swr';

type HybridFetcher<T> = {
  queryKey: string;
  swrKey: string;
  fetcher: () => Promise<T>;
};

export function useHybridFetch<T>({
  queryKey,
  swrKey,
  fetcher
}: HybridFetcher<T>) {
  // React Query主请求
  const query = useQuery(queryKey, fetcher, {
    staleTime: 60_000,
    cacheTime: 300_000
  });

  // SWR后台刷新
  const swr = useSWR(swrKey, fetcher, {
    refreshInterval: 120_000,
    revalidateOnFocus: true,
    onSuccess: (data) => {
      queryClient.setQueryData(queryKey, data);
    }
  });

  // 错误合并
  const error = query.error || swr.error;
  
  return {
    data: query.data,
    isLoading: query.isLoading,
    isRefreshing: swr.isValidating,
    error
  };
}

// 使用示例
const { data, isLoading } = useHybridFetch({
  queryKey: 'user-data',
  swrKey: '/api/user',
  fetcher: () => fetchUserData()
});

数据流对比‌:

css 复制代码
textCopy Code
                            ┌─────────────┐
                            │  React Query │
                            └──────┬──────┘
                                   │
                 ┌─────────────────▼─────────────────┐
                 │          Query Cache              │
                 └─────────────────┬─────────────────┘
                                   │
                 ┌─────────────────▼─────────────────┐
                 │     SWR Background Refresh        │
                 └───────────────────────────────────┘
                                   │
                 ┌─────────────────▼─────────────────┐
                 │          UI Components              │
                 └───────────────────────────────────┘

混合方案优势‌:

  1. React Query负责主数据流和缓存策略
  2. SWR处理后台静默刷新和焦点重验证
  3. 共享缓存避免重复请求
  4. 错误处理优先级:主请求错误 > 刷新错误
相关推荐
LaoZhangAI37 分钟前
Claude MCP模型上下文协议详解:AI与外部世界交互的革命性突破【2025最新指南】
前端
LaoZhangAI37 分钟前
2025最全Cursor MCP实用指南:15个高效工具彻底提升AI编程体验【实战攻略】
前端
uhakadotcom41 分钟前
MQTT入门:轻量级物联网通信协议
后端·面试·github
Kagerou1 小时前
vue3基础知识(结合TypeScript)
前端
市民中心的蟋蟀1 小时前
第五章 使用Context和订阅来共享组件状态
前端·javascript·react.js
逆袭的小黄鸭1 小时前
JavaScript 闭包:强大特性背后的概念、应用与内存考量
前端·javascript·面试
carterwu1 小时前
各个大厂是怎么实现组件库和相应扩展的?基础组件、区块、页面
前端
Face1 小时前
promise 规范应用
前端
Mintopia1 小时前
Node.js 中 fs.readFile API 的使用详解
前端·javascript·node.js
Face1 小时前
事件循环
前端·javascript