React核心概念:
问:请解释React中的虚拟DOM是什么,以及它是如何提高性能的? 虚拟DOM(Virtual DOM)是一种用JavaScript对象表示的内存中的抽象文档对象模型。它是真实DOM的轻量级拷贝,React通过虚拟DOM来提高性能和优化页面渲染的过程。
虚拟DOM的工作流程如下:
- 状态变更触发渲染: 当React组件的状态发生变化,或者有其他引起重新渲染的事件发生时,React会创建一个新的虚拟DOM树。
- 虚拟DOM比较: 新创建的虚拟DOM树与先前的虚拟DOM树进行比较,React使用一种称为"协调算法"的策略来确定需要进行更新的最小操作集。
- 差异计算: React通过比较前后两个虚拟DOM树的差异,计算出需要进行更新的最小子树,而不是直接操作真实DOM。
- 生成变更集: React生成一个包含有关将要进行的DOM操作的变更集,这个变更集包括需要添加、更新或删除的节点信息。
- 批量更新真实DOM: 最后,React将变更集一次性应用到真实DOM上,通过最小化对真实DOM的操作,提高了性能。
虚拟DOM如何提高性能:
- 减少直接操作真实DOM的次数: 直接对真实DOM的操作是相对昂贵的,通过使用虚拟DOM,React可以在内存中操作JavaScript对象,而不是频繁地进行真实DOM的增、删、改操作。
- 批量更新: 虚拟DOM允许React批量处理变更集,而不是每次状态变化都立即更新真实DOM。这样可以最小化布局和绘制的次数,提高整体性能。
- 更高效的更新策略: React使用协调算法,只更新必要的部分,而不是整个DOM树。这减少了不必要的DOM操作,提高了更新的效率。
- 跨平台渲染: 虚拟DOM的概念使得React可以更容易地在不同平台上进行渲染,比如在浏览器、服务器端以及移动端使用React Native。
总体而言,虚拟DOM的引入使得React在保持开发者友好性的同时,能够更高效地管理和更新页面,提高了React应用的性能。
组件和状态管理:
在React中,什么是受控组件和非受控组件?在什么情况下你会选择使用其中之一?
在React中,"受控组件(Controlled Components)"和"非受控组件(Uncontrolled Components)"是两种处理表单元素的不同方式。
受控组件(Controlled Components) :
-
定义: 受控组件是由React控制表单元素的状态的组件。表单元素的值(如
<input>
、<textarea>
、<select>
等)被React的state管理,并通过onChange
等事件处理函数来更新状态。 -
选择情况: 通常在需要对输入进行验证、处理和控制时使用。适用于需要在React中保持单一数据源的情况,以便进行状态管理和操作。
-
示例:
jsx
class ControlledComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
};
}
handleChange = (event) => {
this.setState({ inputValue: event.target.value });
};
render() {
return (
<input
type="text"
value={this.state.inputValue}
onChange={this.handleChange}
/>
);
}
}
非受控组件(Uncontrolled Components) :
-
定义: 非受控组件是由DOM本身管理表单元素的状态的组件。表单元素的值由DOM直接管理,而不是通过React的state。
-
选择情况: 适用于简单的场景,或者在与非React代码集成时。非受控组件使得对表单元素的访问更直接,但缺少了React的状态管理和一致性。
-
示例:
jsx
class UncontrolledComponent extends React.Component {
inputRef = React.createRef();
handleSubmit = () => {
alert(`Input Value: ${this.inputRef.current.value}`);
};
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.handleSubmit}>Submit</button>
</div>
);
}
}
选择时机:
-
受控组件:
- 当需要在React中进行状态管理和控制时。
- 需要进行输入验证或实时反馈时。
- 在表单元素之间有互动关系需要同步时。
-
非受控组件:
- 当希望让DOM自己管理表单元素的状态时。
- 在React和非React代码之间有交互,需要更直接的DOM访问时。
- 在某些简单场景下,可以减少React的状态管理和事件处理的开销。
React Hooks:
问:请讨论一下React Hooks的用途,并解释一下
useState
和useEffect
的作用及使用场景。 React Hooks 是 React 16.8 引入的一项特性,它们使得在函数组件中使用状态(state)和其他 React 特性变得更加方便。Hooks 是一组可以让你在函数组件中"钩入" React 特性的函数。
React Hooks 的用途:
- 状态管理: 使用
useState
Hook 可以在函数组件中添加局部状态,而不再需要类组件。这使得状态逻辑更加清晰和简单。 - 副作用处理: 使用
useEffect
Hook 可以在组件渲染后执行副作用操作,例如数据获取、订阅、手动 DOM 操作等。 - 上下文(Context): 使用
useContext
Hook 可以更方便地访问 React 上下文。 - 引用(Ref): 使用
useRef
Hook 可以创建可变的 ref 对象,通常用于跟踪 DOM 元素或保存之前的状态。 - 自定义 Hooks: 允许开发者自定义自己的 Hooks,以便在多个组件之间共享逻辑。
useState
的作用及使用场景:
useState
Hook 用于在函数组件中添加局部状态。
- 作用: 用于声明一个状态变量,并返回该状态变量和一个更新该变量的函数。
- 使用场景:
-
表单输入处理: 当你需要追踪表单元素的输入状态时,可以使用
useState
。例如,一个简单的登录表单:jsxconst [username, setUsername] = useState(''); const [password, setPassword] = useState(''); // ... return ( <form> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> {/* ... */} </form> );
-
动态渲染列表: 在处理动态列表时,你可以使用
useState
来追踪列表数据。jsxconst [items, setItems] = useState(['Item 1', 'Item 2']); // ... return ( <ul> {items.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> );
-
展开/收起状态: 当你需要在组件中实现展开和收起的功能时,可以使用
useState
来追踪展开状态。jsxconst [isOpen, setIsOpen] = useState(false); // ... return ( <div> <button onClick={() => setIsOpen(!isOpen)}> {isOpen ? '收起' : '展开'} </button> {isOpen && <p>这是展开的内容。</p>} </div> );
useEffect
的作用及使用场景:
useEffect
Hook 用于处理副作用操作。
- 作用: 用于在每次组件渲染后执行一些操作,例如数据获取、订阅、DOM 操作等。
- 使用场景: 任何副作用的场景,例如网络请求、定时器、订阅等。也可用于模拟类组件的生命周期方法。
-
数据获取和副作用操作: 用于处理需要在组件渲染后进行的数据获取或副作用操作。
jsxuseEffect(() => { // 数据获取 fetchData().then((result) => { setData(result); }); }, []); // 依赖项为空数组表示只在组件挂载时执行
-
订阅和取消订阅: 当你需要在组件挂载时订阅某些事件,可以使用
useEffect
进行订阅,同时返回一个清理函数用于取消订阅。jsxuseEffect(() => { const subscription = subscribeToEvent(); return () => { // 清理订阅 subscription.unsubscribe(); }; }, []); // 依赖项为空数组表示只在组件挂载时执行
-
模拟
componentDidUpdate
: 使用useEffect
可以在组件更新后执行某些操作。jsxuseEffect(() => { // 在每次组件更新时执行的操作 });
-
清理操作: 使用
useEffect
返回的清理函数,可以用于清理资源或取消异步操作。jsxuseEffect(() => { // 在组件挂载时执行的操作 return () => { // 在组件卸载时执行的清理操作 }; }, []);
-
useEffect
的依赖项
useEffect
的依赖项是一个可选的参数,用于指定什么情况下 useEffect
应该重新运行。它是一个数组,包含影响 useEffect
行为的变量。如果依赖项数组中的任何一个变量发生变化,useEffect
将会重新运行。如果依赖项数组为空,则 useEffect
仅在组件挂载和卸载时运行。
useEffect
的依赖项可以是以下类型:
-
状态变量: 最常见的用法是传递一个或多个状态变量,以便在这些状态变量发生变化时触发
useEffect
的重新运行。jsxconst [count, setCount] = useState(0); useEffect(() => { // 在 count 变化时执行的操作 }, [count]);
-
Props: 如果
useEffect
内部使用了来自 props 的值,你可能需要将这些 props 添加到依赖项数组中。jsxuseEffect(() => { // 在 props.value 变化时执行的操作 }, [props.value]);
-
回调函数: 如果
useEffect
内部使用了一个回调函数,通常情况下,你需要将该回调函数添加到依赖项数组中,以确保每次回调函数变化时useEffect
重新运行。jsxconst callback = () => { // 回调函数的实现 }; useEffect(() => { // 在 callback 变化时执行的操作 }, [callback]);
-
引用类型: 如果依赖项是一个引用类型,如对象或数组,你需要确保在更新状态时创建一个新的引用,以便触发
useEffect
重新运行。jsxconst [data, setData] = useState([]); useEffect(() => { // 在 data 变化时执行的操作 }, [data]);
需要注意的是,如果依赖项数组中的变量是一个闭包中的值,而这个闭包在每次渲染时都创建,那么 useEffect
将会在每次渲染时都重新运行。这可能会导致性能问题,因此在选择依赖项时需要谨慎。
性能优化:
问:在React应用中,有哪些常见的性能优化手段,尤其是在处理大型数据集或复杂UI时?
在处理大型数据集或复杂 UI 的 React 应用中,有一些常见的性能优化手段可以帮助提高应用性能,减少页面渲染时间。以下是一些常见的性能优化策略:
-
虚拟化列表: 使用虚拟滚动或虚拟列表技术,仅渲染可见区域的元素,而不是一次性渲染整个大型列表。例如,
react-window
或react-virtualized
库可以帮助实现虚拟列表。 -
分页加载数据: 将大型数据集分成多个分页,根据需要异步加载每一页的数据,以避免一次性加载过多的数据。这对于大型表格或列表特别有效。
-
使用 shouldComponentUpdate 或 React.memo: 在类组件中,可以通过手动实现
shouldComponentUpdate
函数,或者在函数组件中使用React.memo
高阶组件,避免不必要的组件重新渲染。jsxclass MyComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { // 返回 true 表示组件应该重新渲染,返回 false 表示避免重新渲染 } // ... } // 或者使用 React.memo const MemoizedComponent = React.memo(MyComponent);
-
使用 PureComponent 或 memo 包装纯函数组件: PureComponent 会对组件的 props 和 state 进行浅比较,如果没有变化,就不会重新渲染。
React.memo
在函数组件中提供了类似的功能。jsxclass MyPureComponent extends React.PureComponent { // ... } // 或者使用 React.memo const MemoizedFunctionalComponent = React.memo(MyFunctionalComponent);
-
避免不必要的重新渲染: 在组件中避免在
render
方法中创建新的对象或函数,以减少不必要的重新渲染。 -
使用 useMemo 和 useCallback: 使用
useMemo
缓存计算结果,避免在每次渲染时重新计算。使用useCallback
缓存回调函数,以确保相同的回调函数引用在渲染之间保持不变。jsxconst memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); const memoizedCallback = useCallback(() => { // callback implementation }, [dependency1, dependency2]);
-
性能分析工具: 使用 React 的性能分析工具,例如 React DevTools 的性能面板,来识别组件渲染的性能瓶颈,并进行进一步的优化。
-
懒加载和代码分割: 使用 React 的懒加载和代码分割功能,将组件按需加载,以减小初始加载的包大小,提高应用启动性能。
以上这些手段并非唯一适用,具体应用场景需要根据实际情况灵活选用。在性能优化过程中,建议使用浏览器的性能分析工具来检查和确认优化效果。
如何实现组件按需加载:
在React中,实现组件按需加载通常通过使用动态 import()
函数或 React 的 React.lazy
和 Suspense
特性来实现。这些方法允许你在需要时异步加载组件,而不是将所有组件一次性打包到初始加载的包中。这对于减小初始加载的包大小,提高应用启动性能非常有帮助。
方法一:使用动态 import()
函数
jsx
import React, { lazy, Suspense } from 'react';
const MyComponent = lazy(() => import('./MyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
}
在上面的例子中,MyComponent
组件会在首次渲染时被懒加载,并且 fallback
属性指定了一个在加载过程中显示的占位符。
方法二:使用 React.lazy 和 Suspense
jsx
import React, { lazy, Suspense } from 'react';
const MyComponent = lazy(() => import('./MyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
}
与第一种方法相似,React.lazy
允许你使用动态 import()
来创建一个懒加载的组件。Suspense
则用于指定加载过程中的占位符。
额外注意事项:
-
Webpack 配置: 确保你的 Webpack 配置支持动态
import()
,通常需要 Babel 的插件(如@babel/plugin-syntax-dynamic-import
)和 Webpack 的@babel/preset-env
。json// .babelrc or babel.config.js { "presets": ["@babel/preset-env"], "plugins": ["@babel/plugin-syntax-dynamic-import"] }
-
Chunk 名称: 当你使用动态
import()
时,Webpack 会为每个按需加载的组件生成一个单独的 chunk。你可以使用webpackChunkName
注释来为 chunk 命名,以便在调试时更容易识别。jsxconst MyComponent = lazy(() => import(/* webpackChunkName: "my-component" */ './MyComponent'));
状态管理库:
问:除了React的内置状态管理,你是否使用过其他状态管理库(如Redux、MobX)?在什么场景下你会选择使用它们?
在React应用中,除了React的内置状态管理(使用useState
和useReducer
)之外,一些常见的第三方状态管理库包括Redux和MobX。
Redux:
Redux 是一个可预测状态容器,广泛用于大型和复杂的React应用中。选择使用Redux的一些场景包括:
- 大型应用: 当应用的状态变得复杂,需要在多个组件之间共享状态时,Redux可以提供一个单一的状态源,简化了状态管理。
- 时间旅行调试: Redux提供了强大的时间旅行调试工具,可以回溯到先前的应用状态,有助于诊断和修复错误。
- 异步数据流: 当应用需要处理大量异步操作时,Redux的中间件(如redux-thunk、redux-saga)可以帮助管理异步数据流。
MobX:
MobX 是一个简单、可扩展的状态管理库,它采用了响应式编程的思想。选择使用MobX的一些场景包括:
- 简单性: 对于一些小到中等规模的应用,MobX提供了一种更简单的状态管理方式,不需要像Redux那样配置大量的动作和规则。
- 响应式: MobX的响应式系统使得状态的变化可以自动地通知相关的组件进行更新,减少了手动的状态管理工作。
- 可读性: MobX的代码通常比Redux更为简洁和易读,适用于一些对可维护性和开发效率有较高要求的项目。
选择使用Redux、MobX还是React的内置状态管理取决于项目的规模、复杂性、团队经验以及开发者个人喜好。在小型项目中,React的内置状态管理通常已经足够,而在大型项目中,引入Redux或MobX可以更好地组织和管理状态。
React Router:
问:请解释React Router的作用,并讨论一下它是如何处理路由的?
React Router 是一个用于在React应用中处理导航和路由的库。它允许你根据应用的不同状态显示不同的组件,实现单页应用(SPA)中的页面切换和导航功能。
React Router 的作用:
- 路由导航: React Router 提供了一套导航组件,如
<Link>
和<NavLink>
,用于在应用中不同的页面之间进行导航。 - 声明式路由: 通过使用声明式的路由配置,你可以在应用中定义路由规则,而不必依赖于特定的URL结构。
- 路由参数: 支持在路由中传递参数,使得不同页面可以接收和处理不同的数据。
- 嵌套路由: 可以嵌套配置路由,实现页面内的子页面和组件的切换。
- 历史记录管理: React Router 提供了一个
<BrowserRouter>
组件,可以处理浏览器历史记录,使得浏览器的前进和后退按钮能够与应用的导航交互。
React Router 的工作原理:
React Router 主要通过组件渲染和上下文传递来实现路由的处理。
-
Router 组件: React Router 提供了不同类型的
<Router>
组件,如<BrowserRouter>
、<HashRouter>
等。这些组件包装整个应用,负责管理历史记录和URL。 -
Route 组件: 使用
<Route>
组件定义页面的路由规则。每个<Route>
组件都有一个path
属性,用于指定匹配的URL路径,并有一个component
属性,指定要渲染的React组件。jsx<Route path="/home" component={Home} />
-
Link 和 NavLink 组件: 使用
<Link>
和<NavLink>
组件创建导航链接,允许用户点击链接切换页面。jsx<Link to="/home">Home</Link>
-
Router 上下文: React Router 使用上下文(context)来在组件之间传递路由信息。通过
withRouter
高阶组件,可以将路由信息注入到组件的props
中。jsximport { withRouter } from 'react-router-dom'; const MyComponent = ({ history, location, match }) => { // 使用 history, location, match 对象 // ... }; export default withRouter(MyComponent);
-
Redirect 组件: 使用
<Redirect>
组件可以在特定条件下重定向到其他页面。jsx<Redirect from="/old-url" to="/new-url" />
React Router 的这些组件和机制共同作用,使得在React应用中实现导航和路由管理变得相对简单,同时提供了丰富的功能和配置选项。
测试:
问:在React项目中,你是如何进行单元测试和集成测试的?是否有使用任何测试工具或框架?
单元测试:
测试工具:
- Jest: Jest 是Facebook出品的测试框架,广泛用于React项目。它集成了断言库、测试运行器和覆盖率报告工具,并支持快照测试和异步测试等功能。
- React Testing Library: React Testing Library 提供了用于测试React组件的实用工具,强调模拟用户行为和测试组件的行为而非实现。
示例单元测试文件:
jsx
// MyComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyComponent from './MyComponent';
test('renders MyComponent with correct text', () => {
render(<MyComponent />);
const textElement = screen.getByText(/hello world/i);
expect(textElement).toBeInTheDocument();
});
test('clicking button triggers an action', () => {
const mockOnClick = jest.fn();
render(<MyComponent onClick={mockOnClick} />);
const button = screen.getByRole('button');
userEvent.click(button);
expect(mockOnClick).toHaveBeenCalledTimes(1);
});
集成测试:
测试工具:
- Cypress: Cypress 是一种现代的JavaScript端到端测试框架,专注于提供简单的API和强大的测试能力,用于编写和运行集成测试。
- Jest + Enzyme: Jest和Enzyme的组合也可以用于编写一些简单的集成测试。Enzyme是一个React组件测试工具,可用于测量组件的渲染和交互。
示例集成测试文件:
js
// integration.test.js
describe('Application flow', () => {
it('Visits the application, interacts with UI elements', () => {
cy.visit('/');
// Perform interactions
cy.get('input[name="username"]').type('john_doe');
cy.get('input[name="password"]').type('secure_password');
cy.get('button[type="submit"]').click();
// Assert on the result
cy.url().should('include', '/dashboard');
cy.get('h1').should('contain', 'Dashboard');
});
});
Webpack和构建工具:
问:你在React项目中使用过哪些构建工具和打包工具?请描述一下Webpack的作用,并解释一下常见的配置选项。
在React项目中,常用的构建工具和打包工具主要有 Webpack。Webpack 是一个强大的前端构建工具,它可以将项目中的各种资源(JavaScript、CSS、图片等)打包成静态文件,并提供了模块化、代码分割、懒加载等丰富的功能。
Webpack 的作用:
- 模块打包: 将项目中的各种模块(包括 JavaScript、CSS、图片等)打包成静态文件,以便在浏览器中加载。
- 模块化支持: 支持使用 ES6+ 的模块化语法,以及 CommonJS、AMD 等其他模块化规范。
- 代码分割: 可以将代码拆分成多个块,实现按需加载,减小初始加载体积,提高应用性能。
- 加载器(Loader): 可以使用加载器处理项目中的各种资源,例如 Babel 处理 JavaScript,CSS 处理样式,File Loader 处理图片等。
- 插件系统: 提供了丰富的插件系统,可以通过插件完成各种任务,如压缩代码、拷贝静态文件、生成 HTML 文件等。
- 开发服务器: 提供了内置的开发服务器,支持热更新,方便在开发阶段调试应用。
常见的 Webpack 配置选项:
-
entry: 指定项目的入口文件,Webpack会从入口文件开始构建依赖图。
javascriptmodule.exports = { entry: './src/index.js', // ... };
-
output: 配置输出文件的位置和名称。
javascriptmodule.exports = { output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, // ... };
-
module: 配置模块的加载规则和使用的加载器。
jsmodule.exports = { module: { rules: [ { test: /.js$/, exclude: /node_modules/, use: 'babel-loader', }, { test: /.css$/, use: ['style-loader', 'css-loader'], }, // ... ], }, // ... };
-
plugins: 配置使用的插件。
javascriptconst HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', }), // ... ], // ... };
-
resolve: 配置模块解析的规则。
javascriptmodule.exports = { resolve: { extensions: ['.js', '.jsx'], alias: { '@components': path.resolve(__dirname, 'src/components'), }, }, // ... };
-
devServer: 配置开发服务器。
javascriptmodule.exports = { devServer: { contentBase: './dist', port: 3000, hot: true, }, // ... };
前端工程化:
问:你是如何进行前端工程化的,有没有使用过CI/CD工具?描述一下你的项目部署流程。
前端工程化:
- 版本控制: 使用版本控制工具(如 Git)来管理代码。创建分支进行开发,确保主分支保持稳定。
- 包管理: 使用包管理工具(如 npm 或 yarn)来管理项目依赖,保证项目的依赖关系和版本得到良好的管理。
- 模块化开发: 使用模块化的开发方式,将功能模块拆分为独立的组件或模块,提高代码的可维护性和复用性。
- 代码规范: 使用代码规范工具(如 ESLint、Prettier)来保持代码风格一致,并在团队中建立一致的编码规范。
- 自动化构建: 使用构建工具(如 Webpack、Parcel)自动化构建过程,将源代码转换、打包、压缩,生成可在浏览器中运行的最终文件。
CI/CD 工具:
- 持续集成(CI): 使用 CI 工具(如 Jenkins、Travis CI、GitHub Actions)在代码仓库中配置自动化构建和测试流程。每次提交代码时,CI 工具会自动触发构建和测试。
- 持续交付(CD): 在持续集成的基础上,可以进一步配置持续交付流程,确保构建通过测试后,自动将代码部署到预发布环境或生产环境。
项目部署流程:
- 预发布环境: 在持续集成通过构建和测试后,将构建后的代码部署到预发布环境,进行更全面的测试,包括性能测试、端到端测试等。
- 生产环境: 如果预发布环境的测试通过,可以将代码部署到生产环境。这通常通过 CD 工具来自动化完成,确保部署过程的可靠性和一致性。
- 监控和回滚: 在生产环境部署后,设置监控工具来监测应用性能和稳定性。如果出现问题,可以及时回滚到之前的稳定版本。
- 灰度发布: 对于大型项目,可以考虑使用灰度发布策略,逐步将新版本引入生产环境,降低发布带来的风险。
最新技术和趋势:
问:你对React的最新版本有了解吗?是否关注React生态系统中的一些新兴技术或趋势,比如React Server Components、React Concurrent Mode等?
React Server Components:
React Server Components 是 React 团队提出的一项实验性特性,旨在改善服务器渲染和提高应用的性能。这一特性旨在让开发者能够在服务器上运行 React 组件,以实现更高效的服务器渲染。
主要特点和优势:
- 在服务器上运行: 通过将组件在服务器上运行,可以将渲染的工作分布在客户端和服务器之间,以提高性能。
- 懒加载: 可以将组件按需加载,以减小初始加载体积,加速首次渲染。
- 实时数据: 通过 Server Components,可以在服务器上获取和处理实时数据,以确保在首次渲染时使用最新的数据。
React Server Components 目前仍处于实验性阶段,需要注意在实际项目中使用时可能会有限制和变化。
React Concurrent Mode:
React Concurrent Mode 是 React 团队引入的一项功能,旨在提高应用的并发能力和用户体验。它包括一系列的新特性和 API,允许 React 应用更好地处理并发更新和异步操作。
主要特点和优势:
- 并发渲染: Concurrent Mode 允许 React 应用在同一时间处理多个更新,提高应用的并发性能。
- 渐进式加载: 支持渐进式加载,使得在加载大型组件树时,用户能够更快地看到可交互的部分。
- Time Slicing: 引入 Time Slicing 技术,将渲染工作分解为小的时间片,以确保对于用户输入的响应更加及时。
- 新的生命周期方法: Concurrent Mode 提供了一些新的生命周期方法,如
useTransition
,用于控制过渡效果。