1. 前言
本文将以 React 和 Vue3 进行同步对比,带大家快速掌握 React。这个系列分为上下两篇,上篇内容包括:
- 组件
 - JSX
 - 插值
 - 数据通信
 - 渲染
 - 事件
 - 状态
 
下篇包括:
- 元素引用
 - 跨级传值
 - Portals
 - Suspense
 - Error Boundaries
 - Hooks
 
2. 元素引用
想要引用HTML元素节点,在 Vue 中通过 ref 来实现,React 通过 useRef 钩子。
Vue:
            
            
              ts
              
              
            
          
          const inputRef = ref(null);
onMounted(() => {
  if (inputRef) {
    inputRef.value.focus();
  }
});
<input type="text" ref="inputRef" />
        React:
            
            
              ts
              
              
            
          
          const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
  inputRef?.current?.focus();
}, []);
<input type="text" ref={inputRef} />
        通过 .current 使用 ref 引用。
3. 跨级传值
Vue:
            
            
              ts
              
              
            
          
          // App.vue
<script setup>
import { provide } from 'vue';
provide('theme', 'light');
</script>
<template>
  <ChildComponent />
</template>
// ChildComponent.vue
<script setup>
import { inject } from 'vue';
const theme = inject('theme');
</script>
<template>
  <div>当前主题:{{ theme }}</div>
</template>
        React:
            
            
              ts
              
              
            
          
          // 1. 创建一个Context
export const ThemeContext = createContext('light');
// 2. 在父组件(App.tsx)中使用 ThemeContext.Provider 包裹子组件,并传值
//<ThemeContext.Provider value="dark">
  <ChildComponent />
//</ThemeContext.Provider>
// ChildComponent.tsx
import { useContext } from 'react';
import { ThemeContext } from '../App'; // 3. 导入上下文
export default function ChildComponent() {
  // 使用 useContext 获取Context的值
  const theme = useContext(ThemeContext); // 4. 使用上下文中的数据
  return <div>当前主题:{theme}</div>;
}
        如果父组件没有提供 <ThemeContext.Provider>,将会使用默认值 light。
4. Portals
传送门,Vue3提供了内置组件<Teleport>,而在 React 中使用的是 createPortal。
Vue:
            
            
              ts
              
              
            
          
          <template>
  <div>
    <teleport to="body">
      <p>hello</p>
    </teleport>
  </div>
</template>
        React:
            
            
              ts
              
              
            
          
          import { createPortal } from 'react-dom';
export default function App() {
	return (
		<div>
			{createPortal(<p>hello</p>, document.body)}
		<div>
	)
}
        5. Suspense
Vue:
            
            
              ts
              
              
            
          
          <template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <p>Loading...</p>
    </template>
  </Suspense>
</template>
<script setup>
import { defineAsyncComponent, Suspense } from 'vue';
const AsyncComponent = defineAsyncComponent(() =>
  import('./AsyncComponent.vue')
);
</script>
        React:
            
            
              ts
              
              
            
          
          import React, { Suspense } from 'react';
const AsyncComponent = React.lazy(() => import('./AsyncComponent'));
function App() {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <AsyncComponent />
    </Suspense>
  );
}
export default App;
        6. Error Boundaries
边界错误处理。
Vue:
            
            
              ts
              
              
            
          
          app.config.errorHandler = (err, instance, info) => {
  // 处理错误,例如:报告给一个服务
}
        React:
            
            
              ts
              
              
            
          
          class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  static getDerivedStateFromError(error) {
    // 更新状态,以便下一次渲染将显示后备 UI。
    return { hasError: true };
  }
  componentDidCatch(error, info) {
    // 示例"组件堆栈":
    //   在 ComponentThatThrows 中(由 App 创建)
    //   在 ErrorBoundary 中(由 APP 创建)
    //   在 div 中(由 APP 创建)
    //   在 App 中
    logErrorToMyService(error, info.componentStack);
  }
  render() {
    if (this.state.hasError) {
      // 你可以渲染任何自定义后备 UI
      return this.props.fallback;
    }
    return this.props.children;
  }
}
<ErrorBoundary fallback={<p>Something went wrong</p>}>
  <Profile />
</ErrorBoundary>
        7. Hooks
7.1 State 状态
Vue:ref(), reactive()
            
            
              ts
              
              
            
          
          const demoRef = ref('demo ref');
const demoRef2 = ref([1, 2, 3]);
const demoReactive = reactive({
  name: 'Alice',
  age: 18,
});
        React:useState()
            
            
              ts
              
              
            
          
          const [demoState, setDemoState] = useState();
        7.2 Context 上下文
Vue:provide() + inject()
React:useContext()
7.3 Ref 引用
Vue:ref()
React:useRef()
7.4 Effect 副作用
Vue:无
React:useEffect() React 组件是比较"纯"的函数,它能保证输入和输出的一致性。与浏览器、API等外部系统等连接(如:网络请求、订阅事件、更新 DOM),要使用 useEffect() 钩子,而不是插入到组件的渲染中,这是跳出 React 的方式。
下面是 React 组件能够直接进行网络请求的两个位置:
            
            
              ts
              
              
            
          
          // 在事件处理函数中
function onSubmit(e) {
	e.preventDefault();
	http.post('api/login', {...}).then(...).catch(...);
}
// 在 useEffect 中
useEffect(() => {
	const getData = async () => {
		fetch(url).then(...).catch(...)
	}
	getData();
},[])
        7.5 Performance 性能优化
Vue:computed()
            
            
              ts
              
              
            
          
          const count = ref(1);
const doubleCount = computed(() => count.value * 2);
        React:useMemo(), useCallback()
            
            
              ts
              
              
            
          
          // 1. useMemo()
// 接受一个函数和一个依赖项数组,它会在依赖项发生变化时重新计算函数的返回值,并将结果缓存起来。
import React, { useState, useMemo } from 'react';
const ExampleComponent = () => {
    const [count, setCount] = useState(0);
    // 使用 useMemo 缓存计算结果
    const doubleCount = useMemo(() => {
        return count * 2;
    }, [count]); // 仅在 count 发生变化时重新计算
    return (
        <div>
            <button onClick={() => setCount(count + 1)}>Increment Count</button>
            <p>Count: {count}</p>
            <p>double Count: {doubleCount}</p>
        </div>
    );
};
// 2. useCallback()
// 用于缓存回调函数,避免在每次渲染时创建新的回调函数。
import { useState, useCallback } from 'react';
const ExampleComponent = () => {
    const [count, setCount] = useState(0);
    // 使用 useCallback 缓存回调函数
    const increment = useCallback(() => {
        setCount(count + 1);
    }, [count]); // 仅在 count 发生变化时重新创建
    return (
        <div>
            <button onClick={increment}>Increment Count</button>
            <p>Count: {count}</p>
        </div>
    );
};
        技术交流:
- 公众号:见嘉 Being Dev
 - v:with_his_x