大家好,我卡颂。

长期运用React的同学应该知道,React中存在两种组件:

  • Class Component,类组件

  • Function Component,函数组件

已然说到函数,那么很天然的,咱们会进一步考虑:

  • 类组件和OOP(面向对象编程)有联系么?

  • 函数组件和FP(函数式编程)有联系么?

究竟,如果类组件和OOP有关,那么OOP中的思维(承继、封装、多态…)也能辅导类组件的事务开发(函数组件与FP的联系同理)。换言之,咱们能够直接用这些编程范式的最佳实践辅导React项目开发。

那么,函数组件函数式编程究竟是什么联系呢?本文会环绕这个论题打开讲解。

欢迎围观朋友圈、参加人类高质量前端交流群,带飞

编程范式与DSL

首先,咱们应该清晰,结构语法本质是一种DSL(领域相关语言),他是为了某个特定领域的开发量身定制的。

比方,React作为一款针对view开发DSL,尽管不同的view运用的结构不同,比方:

  • 对于web,结构为ReactDOM

  • 对于小程序,结构为Taro

  • 对于原生开发,字节内部有个叫React Lynx的结构

但这些结构都大体遵从同一套DSLReact语法),这套DSL并不归于某一种编程范式,而应该被视为不同编程范式中,更符合view开发的语言特性的调集

所以,作为React DSL的一部分,函数组件能够表现OOP的思维,类组件也能表现FP的思维。只需这些思维有利于view开发,就能够归入DSL的语法中。

比方,下面的函数组件Header,是由WelcomeMessageLogoutButton组件组合而成,这是OOP中的组合优于承继思维:

function Header(props) {
  return (
    <div>
      <WelcomeMessage name={props.name} />
      <LogoutButton onClick={props.onLogout} />
    </div>
  );
}

再比方,下面的类组件Cpn中,要改动状况count,并不是经过突变(相似this.state.count++),而是调用this.setState,传入不行变数据:

class Cpn extends React.Component {
  // ...
  onClick() {
    const count = this.state.count;
    this.setState({count: count + 1});
  }
  render() {
    // ...
  }
}

运用不行变数据归于FP中的思维。

所以,当咱们要深入了解某个React特性时,应该以如下次序递进的考虑:

  1. React的开发理念是什么?

  2. 为了完成这套理念,吸收了哪些编程范式中的思维

  3. 这些思维如安在React中落地

如果咱们用上述考虑进程研究函数组件与函数式编程的联系,会发现:

  • 函数组件归于落地的产品(上述考虑的第三步)

  • 函数式编程归于编程范式(上述考虑的第二步)

这便是两者的联系 —— 函数组件归于多种编程范式(主要是OOPFP)在React中终究的落地产品,其间借鉴了一部分FP的思维。

咱们不应该将函数组件单纯视为FPReact中的具象表现。

那么,函数组件究竟是如何演进而来的呢?

函数组件的演进

让咱们依照上述三步演进次序考虑。首先,React的开发理念饯别了如下公式(即:UI是数据快照经过函数映射而来):

UI = fn(snapshot)

要落地这个理念,有两个要素需求完成:

  • 数据快照

  • 函数映射

在这儿,FP不行变数据更适合作为数据快照的载体,所以React中状况是不行变的,因为状况的本质是快照。

函数映射的载体则没有特殊要求。在React中,每次触发更新,全部组件都会从头renderrender的进程便是函数映射的进程,输入是propsstate,输出是JSX

React相对的,Vue中组件则更符合OOP的理念,考虑如下App组件:

const App = {
  setup(initialProps) {
    const count = reactive({count: 0})
    const add = () => { count.value++ }
    return {count, add}
  }
  template: "...省掉"
}

组件的setup办法只会在初始化时履行一次,后续触发更新时操作的都是同一个闭包中的数据。这儿面的闭包便是OOP思维中的实例。

已然React函数映射的载体没有特殊要求,那么类组件、函数组件都是能够的。

那为什么函数组件终究替代了类组件成为React开发的主流呢?许多同学以为函数组件的Hooks能够更好的复用逻辑这一点,是函数组件优于类组件的主要原因。

但实际上,根据装饰器的类开发形式早已被验证是优异的逻辑复用形式,类组件合作TS装饰器的形式是行得通的。

主要原因还是 —— 函数组件能够更好的落地UI = fn(snapshot)这一理念。

方才说过,公式中的snapshot快照的意义。在React中,快照主要包括三类数据:

  • state

  • props

  • context

对于同一个组件,根据公式UI = fn(snapshot),相同的快照输入应该取得相同输出(JSX)。

但状况更新也或许触发副作用,比方恳求数据、操作DOM

在类组件中,这些副作用逻辑被涣散在各个生命周期钩子函数中,React无法掌控。

而在函数组件中:

  • 副作用受限在useEffect中。每次renderReact都会保证上次的副作用效果现已被铲除(经过useEffect回调的返回值函数)

  • ref的传播也需求借由forwardRef,这进一步限制了ref或许的影响范围

  • 数据恳求的副作用被交给Suspense处理,考虑下面组件:

function UserList({id}) {
  // 异步恳求数据
  const data = use(fetchUser(id));
  // ...
}

运用时:

<Suspense fallback={<div>加载中...</div>}>
  <UserList id={1}/>
</Suspense>

总而言之,运用函数组件时,全部副作用都处于一种遭到管控的状况,能够尽或许保证每次更新时相同的快照输入,取得相同的JSX输出,所以函数组件在React中才会发扬光大。

同时,这也符合了FP中的纯函数思维。

总结

函数组件并不是函数式编程React中的详细完成,而是React的规划理念UI = fn(snapshot)落地的最好载体。

React中,还吸收了其他编程范式中的优异思维。FP仅仅其间影响React最深的一种。究竟,全部落地都是为了饯别开始的规划理念。