React 17 新特性
React 17 的主要变化和升级要点
问题
React 17 带来了哪些改变?
解答
React 17 是一个”垫脚石”版本,没有新增面向开发者的功能,主要为未来的渐进式升级做准备。
1. 新的 JSX 转换
不再需要手动引入 React:
// React 16 及之前
import React from 'react';
function App() {
return <div>Hello</div>;
}
// React 17 之后
// 无需引入 React,编译器自动处理
function App() {
return <div>Hello</div>;
}
编译后的代码变化:
// 旧转换(React.createElement)
import React from 'react';
function App() {
return React.createElement('div', null, 'Hello');
}
// 新转换(jsx-runtime)
import { jsx as _jsx } from 'react/jsx-runtime';
function App() {
return _jsx('div', { children: 'Hello' });
}
2. 事件委托变更
事件不再挂载到 document,而是挂载到 React 根容器:
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
// React 16: 事件挂载到 document
// React 17: 事件挂载到 rootNode
这解决了多个 React 版本共存时的事件冲突问题:
// React 16 的问题
document.addEventListener('click', (e) => {
e.stopPropagation(); // 会阻止所有 React 事件
});
// React 17 不受影响,因为事件在根容器上
3. 移除事件池
// React 16: 事件对象会被复用,异步访问需要 e.persist()
function handleClick(e) {
console.log(e.type); // 'click'
setTimeout(() => {
console.log(e.type); // null(事件对象已被清空)
}, 100);
}
// 需要这样写
function handleClick(e) {
e.persist(); // 保留事件对象
setTimeout(() => {
console.log(e.type); // 'click'
}, 100);
}
// React 17: 直接访问即可
function handleClick(e) {
setTimeout(() => {
console.log(e.type); // 'click'
}, 100);
}
4. useEffect 清理函数异步执行
useEffect(() => {
// 副作用逻辑
return () => {
// React 16: 同步执行清理
// React 17: 异步执行清理(屏幕更新后)
};
}, []);
5. 渐进式升级支持
允许页面中同时运行多个 React 版本:
// 主应用使用 React 17
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// 某个独立模块可以使用 React 18
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('widget'));
root.render(<Widget />);
6. 更好的错误堆栈
组件堆栈信息更完整,可以点击跳转到源码位置:
Warning: Each child in a list should have a unique "key" prop.
at Item (http://localhost:3000/src/Item.js:5:3)
at List (http://localhost:3000/src/List.js:10:5)
at App (http://localhost:3000/src/App.js:8:3)
关键点
- JSX 转换:无需手动
import React,编译器自动注入 - 事件委托:从
document改为根容器,支持多版本共存 - 事件池移除:异步访问事件对象不再需要
e.persist() - 清理函数异步化:
useEffect清理在屏幕更新后执行 - 渐进式升级:为 React 18 及未来版本的平滑迁移铺路
目录