🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱

🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱

📋 目录

  1. [💡 Context API 基础概念](#💡 Context API 基础概念 "#context-api-%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5") - 面试官最爱问的基础知识
  2. [🛠 项目实例分析](#🛠 项目实例分析 "#%E9%A1%B9%E7%9B%AE%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90") - 手把手教你从0到1实现
  3. [🔍 Context 源码实现原理](#🔍 Context 源码实现原理 "#context-%E6%BA%90%E7%A0%81%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86") - 深入React内核
  4. [⚡ 深度源码分析](#⚡ 深度源码分析 "#%E6%B7%B1%E5%BA%A6%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90") - Fiber架构下的Context处理
  5. [🎯 运行时模拟分析](#🎯 运行时模拟分析 "#%E8%BF%90%E8%A1%8C%E6%97%B6%E6%A8%A1%E6%8B%9F%E5%88%86%E6%9E%90") - 独家模拟器带你看透执行过程
  6. [🚀 性能优化与最佳实践](#🚀 性能优化与最佳实践 "#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5") - 避开99%开发者踩过的坑
  7. [❓ 常见问题与解决方案](#❓ 常见问题与解决方案 "#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88") - 面试真题实战

🎯 为什么要读这篇文章?

  • 面试必备: 腾讯、字节、阿里高频考点全覆盖
  • 源码级解析: 带你看懂React内部实现机制
  • 性能优化: 解决Context Hell、重渲染等常见问题
  • 实战导向: 结合真实项目代码,不纸上谈兵

💡 Context API 基础概念

🤔 什么是 Context API?(面试官最爱问)

Context API 是 React 提供的一种跨组件层级传递数据的机制,解决了"props drilling"(属性钻取)问题。

📝 面试题 : Context API 解决了什么问题?
标准答案: 解决了props drilling问题,避免了通过多层组件逐级传递props,让深层嵌套的组件能够直接访问祖先组件的数据。

🔑 核心概念(必知必会):

  • Context 对象:存储共享数据的容器
  • Provider 组件:提供数据的组件
  • Consumer:消费数据的方式(useContext Hook 或 Context.Consumer)

⚠️ 面试陷阱: 很多人分不清Provider和Consumer的区别,记住:Provider是"数据提供者",Consumer是"数据消费者"

📖 基本使用模式(三步走战略)

javascript 复制代码
// 🔸 步骤1: 创建 Context
const MyContext = createContext(defaultValue);

// 🔸 步骤2: 提供数据
<MyContext.Provider value={data}>
  <ChildComponents />
</MyContext.Provider>

// 🔸 步骤3: 消费数据
const data = useContext(MyContext);

💡 面试加分项: 能说出Context的三个核心API:createContext、Provider、useContext,并解释每个的作用

🛠 项目实例分析

📁 项目结构概览(企业级项目结构)

bash 复制代码
src/
├── ThemeContext.js      # 🎨 Context 创建(核心文件)
├── App.jsx             # 🏠 Provider 提供者(根组件)
├── hooks/
│   └── useTheme.js     # 🎣 自定义 Hook(封装逻辑)
└── components/
    ├── Page/           # 📄 中间组件(传递层)
    └── Child/          # 👶 消费者组件(最终使用)

🎯 架构设计思路: 这种结构体现了关注点分离原则,Context定义、Hook封装、组件使用各司其职

🔍 代码逐步分析(从0到1手把手教学)

🎨 步骤1: Context 创建(ThemeContext.js)
javascript 复制代码
import { createContext } from "react";

// 创建主题上下文,默认值为 "light"
export const ThemeContext = createContext("light");

🔬 源码解析(面试重点): createContext 在 React 内部会创建一个 Context 对象,包含:

  • _currentValue:当前值
  • _defaultValue:默认值
  • Provider:提供者组件
  • Consumer:消费者组件

📝 面试题 : createContext 的默认值什么时候会被使用?
答案: 当组件树中没有找到对应的 Provider 时,会使用默认值

2. Provider 提供者(App.jsx)
javascript 复制代码
import { useState } from 'react'
import Page from './components/Page'
import { ThemeContext } from './ThemeContext'

function App() {
  const [theme, setTheme] = useState("light");
  
  return (
    <ThemeContext.Provider value={theme}>
      <Page/> 
      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
        切换主题
      </button>
    </ThemeContext.Provider>
  )
}

运行机制:

  1. useState 创建响应式主题状态
  2. Providertheme 值传递给所有子组件
  3. 状态更新时,所有消费者组件重新渲染
3. 自定义 Hook(useTheme.js)
javascript 复制代码
import { useContext } from "react";
import { ThemeContext } from "../ThemeContext";

export function useTheme() {
  return useContext(ThemeContext);
}

设计模式分析:

  • 封装性:隐藏 Context 实现细节
  • 复用性:多个组件可以复用同一逻辑
  • 类型安全:可以添加 TypeScript 类型检查
4. 消费者组件

Page 组件(中间层):

javascript 复制代码
import Child from '../Child';
import {useTheme} from '../../hooks/useTheme';

const Page = () => {
  const theme = useTheme();
  console.log(theme);
  
  return (
    <>
      <h1 className={theme}>Page</h1>
      <h1>{theme}</h1>
      <Child/>
    </>
  )
}

Child 组件(最终消费者):

javascript 复制代码
import {useContext} from 'react';
import { ThemeContext } from '../../ThemeContext';

const Child = () => {
  const theme = useContext(ThemeContext);
  
  return (
    <div className='theme'>
      <h1>Child {theme}</h1>
    </div>
  )
}

Context 源码实现原理

createContext 源码分析

javascript 复制代码
// React 源码简化版
function createContext(defaultValue) {
  const context = {
    // 当前值(在没有 Provider 时使用默认值)
    _currentValue: defaultValue,
    _defaultValue: defaultValue,
    
    // Provider 和 Consumer 组件
    Provider: null,
    Consumer: null,
    
    // 调试用的显示名称
    displayName: null,
  };

  // 创建 Provider 组件
  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };

  // 创建 Consumer 组件
  context.Consumer = context;

  return context;
}

Provider 组件内部实现

javascript 复制代码
// Provider 组件的简化实现
function ContextProvider(props) {
  const { value, children, ...otherProps } = props;
  const context = this._context;
  
  // 核心:将新值推入 Context 栈
  pushProvider(context, value);
  
  try {
    // 渲染子组件
    return children;
  } finally {
    // 渲染完成后恢复之前的值
    popProvider(context);
  }
}

useContext Hook 实现

javascript 复制代码
// useContext 的简化实现
function useContext(context) {
  // 获取当前 Fiber 节点
  const dispatcher = resolveDispatcher();
  
  // 从 Context 栈中读取当前值
  return dispatcher.useContext(context);
}

function useContextImpl(context) {
  // 获取当前组件的 Fiber 节点
  const currentFiber = getCurrentFiber();
  
  // 查找最近的 Provider
  const value = readContext(context, currentFiber);
  
  return value;
}

深度源码分析

1. Context 值的传递机制

React 使用 栈结构 来管理 Context 值的传递:

javascript 复制代码
// Context 栈管理(简化)
const valueStack = [];
let index = -1;

function pushProvider(context, nextValue) {
  index++;
  valueStack[index] = context._currentValue;
  context._currentValue = nextValue;
}

function popProvider(context) {
  const currentValue = valueStack[index];
  index--;
  context._currentValue = currentValue;
}

运行过程模拟:

javascript 复制代码
// 初始状态
ThemeContext._currentValue = "light" // 默认值

// App 组件渲染时
pushProvider(ThemeContext, "dark");
// 现在 ThemeContext._currentValue = "dark"

// Page 组件调用 useContext(ThemeContext)
// 返回 "dark"

// Child 组件调用 useContext(ThemeContext)  
// 返回 "dark"

// App 组件渲染完成
popProvider(ThemeContext);
// 恢复 ThemeContext._currentValue = "light"

2. Fiber 架构中的 Context 处理

在 React Fiber 架构中,Context 的处理更加复杂:

javascript 复制代码
// Fiber 节点中的 Context 相关字段
const FiberNode = {
  // ...其他字段
  
  // Context 相关
  dependencies: null,     // 依赖的 Context 列表
  contextDependencies: null, // Context 依赖链表
  
  // 更新相关
  lanes: 0,              // 优先级车道
  childLanes: 0,         // 子树优先级
};

3. Context 变化检测机制

javascript 复制代码
// Context 变化检测(简化版)
function propagateContextChange(workInProgress, context, changedBits) {
  let fiber = workInProgress.child;
  
  while (fiber !== null) {
    // 检查当前 fiber 是否依赖了变化的 context
    if (fiber.dependencies !== null) {
      const dependency = fiber.dependencies.firstContext;
      
      while (dependency !== null) {
        if (dependency.context === context) {
          // 标记需要更新
          scheduleWorkOnFiber(fiber, currentTime, Sync);
          break;
        }
        dependency = dependency.next;
      }
    }
    
    // 继续遍历子树
    fiber = fiber.sibling;
  }
}

运行时模拟分析

完整的渲染流程模拟

让我们通过一个完整的例子来模拟 Context 的运行过程:

javascript 复制代码
// 模拟器 - Context 运行时状态
class ContextSimulator {
  constructor() {
    this.contextStack = [];
    this.fiberTree = null;
    this.currentFiber = null;
  }
  
  // 模拟 createContext
  createContext(defaultValue) {
    return {
      _currentValue: defaultValue,
      _defaultValue: defaultValue,
      _subscribers: new Set(),
    };
  }
  
  // 模拟 Provider 渲染
  renderProvider(context, value, renderChildren) {
    console.log(`📦 Provider 开始: 推入值 "${value}"`);
    
    // 保存旧值
    const oldValue = context._currentValue;
    this.contextStack.push({ context, oldValue });
    
    // 设置新值
    context._currentValue = value;
    
    // 渲染子组件
    const result = renderChildren();
    
    // 恢复旧值
    context._currentValue = oldValue;
    this.contextStack.pop();
    
    console.log(`📦 Provider 结束: 恢复值 "${oldValue}"`);
    return result;
  }
  
  // 模拟 useContext
  useContext(context) {
    const value = context._currentValue;
    console.log(`🔍 useContext 调用: 获取值 "${value}"`);
    
    // 记录依赖关系
    if (this.currentFiber) {
      this.currentFiber.contextDependencies.add(context);
    }
    
    return value;
  }
}

// 使用模拟器
const simulator = new ContextSimulator();
const ThemeContext = simulator.createContext("light");

console.log("=== 开始渲染应用 ===");

// 模拟 App 组件渲染
function App() {
  const theme = "dark"; // 假设状态是 dark
  
  return simulator.renderProvider(ThemeContext, theme, () => {
    // 模拟 Page 组件
    function Page() {
      const theme = simulator.useContext(ThemeContext);
      console.log(`📄 Page 组件渲染,主题: ${theme}`);
      
      // 模拟 Child 组件
      function Child() {
        const theme = simulator.useContext(ThemeContext);
        console.log(`👶 Child 组件渲染,主题: ${theme}`);
        return `Child with ${theme} theme`;
      }
      
      return Child();
    }
    
    return Page();
  });
}

App();

输出结果:

arduino 复制代码
=== 开始渲染应用 ===
📦 Provider 开始: 推入值 "dark"
🔍 useContext 调用: 获取值 "dark"
📄 Page 组件渲染,主题: dark
🔍 useContext 调用: 获取值 "dark"
👶 Child 组件渲染,主题: dark
📦 Provider 结束: 恢复值 "light"

状态更新时的重渲染模拟

javascript 复制代码
// 模拟状态更新导致的重渲染
class ReactScheduler {
  constructor() {
    this.updateQueue = [];
    this.isScheduled = false;
  }
  
  scheduleUpdate(component, newState) {
    console.log(`⏰ 调度更新: ${component.name} -> ${JSON.stringify(newState)}`);
    
    this.updateQueue.push({ component, newState });
    
    if (!this.isScheduled) {
      this.isScheduled = true;
      // 模拟异步调度
      Promise.resolve().then(() => this.flushUpdates());
    }
  }
  
  flushUpdates() {
    console.log(`🚀 执行批量更新,队列长度: ${this.updateQueue.length}`);
    
    while (this.updateQueue.length > 0) {
      const { component, newState } = this.updateQueue.shift();
      component.state = { ...component.state, ...newState };
      
      // 触发重渲染
      this.rerenderComponent(component);
    }
    
    this.isScheduled = false;
  }
  
  rerenderComponent(component) {
    console.log(`🔄 重新渲染组件: ${component.name}`);
    
    // 检查 Context 消费者是否需要更新
    if (component.contextDependencies) {
      component.contextDependencies.forEach(context => {
        console.log(`📡 检测到 Context 变化,通知消费者`);
      });
    }
  }
}

性能优化机制模拟

javascript 复制代码
// 模拟 Context 的优化机制
class ContextOptimizer {
  
  // 模拟浅比较优化
  shouldContextUpdate(oldValue, newValue) {
    const shouldUpdate = !Object.is(oldValue, newValue);
    
    console.log(`🔍 Context 值比较:`, {
      oldValue,
      newValue, 
      shouldUpdate
    });
    
    return shouldUpdate;
  }
  
  // 模拟 bail out 优化
  canBailOut(fiber, context) {
    // 如果组件没有使用这个 Context,可以跳过更新
    const hasContextDependency = fiber.contextDependencies?.has(context);
    
    console.log(`⚡ Bail out 检查:`, {
      componentName: fiber.type.name,
      hasContextDependency,
      canSkip: !hasContextDependency
    });
    
    return !hasContextDependency;
  }
  
  // 模拟 memo 组件的优化
  shouldMemoComponentUpdate(props, context) {
    // memo 组件会阻止因 Context 变化导致的更新
    // 除非显式依赖了 Context
    
    console.log(`🧠 Memo 组件更新检查:`, {
      propsChanged: false, // 假设 props 没变
      contextChanged: true, // Context 变了
      willUpdate: false    // memo 阻止了更新
    });
    
    return false;
  }
}

性能优化与最佳实践

1. Context 拆分策略

javascript 复制代码
// ❌ 不好的做法:单一巨大的 Context
const AppContext = createContext({
  user: null,
  theme: 'light',
  language: 'en',
  settings: {},
  notifications: []
});

// ✅ 好的做法:按功能拆分 Context
const UserContext = createContext(null);
const ThemeContext = createContext('light');
const LanguageContext = createContext('en');
const SettingsContext = createContext({});

原因分析:

  • 单一 Context 的任何变化都会导致所有消费者重渲染
  • 拆分后只有相关消费者会重渲染

2. 使用 useMemo 优化 Provider 值

javascript 复制代码
// ❌ 问题代码:每次渲染都创建新对象
function App() {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');
  
  return (
    <AppContext.Provider value={{ user, theme, setUser, setTheme }}>
      <Components />
    </AppContext.Provider>
  );
}

// ✅ 优化后:使用 useMemo 缓存 value
function App() {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');
  
  const contextValue = useMemo(() => ({
    user,
    theme,
    setUser,
    setTheme
  }), [user, theme]);
  
  return (
    <AppContext.Provider value={contextValue}>
      <Components />
    </AppContext.Provider>
  );
}

3. 自定义 Hook 封装最佳实践

javascript 复制代码
// 完整的自定义 Hook 实现
function useTheme() {
  const context = useContext(ThemeContext);
  
  // 错误边界检查
  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  
  return context;
}

// 带选择器的优化版本
function useThemeSelector(selector) {
  const theme = useTheme();
  
  // 使用 useMemo 避免不必要的计算
  return useMemo(() => selector(theme), [theme, selector]);
}

// 使用示例
function Component() {
  // 只关心主题颜色,不关心其他属性
  const primaryColor = useThemeSelector(theme => theme.colors.primary);
  
  return <div style={{ color: primaryColor }}>Hello</div>;
}

4. Provider 组件的最佳实践

javascript 复制代码
// 完整的 Provider 实现
function ThemeProvider({ children, initialTheme = 'light' }) {
  const [theme, setTheme] = useState(initialTheme);
  const [isDarkMode, setIsDarkMode] = useState(initialTheme === 'dark');
  
  // 复杂逻辑的 reducer 管理
  const [state, dispatch] = useReducer(themeReducer, {
    theme,
    isDarkMode,
    colors: getThemeColors(theme),
    fonts: getThemeFonts(theme)
  });
  
  // 派生状态计算
  const derivedValues = useMemo(() => ({
    isLightMode: !state.isDarkMode,
    contrastRatio: calculateContrastRatio(state.colors),
    accessibility: getAccessibilitySettings(state)
  }), [state.isDarkMode, state.colors]);
  
  // 动作函数
  const actions = useMemo(() => ({
    toggleTheme: () => dispatch({ type: 'TOGGLE_THEME' }),
    setTheme: (newTheme) => dispatch({ type: 'SET_THEME', payload: newTheme }),
    setCustomColors: (colors) => dispatch({ type: 'SET_COLORS', payload: colors })
  }), []);
  
  // Context 值
  const value = useMemo(() => ({
    ...state,
    ...derivedValues,
    ...actions
  }), [state, derivedValues, actions]);
  
  // 副作用:主题变化时更新 CSS 变量
  useEffect(() => {
    const root = document.documentElement;
    
    Object.entries(state.colors).forEach(([key, value]) => {
      root.style.setProperty(`--color-${key}`, value);
    });
  }, [state.colors]);
  
  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

常见问题与解决方案

1. Context Hell 问题

问题:

javascript 复制代码
// Context Hell - 过多的 Provider 嵌套
<UserProvider>
  <ThemeProvider>
    <LanguageProvider>
      <SettingsProvider>
        <NotificationProvider>
          <App />
        </NotificationProvider>
      </SettingsProvider>
    </LanguageProvider>
  </ThemeProvider>
</UserProvider>

解决方案:

javascript 复制代码
// 创建复合 Provider
function AppProviders({ children }) {
  return (
    <UserProvider>
      <ThemeProvider>
        <LanguageProvider>
          <SettingsProvider>
            <NotificationProvider>
              {children}
            </NotificationProvider>
          </SettingsProvider>
        </LanguageProvider>
      </ThemeProvider>
    </UserProvider>
  );
}

// 或者使用 Provider 组合器
function composeProviders(...providers) {
  return ({ children }) => {
    return providers.reduceRight(
      (acc, Provider) => <Provider>{acc}</Provider>,
      children
    );
  };
}

const CombinedProvider = composeProviders(
  UserProvider,
  ThemeProvider,
  LanguageProvider
);

2. Context 更新导致的性能问题

问题诊断工具:

javascript 复制代码
// Context 性能监控 Hook
function useContextPerformance(contextName, value) {
  const renderCount = useRef(0);
  const lastValue = useRef(value);
  
  useEffect(() => {
    renderCount.current++;
    
    if (!Object.is(lastValue.current, value)) {
      console.log(`🔄 Context "${contextName}" 更新:`, {
        渲染次数: renderCount.current,
        旧值: lastValue.current,
        新值: value,
        变化原因: getChangeReason(lastValue.current, value)
      });
      
      lastValue.current = value;
    }
  });
  
  useEffect(() => {
    return () => {
      console.log(`📊 Context "${contextName}" 性能统计:`, {
        总渲染次数: renderCount.current,
        平均渲染时间: getAverageRenderTime()
      });
    };
  }, []);
}

function getChangeReason(oldValue, newValue) {
  if (typeof oldValue === 'object' && typeof newValue === 'object') {
    const changedKeys = Object.keys(newValue).filter(
      key => !Object.is(oldValue[key], newValue[key])
    );
    return `对象属性变化: ${changedKeys.join(', ')}`;
  }
  return '值类型变化';
}

3. 条件渲染中的 Context 问题

问题:

javascript 复制代码
// 问题:Context Provider 被条件渲染
function App() {
  const [showTheme, setShowTheme] = useState(false);
  
  return (
    <div>
      {showTheme && (
        <ThemeProvider>
          <Component />
        </ThemeProvider>
      )}
    </div>
  );
}

解决方案:

javascript 复制代码
// 解决方案:始终保持 Provider,通过 disabled 状态控制
function App() {
  const [showTheme, setShowTheme] = useState(false);
  
  return (
    <ThemeProvider disabled={!showTheme}>
      <Component />
    </ThemeProvider>
  );
}

function ThemeProvider({ children, disabled = false }) {
  const defaultValue = useMemo(() => ({ disabled }), [disabled]);
  
  return (
    <ThemeContext.Provider value={disabled ? defaultValue : realValue}>
      {children}
    </ThemeContext.Provider>
  );
}

4. Context 在服务端渲染中的问题

javascript 复制代码
// SSR 兼容的 Context Provider
function SSRCompatibleProvider({ children }) {
  const [isClient, setIsClient] = useState(false);
  
  // 确保客户端和服务端渲染一致
  useEffect(() => {
    setIsClient(true);
  }, []);
  
  const value = useMemo(() => ({
    isClient,
    // 只在客户端可用的功能
    localStorage: isClient ? window.localStorage : null,
    sessionStorage: isClient ? window.sessionStorage : null
  }), [isClient]);
  
  return (
    <SSRContext.Provider value={value}>
      {children}
    </SSRContext.Provider>
  );
}

高级模式与扩展

1. Context 与 Suspense 结合

javascript 复制代码
// 异步 Context 模式
function AsyncDataProvider({ children }) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  
  // 使用 Suspense 处理异步数据
  const suspenseData = useMemo(() => {
    if (error) throw error;
    if (!data) throw fetchData().then(setData).catch(setError);
    return data;
  }, [data, error]);
  
  return (
    <DataContext.Provider value={suspenseData}>
      <Suspense fallback={<Loading />}>
        {children}
      </Suspense>
    </DataContext.Provider>
  );
}

2. Context 状态机模式

javascript 复制代码
// 使用状态机管理复杂 Context 状态
import { createMachine, interpret } from 'xstate';

const themeMachine = createMachine({
  id: 'theme',
  initial: 'light',
  states: {
    light: {
      on: { TOGGLE: 'dark', SET_AUTO: 'auto' }
    },
    dark: {
      on: { TOGGLE: 'light', SET_AUTO: 'auto' }
    },
    auto: {
      on: { TOGGLE: 'light', SET_MANUAL: 'light' }
    }
  }
});

function ThemeStateMachineProvider({ children }) {
  const [state, send] = useReducer(themeMachine.transition, themeMachine.initialState);
  
  const value = useMemo(() => ({
    theme: state.value,
    toggleTheme: () => send('TOGGLE'),
    setAutoTheme: () => send('SET_AUTO'),
    isAuto: state.matches('auto')
  }), [state]);
  
  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

总结

React Context API 是一个强大但需要谨慎使用的特性。通过深入理解其源码实现和运行机制,我们可以:

  1. 正确使用:避免常见的性能陷阱和反模式
  2. 合理设计:构建可维护、高性能的状态管理方案
  3. 调试优化:快速定位和解决 Context 相关问题
  4. 扩展应用:结合其他 React 特性构建复杂应用

关键要点回顾:

  • 理解机制:Context 通过栈结构管理值的传递
  • 性能优化:合理拆分 Context,使用 useMemo 缓存值
  • 最佳实践:封装自定义 Hook,提供错误边界
  • 避免陷阱:防止 Context Hell,注意条件渲染问题
  • 高级模式:结合 Suspense、状态机等构建复杂应用

通过本文的深入分析,相信你已经掌握了 React Context API 的精髓,能够在实际项目中灵活运用这一强大的特性。

相关推荐
江号软件分享32 分钟前
有效保障隐私,如何安全地擦除电脑上的敏感数据
前端
web守墓人2 小时前
【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库
前端·javascript·html
Savior`L2 小时前
CSS知识复习5
前端·css
许白掰2 小时前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器
中微子7 小时前
React 状态管理 源码深度解析
前端·react.js
加减法原则8 小时前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
yanlele9 小时前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
lichenyang4539 小时前
React移动端开发项目优化
前端·react.js·前端框架
天若有情6739 小时前
React、Vue、Angular的性能优化与源码解析概述
vue.js·react.js·angular.js