1 React特性与简介
1.1 React特点
- 声明式
- 组件化
- 跨平台编写
1.2 React哲学
等待资源加载时间和大部分情况下浏览器单线程执行是影响web性能的两大主要原因。
1.2.1 Lazy&Suspence
- React.Lazy:一次请求太多资源情况
- React.Suspense:网络请求慢情况
1.2.2 ErrorBoundary
资源加载失败情况
1.2.3 更新流程
- Scheduler 调度器
- 维护时间切片
- 与浏览器任务调度
- 优先级调度
- reconciler 协调器
- 将JSX转化为Fiber
- FIber树对比(双缓存)
- 确定本次更新的Fiber
- Renderer 渲染器
- 渲染器用于管理一棵React树,使其根据底层平台进行不同的调用
1.2.4 优缺点
- 优点
- 快速响应:Fiber
- 组件化:复用性强
- 声明式编程
- 跨平台:只需修改渲染器
- 缺点
- 大型应用需要配套学习状态管理、路由工具
- 不适合小型应用,需要用babel
2 React基础温故知新
2.1 用React开发web应用
- 架构
- 路由
- UI
- 状态
2.2 组件
- 数据
- 通过定义state操作视图
- Mount时获取数据更新state
- Ref保存与视图无直接关系的值
- unMount前清空Ref
- UI
- 数据决定视图
- 通过Ref获取到DOM
- 通信
- props parent和child组件通信
- context&redux组件信息共享
- 性能
- 函数使用 useCallback
- 值或者计算使用 useMemo
- 组件包裹 memo
2.2.1 Class组件
- 继承+构造函数
- this
- 生命周期
- render方法
2.2.2 函数式组件
- 没有生命周期
- 借助Hook
- return JSX
函数式相对于class的优点
- 代码量减少,组件简洁
- 没有复杂的生命周期
- 支持自定义Hook,逻辑复用方便
因此,现在组件用函数式多于用class
2.3 Hook规则&原理
本质:函数
2.3.1 只能在最顶层使用Hook
案例
如果将useState写进了if函数里,如果出现条件为false的情况下会跳过了该useState,但是React靠的是Hook的调用顺序来进行state与useState的对应,这种情况下后面的对应就乱套了。
正确写法:将useState写在最顶层,将if函数写在useState里。
2.3.2 只能在React函数中调用Hook
- 在React函数组件中或自定义Hook中调用
- 自定义Hook必须以use开头
- Hook中的state是完全隔离的
Hook过期闭包问题
在JS中,函数运行的上下文是由定义的位置决定的,当函数的闭包包住了旧的变量值时,就出现了过期闭包问题。
解决方法:依赖
3 具体场景案例
如何划分组件
1 Layout
- Navbar
- Main Content
- Footer
- Floating Button
2 Page(Main Content)
- Banner
- Card Group
- Slider Group
- Help Docs
- Footer Banner
3 Component
- BlockHeader
- tag
- title
- BlockWrapper
- backgroundType
- animate?:boolen;
- theme?:"dark"|"light"
- SlideButton
- onClick(ditection,index)
- icon?:ReactNode
- animate?:boolean
- AnimationWrapper
- animationName?:string
- animationDuration?:number
- SizeText
- 通过className控制
- 组装成组件
- 安装主题包
案例
1.parent组件给child组件通信(SizeWrapper)
//SizeWrapper
<div>
{React.Children.map(children,(child)=>{
if (child && child.props){
return React.cloneElement(child,commonProps);
}
return child;
})}
</div>
//Before
<div>
<Input value="input" size={props.size}/>
<InputTag value={['input-tag]} size={props.size} />
<InputNumber value={100} size={props.size}/>
<Button type="primary" size={props.size}>
Primary
</Button>
</div>
//After
<SizeWrapper size={props.size}>
<Input value="input" />
<InputTag value={['input-tag']}/>
<InputNumber value={100}/>
<Button type="primary">Primary</Button>
</SizeWrapper>
- 知道子组件表现:通过props传递
- 不知道:props.children
2.child组件给parent组件通信
//FormComponent
const FormComponent=(props,ref)=>{
const [formInstance]=Form.useForm();
//给ref添加change,clear方法
useImperativeHandle(ref,()=>({
change:()=>formInstance.setFieldsValue(newValue),
clear:()=>formInstance.clearFields(['username','email'])
}));
return(
<Form form={formInstance}>
<FormIten label="Username" field="username">
<Input placeholder="please enter your username..."/>
</FormIten>
// ...
</Form>
);
};
//转发ref
const FormComponentWithRef = forwardRef(FormComponent);
//UserSettings
const FormWrapper=()=>{
const formRef = useRef(null);
return(
<div>
<div>
<b>Wrapper Operations:</b>
<Button onClick={()=>formRef.current?.change()}>
Change
</Button>
<Button onClick={()=>formRef.current?.change()}>
Clear
</Button>
</div>
<FormComponentWithRef ref={formRef} />;
</div>
);
};
parent组件提供一个介质,child组件将数据放进介质里传给parent
3.组件间共享信息
利用公共容器store来共享信息
- 定义Reducer
- 传递context
- set loginUser
- 用usecontext
- 用props
案例
问题1:如何在Count不变的场景下,让Counter不重新渲染?
- api:shouldComponentUpadate() return一个false
- hook:用memo包一下组件
问题1.1:没有两个完全相等的对象,increment两次渲染不完全相等,如何让其相等?
- 用useCallback
问题2:Count不变的场景下,如何让calcValue不重新计算?
- 用useMemo
5 个人感想和总结
- 只学过vue没学过react,虽然也有共通(或者说同名)的地方,但是还是有挺多不同点的,消化起来需要时间
- 一个从portal内部触发的事件会一直冒泡至包含React树的祖先,即便这些元素并不是DOM树中的祖先