兼容模式 #

一、兼容模式概述 #

1.1 什么是 preact/compat #

preact/compat 是 Preact 提供的兼容层,允许在 Preact 中使用 React 生态系统。

text
React 代码 → preact/compat → Preact 运行时

1.2 兼容范围 #

功能 支持
React API
Hooks
Context
Refs
Portals
合成事件 部分

二、配置兼容模式 #

2.1 安装 #

bash
npm install preact

2.2 Vite 配置 #

javascript
import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';

export default defineConfig({
  plugins: [preact()],
  resolve: {
    alias: {
      'react': 'preact/compat',
      'react-dom': 'preact/compat',
      'react-dom/test-utils': 'preact/test-utils',
      'react/jsx-runtime': 'preact/jsx-runtime',
      'react/jsx-dev-runtime': 'preact/jsx-dev-runtime'
    }
  }
});

2.3 Webpack 配置 #

javascript
module.exports = {
  resolve: {
    alias: {
      'react': 'preact/compat',
      'react-dom': 'preact/compat',
      'react-dom/test-utils': 'preact/test-utils'
    }
  }
};

2.4 Rollup 配置 #

javascript
import alias from '@rollup/plugin-alias';

export default {
  plugins: [
    alias({
      entries: [
        { find: 'react', replacement: 'preact/compat' },
        { find: 'react-dom', replacement: 'preact/compat' },
        { find: 'react-dom/test-utils', replacement: 'preact/test-utils' }
      ]
    })
  ]
};

2.5 TypeScript 配置 #

json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact",
    "baseUrl": ".",
    "paths": {
      "react": ["node_modules/preact/compat"],
      "react-dom": ["node_modules/preact/compat"],
      "react-dom/test-utils": ["node_modules/preact/test-utils"]
    }
  }
}

三、兼容的 React 库 #

3.1 React Router #

jsx
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

3.2 Redux #

jsx
import { Provider, useSelector, useDispatch } from 'react-redux';
import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: state => { state.value += 1; },
    decrement: state => { state.value -= 1; }
  }
});

const store = configureStore({
  reducer: counterSlice.reducer
});

function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

function Counter() {
  const count = useSelector(state => state.value);
  const dispatch = useDispatch();

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch(counterSlice.actions.increment())}>+</button>
      <button onClick={() => dispatch(counterSlice.actions.decrement())}>-</button>
    </div>
  );
}

3.3 React Query #

jsx
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Users />
    </QueryClientProvider>
  );
}

function Users() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(res => res.json())
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error</p>;

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

3.4 Framer Motion #

jsx
import { motion } from 'framer-motion';

function AnimatedComponent() {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
      transition={{ duration: 0.5 }}
    >
      Animated content
    </motion.div>
  );
}

3.5 React Hook Form #

jsx
import { useForm } from 'react-hook-form';

function MyForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email', { required: true })} />
      {errors.email && <span>Email is required</span>}
      
      <input type="password" {...register('password', { minLength: 6 })} />
      {errors.password && <span>Min 6 characters</span>}
      
      <button type="submit">Submit</button>
    </form>
  );
}

四、兼容 API #

4.1 核心组件 #

jsx
import { 
  Component,
  PureComponent,
  memo,
  forwardRef,
  createContext,
  Fragment,
  StrictMode,
  Suspense,
  lazy
} from 'preact/compat';

// 使用方式与 React 相同
const MemoComponent = memo(function Component({ data }) {
  return <div>{data}</div>;
});

const Input = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

const ThemeContext = createContext('light');

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

4.2 Hooks #

jsx
import {
  useState,
  useEffect,
  useContext,
  useReducer,
  useCallback,
  useMemo,
  useRef,
  useImperativeHandle,
  useLayoutEffect,
  useDebugValue
} from 'preact/compat';

// 使用方式与 React 相同
function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = useRef(count);

  useEffect(() => {
    prevCount.current = count;
  }, [count]);

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

4.3 渲染 API #

jsx
import { render, createRoot, hydrate, createPortal } from 'preact/compat';

// React 18 方式
const root = createRoot(document.getElementById('root'));
root.render(<App />);

// 传统方式
render(<App />, document.getElementById('root'));

// SSR 水合
hydrate(<App />, document.getElementById('root'));

// Portal
function Modal({ children }) {
  return createPortal(
    <div class="modal">{children}</div>,
    document.body
  );
}

五、不兼容的功能 #

5.1 合成事件 #

jsx
// React 合成事件特有方法
e.isPropagationStopped();
e.isDefaultPrevented();
e.isPersistent();
e.persist();
e.nativeEvent;

// Preact 使用原生事件
// 这些方法不可用

5.2 生命周期 #

jsx
// React 支持
getSnapshotBeforeUpdate(prevProps, prevState) {}

// Preact 不支持
// 使用 useLayoutEffect 替代

5.3 内部 API #

jsx
// React 内部 API(不推荐使用)
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

// Preact 不支持

六、性能考虑 #

6.1 包体积 #

text
Preact + compat
├── preact           ~10KB
├── preact/compat    ~2KB
└── 总计             ~12KB

vs React
├── react            ~42KB
├── react-dom        ~130KB
└── 总计             ~172KB

6.2 运行时性能 #

jsx
// compat 模式性能略低于纯 Preact
// 但仍优于 React

// 推荐逐步迁移到纯 Preact API

七、调试 #

7.1 DevTools #

jsx
// 安装 Preact DevTools
import 'preact/debug';

// 或使用 React DevTools(兼容模式)
import 'preact/devtools';

7.2 错误信息 #

jsx
// 开发模式下启用详细错误
if (process.env.NODE_ENV === 'development') {
  require('preact/debug');
}

八、最佳实践 #

8.1 渐进迁移 #

jsx
// 第一阶段:使用 compat 兼容
import { useState } from 'react';  // 通过别名

// 第二阶段:逐步替换
import { useState } from 'preact/hooks';

// 第三阶段:移除 compat
import { useState } from 'preact/hooks';

8.2 选择性使用 #

jsx
// 只对需要的库使用 compat
import { render } from 'preact';  // 纯 Preact
import { Provider } from 'react-redux';  // 需要 compat

九、总结 #

要点 说明
配置 设置别名
兼容 大部分 React 库
差异 合成事件、部分生命周期
性能 略低于纯 Preact

核心原则:

  • 使用别名实现兼容
  • 测试第三方库兼容性
  • 渐进迁移到纯 Preact
  • 注意性能影响
最后更新:2026-03-28