对比学习react, 以vue 为例
Vue3 的双向绑定: state.xxx = xxx => Proxy set => trigger(触发更新) => effect (执行副作用函数) => 更新dom
React(类组件):onClick => dispatch => enqueueSetState(更新队列) => scheduleUpdateOnFiber(标记fiber节点,启动更新)
学习的时候尽量结合ts一起
类组件与函数组件(函数组件的开发效率更高,用的也更多一些)
类组件:
interface AppState { name: string, value: string } class App extends React.Component<AppState> { state:AppState = { name:'react', value: '类组件' }; render():React.ReactNode { return <div>{`我是${this.state.name + this.state.value}`}</div> } } ```
函数组件:
interface AppState { name: string, value: string } const App = () => { const [myState, setState] = useState<AppState>({name: 'react', value : '函数组件'}) return ( <div className="content"> <h1>Rsbuild with React</h1> <p>{`我是${myState.name + myState.value}`}</p> </div> ); };
受控组件与非受控组件(以表单元素举例,自定义组件也适用)
- 受控组件(指表单元素的值由React组件的state完全控制的组件)
const App= () => { const [name, setName] = useState<string>('') const handleChange = ():void => { setName('张三') } return ( <div className="content"> <input value={name} onClick={handleChange}></input> </div> ); };
- 非受控组件(表单元素的值由DOM自身管理,而不是由React state控制的组件)
const App= () => { const inputRef = useRef<HTMLInputElement>(null); const handleChange = ():void => { console.log(inputRef.current?.value) } return <div> <input ref={inputRef}></input> <button onClick={handleChange}>获取input值</button> </div> };
- 受控组件(指表单元素的值由React组件的state完全控制的组件)
props 与 state
interface PersonState { name: string, age: number, } interface PersonComProps { person: PersonState } //props 外部传入使用 const PersonCom:React.FC<PersonComProps> = (props) => { return <div> <span>姓名: {props.person.name}</span> <span>年龄:{props.person.age}</span> </div> } const App= () => { //通过useState hooks 声明state const [person, setPerson] = useState<PersonState>({name: '张三',age: 18}) const changePerson = ():void => { setPerson({name: '王五',age: 20}) } return <div> <PersonCom person ={person} /> <button onClick={changePerson}>切换人物</button> </div> };
条件渲染与列表(React 非常灵活,编写jsx的时候可以就当作我们在写js)
interface User { name: string } const App = () => { const [show, setShow] = useState<boolean>(true); const [list] = useState<User[]>([{name:'张三'},{name:'李四'},{name:'王五'}]) const handleChange = (): void => { setShow(!show); }; return ( <div> {/* 三元判断渲染 */} <div>{show ? "展示这个" : "展示另一个"}</div> <button onClick={handleChange}>切换展示</button> {/* list循环渲染 */} {list.map((item,index) => <div key={index}>{item.name}</div>)} </div> ); };
React 生命周期(这里描述的是19版本的类组件; 函数组件大多数使用useEffect、useMemo等hooks模拟生命周期操作)
- 挂载阶段
constructor
(初始化数据)- 调用 super(props) 继承React组件
- 初始化组件的state状态
- 为方法绑定this上下文(解决this指向问题)
- 创建ref引用等准备工作
static getDerivedStateFromProps
(在渲染前根据新的props来更新state)- 本质上是一个静态函数 传入state 和 props ,返回值会和当前的state 合并
render
(渲染)- 返回JSX描述UI结构
- 必须是纯函数,不能修改state或与DOM交互
- 准备虚拟DOM,为后续的DOM更新做准备
componentDidMount
(组件挂载完成, 这里一般可以请求接口)
- 更新阶段
static getDerivedStateFromProps
shouldComponentUpdate
(是否应该更新)- 比较新旧props和state
- 返回true允许更新,返回false阻止更新
- 性能优化操作
render
getSnapshotBeforeUpdate
(获取更新前快照,React更新过程中会通过计算虚拟dom去更新真实dom,这使得我们会丢失一些想保存的状态,例如聊天室中当前的滚动位置,可以通过此声明周期将位置信息传给 componentDidUpdate)// 在聊天室中,新消息到来时保持当前滚动位置 getSnapshotBeforeUpdate(prevProps, prevState) { if (prevProps.messages.length < this.props.messages.length) { // 捕获滚动容器的滚动高度 return this.chatContainer.scrollHeight - this.chatContainer.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { if (snapshot !== null) { // 根据之前计算的滚动位置调整UI this.chatContainer.scrollTop = this.chatContainer.scrollHeight - snapshot; } }
componentDidUpdate
(更新完成)
- 卸载阶段
componentWillUnmount
(组件将要卸载)- 事件监听函数/定时器的销毁操作
static getDerivedStateFromError
(捕获子组件的渲染错误)componentDidCatch
(捕获子组件的js错误)- 异常兜底操作,防止白屏
- 挂载阶段
React 事件相关
- 阻止默认事件:
e.preventDefault();
- 阻止事件传播:
stopPropagation();
- dom绑定事件
const handleListen = (): void => { console.log('focus了') }; useEffect(() => { inputRef.current?.addEventListener("focus", handleListen); return () => { inputRef?.current?.removeEventListener("focus", handleListen); }; }, []);
- 自定义DOM事件
- 使用
new CustomEvent
来创建自定义事件 - 使用
document.dispatchEvent
触发自定义事件
- 使用
const App = () => { //自定义事件hooks const useCustomEvent = (eventName: string, detail: any) => { const dispathEvent = useCallback( (customDetail?: any) => { const event = new CustomEvent(eventName, { detail: customDetail || detail, }); document.dispatchEvent(event); }, [eventName, detail] ); return dispathEvent; }; const dispathMyEvent = useCustomEvent("myEvent", "hello word"); const handleMyEvent = (): void => { console.log("触发了自定义事件"); }; useEffect(() => { //监听事件 document.addEventListener("myEvent", handleMyEvent); return () => { document.removeEventListener("myEvent", handleMyEvent); }; }, []); return ( <div> {/* //触发自定义事件 */} <button onClick={() => dispathMyEvent()}>触发自定义事件</button> </div> ); };
- 阻止默认事件: