React 基础与实践 | 青训营笔记

80 阅读4分钟

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 调度器
  1. 维护时间切片
  2. 与浏览器任务调度
  3. 优先级调度
  • reconciler 协调器
  1. 将JSX转化为Fiber
  2. FIber树对比(双缓存)
  3. 确定本次更新的Fiber
  • Renderer 渲染器
  1. 渲染器用于管理一棵React树,使其根据底层平台进行不同的调用
1.2.4 优缺点
  • 优点
  1. 快速响应:Fiber
  2. 组件化:复用性强
  3. 声明式编程
  4. 跨平台:只需修改渲染器
  • 缺点
  1. 大型应用需要配套学习状态管理、路由工具
  2. 不适合小型应用,需要用babel

2 React基础温故知新

2.1 用React开发web应用

  • 架构
  • 路由
  • UI
  • 状态

2.2 组件

  1. 数据
  • 通过定义state操作视图
  • Mount时获取数据更新state
  • Ref保存与视图无直接关系的值
  • unMount前清空Ref
  1. UI
  • 数据决定视图
  • 通过Ref获取到DOM
  1. 通信
  • props parent和child组件通信
  • context&redux组件信息共享
  1. 性能
  • 函数使用 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
  1. tag
  2. title
  • BlockWrapper
  1. backgroundType
  2. animate?:boolen;
  3. theme?:"dark"|"light"
  • SlideButton
  1. onClick(ditection,index)
  2. icon?:ReactNode
  3. animate?:boolean
  • AnimationWrapper
  1. animationName?:string
  2. animationDuration?:number
  • SizeText
  1. 通过className控制
  2. 组装成组件
  3. 安装主题包

案例

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
  1. 用usecontext
  2. 用props

案例

问题1:如何在Count不变的场景下,让Counter不重新渲染?

  • api:shouldComponentUpadate() return一个false
  • hook:用memo包一下组件

问题1.1:没有两个完全相等的对象,increment两次渲染不完全相等,如何让其相等?

  • 用useCallback

问题2:Count不变的场景下,如何让calcValue不重新计算?

  • 用useMemo

5 个人感想和总结

  • 只学过vue没学过react,虽然也有共通(或者说同名)的地方,但是还是有挺多不同点的,消化起来需要时间
  • 一个从portal内部触发的事件会一直冒泡至包含React树的祖先,即便这些元素并不是DOM树中的祖先