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