什么是React
React是一个用于构建用户界面的JavaScript库。
传统构建页面的方式
<script>document.getElementById('app').addEventListener('click', () => {console.log('')});const div = docuemnt.createElement('div')// ...
</script>
早期,用JavaScript搭配HTML操作DOM来构建页面。
React构建页面的方式
import React from 'react'; // React组件是一个函数,它接收props作为参数,并返回React元素
function MyComponent() { // 事件处理函数 const handleClick = () => { console.log('App was clicked!'); }; // 使用React.createElement创建React元素 // 注意:React.createElement的第一个参数是元素的类型(字符串或React组件), // 第二个参数是props对象(可选),第三个参数是子元素(可以是字符串、数组或React元素) const appDiv = React.createElement('div', { id: 'app', onClick: handleClick, }, 'Click me!'); // 另一个React.createElement的例子来创建另一个div const dynamicDiv = React.createElement('div', null, 'Hello from React div!'); // 返回所有React元素作为组件的JSX结构 // 在JSX中,你可以直接嵌套React元素,而不需要显式调用React.createElement return React.createElement('div', null, appDiv, dynamicDiv);
} // 导出组件以便在其他地方使用
export default MyComponent;
React.createElement和docuemnt.createElement不同,React.createElement用于创建React元素(虚拟DOM元素),这些元素不是真正的DOM节点,而是JavaScript对象。React元素是React库用于计算最小DOM变更所需的数据结构。
import React from 'react'; function MyComponent() { const handleClick = () => { console.log('App was clicked!'); }; return ( <div> <div id="app" onClick={handleClick}> Click me! </div> <div>Hello from React div!</div> </div> );
} export default MyComponent;
一般来说,我们会使用JSX语法构建虚拟DOM树。
React工作原理
JSX编译
构建或编译过程中,JSX通过Babel等工具被转换成React.createElement
函数调用。
虚拟DOM(VDOM)的创建
React.createElement
根据JSX转换的结果创建虚拟DOM对象。虚拟DOM是一个轻量级的JavaScript对象,表示UI的结构和内容。
Fiber架构与双缓存机制
React引入了Fiber架构,将组件树转换为Fiber树,每个Fiber节点代表一个工作单元。Fiber架构还引入了双缓存机制,即当前屏幕上的UI树(current tree)和正在计算的新的UI树(work-in-progress tree)。
Reconciler(协调器)与Diffing算法
当状态或props变化时,React会触发重新渲染。Reconciler通过Diffing算法比较新旧两棵Fiber树,找出差异,生成差异列表,即需要更新、删除或添加的DOM操作。
提交阶段
根据Reconciler生成的差异列表,React进入提交阶段。在这个阶段,React会将变化应用到实际的DOM上,包括插入、更新或删除DOM节点。
生命周期方法、hooks和状态管理
在组件的挂载、更新和卸载过程中,会触发相应的生命周期方法,如componentDidMount
、componentDidUpdate
等。在16.8后的React中,推荐使用Hooks(如useEffect
、useState
等)来处理组件的生命周期和状态。这其中还包含了状态管理,React提供了Context API用于在组件树中共享数据,而无需手动逐层传递props。对于复杂的状态管理,React社区还提供了如Redux、MobX等状态管理库。
错误处理与边界
React提供了错误边界(Error Boundaries)的概念,用于捕获并打印发生在子组件树任何位置的JavaScript错误,并且,它会阻止这些错误冒泡至更高的树级,导致整个React组件树崩溃。
服务端渲染(SSR)与静态网站生成
React支持服务端渲染,允许在服务器上预先渲染组件并发送到客户端,提高首屏加载速度。还可以与Next.js等工具结合,生成静态网站,提升性能和安全性。
异步渲染与并发模式
React18引入了并发模式,这一机制的核心在于让React能够根据任务的优先级进行调度,确保关键操作(如键盘输入、点击响应等)能够快速完成,同时将非关键任务(如数据加载、图片懒加载等)延后处理。
创建React工程
npx create-react-app demo-app
npx create-react-app demo-app-ts --template typescript
React的基本能力
父子组件
import React, { Component } from 'react'export default class ClassComponent extends Component {render() {return (<div />)}}
import React from 'react'export default function FuncComponent() {return (<div />)
}
import './App.css'
import ClassComponent from './basic/ClassComponent'
import FuncComponent from './basic/FuncComponent'function App() {return (<div id='app'><ClassComponent /><FuncComponent /></div>)}export default App
State和Props
Props定义:Props(Properties的缩写)是React组件的输入参数,它们是从父组件传递到子组件的。
特点:
1、Props是只读的,不应该在子组件内部被修改。
2、它们允许父组件向子组件传递数据或配置。
3、通过Props,父组件可以控制子组件的行为和显示内容。
function Welcome(props) { return <h1>Hello, {props.name}</h1>;
} // 使用Welcome组件并传递name属性
<Welcome name="React" />
State定义:State是React组件内部的私有数据,用于记录组件自身的状态。当状态发生变化时,组件会重新渲染。
特点:
1、State是组件私有的,只能通过组件内部的方法(如setState
)来更新。
2、State的更新可能会导致组件的重新渲染。
3、通常用于处理用户交互、动画等需要动态变化的情况。
class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; // 初始化state } render() { return ( <div> <p>Clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); }
}
生命周期
类组件
初始化阶段
constructor 执行
初始化 state, 初始化一些其他的数据
constructor(props) {super(props);this.state = {number: 0,msg: 'hello world'}
}
getDerivedStsteFromProps(props, state)
本质是一个静态函数,传入 props 和 state, 返回值会和当前的 state 合并。
componentWillMount()(React 16.3废弃)
如果我类中,已经有了 getDerivedStsteFromProps 这个生命周期,则不会执行。
render()
这是类组件中必须实现的方法。它返回代表组件UI的React元素。此方法不应包含任何副作用,并应始终保持纯净。
componentDidMount()
在组件挂载到DOM后立即调用。这是执行需要DOM节点的操作(如AJAX请求或订阅事件)的好地方。
更新阶段
getDerivedStateFromProps(props, state)
如前所述,在更新阶段也会调用此方法,允许基于新的props来更新状态。
shouldComponentUpdate(nextProps, nextState)
此方法允许组件根据新的props或state来决定是否需要重新渲染。如果它返回false,则不会触发后续的render和componentDidUpdate方法。
componentDidMount()(React 16.3废弃)
render()
与挂载阶段相同,此方法用于返回代表组件UI的React元素。
getSnapshotBeforeUpdate(prevProps, prevState)
在DOM更新之前被调用,允许捕获一些DOM信息(如滚动位置)。它的返回值将作为componentDidUpdate的第三个参数。
componentDidUpdate(prevProps, prevState, snapshot)
在更新后立即调用。这是执行依赖于DOM的操作或根据prevProps和prevState进行状态更新的好地方。
卸载阶段
componentWillUnmount()
在组件从DOM中卸载并销毁之前调用。这是执行任何必要的清理操作(如取消网络请求或清除定时器)的好地方。
函数组件
useEffect
有点像vue的watch
useEffect(() => destory, deps)
1、Callback: () => destory ,第一个参数,是一个函数。
2、destroy: 作为 callback的返回值,在下一次 callback 执行之前调用,用于清楚上一次 callback的副作用。
3、deps:依赖数组,如果数组中数据的值改变,执行上一次 callback 返回的 destory,再执行callback 函数
4、Effect是如何模仿生命周期的
import React, { useEffect, useState } from 'react'export default function FuncLifeCycle(props) {const [state, setState] = useState(() => {console.log('getDerivedStateFromProps')return true;})useEffect(() => {// TODO: 请求请求数求数据数据console.log('componentDidMount');return () => {// TODO: 解除一些事件监听console.log('componentWillUnmount')}}, []);useEffect(() => {console.log("componentWillReceiveProps")}, [props])useEffect(() => {console.log("componentDidUpdate")})return (<div><h2>模仿生命周期</h2></div>)
}