# 基础知识点

# 1.状态

简单来说setStateuseState中的返回函数执行时,会将参数放在当前fiber任务队列上,通过参数控制是否执行任务队列,没有执行当前任务队列会在后续循环更新时执行,所以改变状态的过程是否是同步异步不一定

  • 1.类组件setState:参数与回调函数会存储到当前fiberqueue中,是否执行queue取决于参数excuteContext,当setState在合成事件中执行时此参数会变成EventContext执行完成后又恢复原状,所以此时不会继续执行queue中的任务,当用异步任务包裹、用原生事件会执行此处任务
  • 2.函数组件useState:初始化时将结果存储到fiber上,后续更新走另外一个逻辑会拿之前的参数来更新,过程与setState类似,通过excuteContext控制任务流程

# 2.生命周期

  • 1.类组件
    • 1.创建:constructorgetDerivedStateFromPropsrendercomponentDidMount
    • 2.更新:getDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdate
    • 3.销毁:componentWillUnmount
    • 4.错误:getDerivedStateFromeErrorcomponentDidCatch
  • 2.函数组件(useLayoutEffect)
    • 1.创建:useEffect+[]
    • 2.更新:useEffect+[params]
    • 3.销毁:useEffect+return
    • 4.错误:useDebugValue

# 3.优化

  • 1.类组件:shouldComponentUpdate
  • 2.函数组件:memouseMemouseCallback

# 4.通信

  • 1.类组件:createContextprops
  • 2.函数组件:useContextprops

# 5.操作子组件

  • 1.类组件:createRef
  • 2.函数组件:useRefuseImperativeHandle

# 2.hooks

  • useReducer
  • useRef
  • useImperativeHandle

# 3.性能优化

shouldComponentUpdate函数中我们可以通过返回布尔值来决定当前组件是否需要更新。这层代码逻辑可以是简单地比较一下当前state和之前的state是否相同,也可以是判断某个值更新了才触发组件更新。一般来说不推荐完整对比当前state和之前的state是否相同,因为组件更新触发可能会很频繁,这样的完整对比性能开销会有点大,可能会造成得不偿失的情况。

当然如果只是单纯的浅比较一下,可以直接使用PureComponent,底层就是实现了浅比较state

class Test extends React.PureComponent {
  render() {
    return <div>PureComponent</div>
  }
}
1
2
3
4
5

这时候你可能考虑到函数组件就不能使用这种方式了,如果你使用 16.6.0 之后的版本的话,可以使用React.memo来实现相同的功能

const Test = React.memo(() => {
  ;<div>PureComponent</div>
})
1
2
3

通过这种方式我们就可以即实现了shouldComponentUpdate的浅比较,又能使用函数组件。

# 4.通信

其实 React 中的组件同行基本和 Vue 中的一致。同样也分为以下三种情况

  • 父子组件通信
  • 兄弟组件通信
  • 跨多层组件通信
  • 任意组件 父子通信

父组件通过props传递数据给子组件,子组件通过调用父组件传来的函数传递数据给父组件,这两种方式是最常用的父子通信实现办法。

这种父子通信方式也就是典型的单向数据流,父组件通过props传递数据,子组件间不能直接修改props,而是必须通过调用父组件函数的方式告知父组件修改数据。

兄弟组件通信 对于这种情况可以通过共同的父组件来管理状态和事件函数。比如说其中一个兄弟组件调用父组件传递过来的事件函数修改父组件中的状态,然后父组件将状态传递给另一个兄弟组件。

跨多层次组件通信 可以使用 Context API

//创建Context ,可以在开始就传入值
const StateContext = React.createContext()
class Parent extends React.Component {
  render() {
    return <StateContext.Provider value="yck"></StateContext.Provider>
  }
}
class Child extends React.Component {
  render () {
    return (
      <ThemeContext.Consume>{
        context => (
          name is { context}
        )
      }</ThemeContext.Consume>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

任意组件 这种方式可以通过 Redux 或者 Event Bus 解决,另外如果你不怕麻烦的 haul,可以使用这种凡是解决上述所有的通信。

# 5.react 和 vue 的区别

  • 双向数据绑定

  • 修改数据,react 中 setState

  • 相同点:

    • 1.数据驱动页面,提供响应式的视图组件
    • 2.都有 virtual DOM 组件化的开发,通过 props 参数进行父子之间的组件传递数据,都实现了 webComponents 规范
    • 3.数据流动单项,都支持服务器的渲染
    • 4.都有支持 native 的方法,react 有 react native,vue 有 wexx
  • 不同点:

    • 1.数据绑定:Vue 实现了双向的数据绑定,react 数据流动是单项的
    • 2.数据渲染:大规模的数据渲染,react 更快
    • 3.使用场景:react 配合 redux 架构适合大规模多人协作复杂项目,vue 适合小快的项目
    • 4.开发风格:react 推荐做法 jsx + inline style 把 html 和 css 都写在 js 了;vue 是采用 webpack + vue-loader 单文件组件格式,html,js,css 同一个文件

# 6.redux 中的 reducer(纯函数)

  • redux 数据流里,reduces 其实是根据之前的状态(previous state)和现有的 action(current action)更新 state(这个 state 可以理解为上下累加器的结果)
  • 每次 redux reducer 被执行时,state 和 action 被传入,这个 state 根据 action 进行累加或者是自身消减,进而返回最新的 state,这也就是典型 reduce 函数的用法:state -> action -> state

# 7.react 的 refs

  • refs 就像一个逃生窗,允许我们之间访问 dom 元素或者组件实例,可以向组件添加一个 ref 属性的值是一个回调函数,它将接受的 dom 元素或组件的已挂载实列,作为第一个参数

# 8.react 中的 keys

  • 帮助我们跟踪那些项目已更改、添加、从列表中删除,key 是独一无二的,可以让我们高效的去定位元素,并且操作它

# 9.React 的生命周期

  • 三个状态:
    • Mounting(已插入真实的 DOM)
    • Updating(正在被重新渲染)
    • Unmounting(已移除真实的 DOM)
  • comonentDidMount 在第一次渲染后调用,只在客户端。之后组件已经生成对应的 DOM 结构
  • componentDidUpdate 在组件完成更新后立即调用,再初始化是不会调用的

# 10.子组件向父组件传值

  • 父组件通过 props 给子组件传递数据,子组件则是通过调用父组件给它的函数给父组件传递数据

# 11.为什么虚拟 DOM 会提高性能

  • 虚拟 DOM 相当于在 js 和真实 DOM 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能
  • 具体实现步骤:
    • 用 JavaScript 对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档中
    • 当状态变更的时候,重新构建一颗树的对象树,然后用新的和旧的树进行对比,记录两颗树的差异
    • 把所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了

# 12.diff 算法

  • 1.把树形结构按照层级分解,只比较同级元素
  • 2.给列表结构的每个单元添加 key 属性,方便比较,在实际代码中,会对新旧两棵树进行一个深度优先遍历,这样每个节点都会有一个标记
  • 3.在深度优先遍历的时候,每遍历到一个节点就把该节点和新的树进行对比,如果有差异的话就记录到一个对象里面。Virtual DOM 算法主要实现上面步骤的三个函数:element,diff,path.然后就可以实际的进行使用。react 只会匹配相同的 class 的 component(这里的 class 指的是组件的名字)。合并操作,调用 component 的 setState 方法的时候,React 将其标记为 dirty 到每一个时间循环,React 检查所有标记 dirty 的 component 重新绘制
  • 4.选择性子树渲染。可以重写 shouldComponentUpdate 提高 diff 性能

# 10.怎么划分业务组件和技术组件

  • 根据组件的职责通常把组件分为 UI 组件和容器组件
  • UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑
  • 两者通过 React-redux 提供 connect 方法联系起来

# 11. setState

  • setState 通过一队列机制实现 state 更新,当执行 setState 时,会将需要更新的 state 往后放入状态队列

  • 那么该 state 将不会被放入状态队列中。当下次调用 setState 并对状态队列进行合并时,就会忽略之前修改的 state,造成不可预知的错误

  • 同时,也利用了队列机制实现了 setState 的异步更新,避免了频繁的重复更新 state

  • 同步更新 state:

    • setState 函数并不会阻塞等待状态更新完毕,因此 setNetworkActivityIndicatoryViseible 有可能先于数据渲染完毕就执行。第二个参数是一个回调函数,在 setState 的异步操作结束并且组件已经重新渲染的时候执行,也就是说,我们可以通过这个回调来拿到更新的 state 的值,实现代码的同步
    • 例子
    componentDidMount(){
        fetch('http://localhost:3000')
        .then(res=>res.json())
        .then(data=>{
            this.setState({data:data})
        })
    }
    StatusBar.setNetworkActivityIndicatorVisible(false)
    
    1
    2
    3
    4
    5
    6
    7
    8
上次更新: 2024/7/23 上午7:48:41