React
React 使得 UI 开发更倾向于声明式编程,但并没有完全排除指令式编程。在实际开发中,两者往往是结合使用的,以适应不同的开发需求和场景
响应式系统与React
React的历史与应用
历史
应用
- 前端应用开发,如Facebook, Instagram,Netflix网页版
- 移动原生应用开发,如Instagram,Discord,Oculus
- 结合Electron,进行桌面应用开发
- 结合React Three Fiber库,创建和控制3D内容。
React的设计思路
UI编程痛点
- 状态更新,UI不会自动更新,需要手动地调用DOM进行更新。
- 欠缺基本的代码层面的封装和隔离,代码层面没有组件化。
- UI之间的数据依赖关系,需要手动维护,如果依赖链路长,则会遇到"Callback Hell”。
响应式与转换式
响应式系统
事件->执行既定的回调->状态变更
前端UI
事件->执行既定的回调->状态变更->UI更新
对React的期望
- 状态更新,UI自动更新。
- 前端代码组件化,可复用,可封装。
- 状态之间的互相依赖关系,只需声明即可。
组件化
- 组件是组件的组合/原子组件
- 组件内拥有状态,外部不可见
- 父组件可将状态传入组件内部
[当前价格]属于Root节点 不好 状态上升
[当前价格]由Root的函数onChangeValue()向下传递到型号选择等
- React是单向数据流->父传子状态 子改变父状态->父把函数传给子 子执行函数
组件设计
- 组件声明了状态和UI的映射
- 组件有Props/State 两种状态
- “组件”可由其他组件拼装而成
组件代码
- 组件内部拥有私有状态State
- 组件接受外部的Props状态提供复用性
- 根据当前的State/Props,返回一个UI
生命周期
React(hooks)的写法
import React, { useState } from 'react' ;
function Example() {
//声明一个新的 state 变量,我们将其称为 “count”
const [count, setCount] = useState(0);//声明一个新状态需要手动调用useState函数 这个函数传入一个状态的初始值,返回一个数组 第一个是状态本身 第二个函数是这个状态的setCount
//类似于组件生命周期中的componentDidMount和componentDidUpdate:
useEffect(() => {
//使用浏览器API更新文档标题
document.title = 'You clicked ${count} times';
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
import { Switch, Route } from "@modern-js/runtime/router";
import { useState, useEffect } from "react";
import "./App.css";
//父组件给子组件传入了一个名叫title的值
const Subcomponent = (props) => {
<h1>{props.title}</h1>
}
//useState和useEffect是Hooks
const App = () => {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const sum = x + y;
//副作用: 每次x,y变化时,更新document.title
useEffect(() => {
document.title = `${x}-${y}`;
});
return (
<Switch>
<Route exact={true} path="/">
<div className="container">
<main>
<h1>Welcome to 青训营!</h1>
<Subcomponent title={'青训营'} />
<h1>
x={x},y={y}{' '},和是{sum}
</h1>
<div className="grid">
<button type="button" onClick={() => setX(x + 1)}>
x+1
</button>
<button type="button" onClick={() => setY(y + 1)}>
y+1
</button>
</div>
</main>
</div>
</Route>
<Route path="*">
<div>
404
</div>
</Route>
</Switch>
);
}
export default App;
useEffect
传入一个函数,和一个数组,数组是状态的数组,称作依赖项,该函数在mount时,和依赖项被set的时候会执行。
useEffect是有“副作用”的函数,要传入useEffect来执行。副作用代表除了单纯的计算之外,还要做其它的一些事情,比如网络请求,更新DOM,localStorage,存储数据等。
Hook 使用法则
- 不要在循环,条件或嵌套函数中调用Hook
React的实现
问题
-
JSX 不符合 JS 标准语法
-
const Test = (props) => { const { url } = props; return ( <div className="root"> <img src={url} /> </div> ) }会自动转换成js
const Test = props => { const { url } = props; return React.createElement( "div", { className: "root" }, React.createElement("img", { src: url }) ); };
-
-
返回的 JSX 发生改变时,如何更新DOM DOM的操作比较耗费性能
-
Virtual DOM(虚拟 DOM)
Virtual DOM是一种用于和真实 DOM同步,而在JS内存中维护的一个对象,它具有和DOM类似的树状结构,并和DOM可以建立一一对应的关系。
它赋予了React 声明式的API:您告诉React 希望让 UI是什么状态,React就确保DOM匹配该状态。这使您可以从属性操作、事件处理和手动DOM更新这些在构建应用程序时必要的操作中解放出来。
-
如何计算Diff
-
权衡:更新次数少 计算速度快
-
完美的最小Diff算法,需要O(n^3)的复杂度。
牺牲理论最小Diff,换取时间,得到了O(n)复杂度的算法:Heuristic O(n) Algorithm
-
-
-
State/Props更新时,要重新触发 render 函数
React状态管理库
-
核心思想
坏处:降低组件的复用性 组件与外部数据管理的Store强耦合性
-
推荐
-
redux
- 集中式管理和严格的数据流
-
XState
- 处理复杂的状态转换和异步逻辑
-
mobx
- 更灵活和自动的状态追踪
-
recoil
- 简单且与React紧密集成的状态管理解决方案
-
-
状态机
类似红绿灯
当前状态->收到外部事件->迁移到下一个状态
-
哪些东西适合放在状态管理库
- 被全局所使用的数据放在Store
- 仅被该组件所使用的数据不用放
-
例子:
import { model } from '@modern-js/runtime/model'; //状态机 const countModel =model('count').define({ //状态共享 state: { x: 0, y: 0, }, actions: { incrementX(state) { state.x += 1; }, incrementY(state) { state.y += 1; }, }, }); export default countModel;import { Switch, Route } from "@modern-js/runtime/router"; import { useState } from "react"; import { countModel } from "./CountModel"; import "./App.css"; const Subcomponent = props => { const [{ x, y }] = useModel(countModel); return ( <h1>{x} and {y}</h1> ); } //useState和useEffect是Hooks const App = () => { const [{ x, y }, { incrementX, incrementY }] = useModel(countModel); const sum = x + y; return ( <Switch> <Route exact={true} path="/"> <div className="container"> <main> <h1>Welcome to 青训营!</h1> <Subcomponent title={'青训营'} /> <h1> x={x},y={y}{' '},和是{sum} </h1> <div className="grid"> <button type="button" onClick={() => incrementX()}> x+1 </button> <button type="button" onClick={() => incrementY()}> y+1 </button> </div> </main> </div> </Route> <Route path="*"> <div> 404 </div> </Route> </Switch> ); } export default App;
应用级框架科普
-
next.js
-
硅谷明星创业公司 Vercel的React开发框架,稳定,开发体验好,支持 Unbundled Dev, SWC 等,其同样有Serverless 一键部署平台帮助开发者快速完成部署。口号是“Let'sFaster"
-
内置的能力
-
-
MODERN.js
-
字节跳动 Web Infra 团队研发的全栈开发框架,内置了很多开箱即用的能力与最佳实践,可以减少很多调研选择工具的时间。
-
内置的能力
-
-
Blitz
- 无 API 思想的全栈开发框架,开发过程中无需写API 调用与 CRUD 逻辑,适合前后端紧密结合的小团队项目。