乐趣区

关于前端:对于前端状态相关问题如何思考比较全面

大家好,我卡颂。

最近看到个写得很不错的知乎答复 Hooks 是否过誉了?前端应该跟着 React 走还是跟着 JS、TS 走?- beeplin 的答复。

在这个答复的根底上,我想引申出一个问题 —— 对于 前端状态 相干问题,如何思考比拟全面?

明天,咱们试着从多个形象层级的角度答复这个问题。

欢送退出人类高质量前端框架群,带飞

问题的起源

有相当比例的前端从业者入行是从 学习前端框架的应用 开始的。换言之,在他们的常识体系中,最底层是 前端框架如何应用,其余业务知识都是构建于此之上。

要以此为根底答复 前端状态 相干问题,并不容易。就比方你问组长:

  • 为什么我的项目中用 Redux 而不必Mobx
  • 为什么要用 Hooks 而不必ClassComponent

很多时候失去的是一个既定的事实(就是这样,没有为什么),而不是剖析后的后果。

要剖析这类问题,咱们须要晓得一些更低形象层级的常识。

简直所有支流前端框架的实现原理,都在践行 UI = f(state) 这个公式,艰深的说 —— UI 是对状态的映射

这应该是 前端状态 会呈现的最低形象层级了,所以咱们从这个层级登程。

前端框架的实现原理

限于篇幅无限,这里咱们以最常见的 ReactVue举例。

在实现 UI 是对状态的映射 过程中,两者的方向不同。

React并不关怀状态如何变动。每当调用更新状态的办法(比方this.setState,或者useState dispatch…),就会对整个利用进行diff

所以在 React 中,传递给 更新状态的办法 的,是 状态的快照 ,换言之,是个 不可变的数据

Vue关怀状态如何变动。每当更新状态时,都会对 与状态关联的组件 进行diff

所以在 Vue 中,是间接扭转状态的值。换言之,状态是个 可变的数据

这种底层实现的区别在独自应用框架时不会有很大区别,然而会影响下层库的实现(比方状态治理库)。

当初咱们晓得,通过前端框架,咱们能够将状态映射到UI。那么如何治理好对应的映射关系呢?

换言之,如何将状态与 和他相干的 UI束缚在一起?

咱们再往更高一级形象看。

如何封装组件

前端开发广泛采纳 组件 作为 状态与 UI 的涣散耦合单元

到这里咱们能够发现,如果仅仅会应用前端框架,那么只能将组件看作是 前端框架中既定的设计

但如果从更低一层形象(前端框架的实现原理)登程,就能发现 —— 组件是为了解决框架实现原理中 UI 到状态的映射 的路径。

那么组件该如何实现,他的载体是什么呢?从软件工程的角度登程,有两个方向能够摸索:

  • 面向对象编程
  • 函数式编程

面向对象编程 的特点包含:

  • 继承
  • 封装
  • 多态

其中 封装 这一特点使得 面向对象编程 很天然成为组件的首选实现形式,毕竟组件的实质就是 将状态与 UI 封装在一起的涣散耦合单元

ReactClassComponentVueOptions API都是相似实现。

但毕竟组件的实质是 状态与 UI 的涣散耦合单元 ,在思考复用性时,不仅要思考 逻辑的复用 (逻辑是指操作状态的业务代码),还要思考UI 的复用。所以 面向对象编程 的另两个个性并不适用于组件。

框架们依据本身特点,在 类面向对象编程 的组件实现上,拓展了复用性:

  • React通过HOCrenderProps
  • Vue2通过mixin

通过长期实践,框架们逐步发现 —— 类面向对象编程的组件实现 封装 带来的益处不足以对消 复用性 上的劣势。

于是 React 引入了 Hooks,以函数作为组件封装的载体,借用 函数式编程 的理念进步复用性。相似的还有 Vue3 中的Composition API

不论是 ClassComponent 还是 FunctionComponentOptions API 还是 Composition API,他们的实质都是 状态与 UI 的涣散耦合单元

当组件数量增多,逻辑变简单时,一种常见的解耦形式是 —— 将可复用的逻辑从组件中抽离进去,放到独自的 Model 层。UI间接调用 Model 层的办法。

Model 层的治理,也就是所谓的 状态治理

对状态的治理,是比组件中 状态与 UI 的耦合 更高一级的形象。

状态治理问题

状态治理 要思考的最根本的问题是 —— 如何与框架实现原理尽可能符合?

比方,咱们要设计一个 User Model,如果用class 的模式书写:

class User {
  name: String;
  constructor(name: string) {this.name = name;}
    changeName(name: string) {return this.name = name;}
}

只须要将这个 Model 的实例包装为响应式对象,就能很不便的接入Vue3

import {reactive} from 'vue'

setup() {const user = reactive(new User('KaSong') as User;
  return () => (<button onClick={() => user.changeName('XiaoMing')}>
      {user.name}
    </button>
  )
}

之所以这么不便,诚如本文开篇提到的 —— Vue的实现原理中,状态是 可变的数据 ,这与User Model 的用法是符合的。

同样的 User Model 要接入 React 则比拟艰难,因为 React 原生反对的是 不可变数据 类型的状态。

要接入 React,咱们能够将同样的User Model 设计为不可变数据,采纳 reducer 的模式书写:

const userModel = {name: 'KaSong'};

const userReducer = (state, action) => {switch (action.type) {
    case "changeName":
      const name = action.payload;
      return {...state, name}
  }
};

function App() {const [user, dispatch] = useReducer(userReducer, userModel);

  const changeName = (name) => {dispatch({type: "changeName", payload: name});
  };

  return (<button onClick={() => changeName('XiaoMing')}>
      {user.name}
    </button>
  );
}

如果肯定要接入 可变类型状态 ,能够为React 提供相似 Vue响应式更新 能力后再接入。比方借用 Mobx 提供的响应式能力:

import {makeAutoObservable} from "mobx"

function createUser(name) {return makeAutoObservable(new User(name));
}

到目前为止,不论是 可变类型状态 还是 不可变类型状态 Model,都带来了 从组件中抽离逻辑 的能力,对于上例来说:

  • 可变类型状态 将状态与逻辑抽离到 User
  • 不可变类型状态 将状态与逻辑抽离到 userModeluserReducer
  • 最终裸露给 UI 的都仅仅是 changeName 办法

当业务进一步简单,Model自身须要更欠缺的架构,此时又是更高一级的形象。

到这一层时曾经脱离前端框架的领域,回升到纯状态的治理,比方为 mobx 带来结构化数据的mobx-state-tree

此时框架实现原理对 Model 的影响曾经在更高的形象中被抹去了,比方 Redux-toolkitReact技术栈的解决方案,VuexVue 技术栈的解决方案,但他们在应用形式上是相似的。

这是因为 ReduxVuex的理念都借鉴自 Flux,即便ReactVue在实现原理上有区别,但这些区别都被状态治理计划抹平了。

更高的形象

在此之上,对于状态还有没有更高的形象呢?答案是必定的。

对于惯例的状态治理计划,依据用处不同,能够划分出更多细分畛域,比方:

  • 对于表单状态,收敛到表单状态治理库中
  • 对于服务端缓存,收敛到服务端状态治理库中(React QuerySWR
  • 用残缺的框架收敛前后端Model,比方RemixNext.js

总结

回到咱们开篇提到的问题:

  • 为什么我的项目中用 Redux 而不必Mobx
  • 为什么要用 Hooks 而不必ClassComponent

当初咱们曾经能清晰的晓得这两个问题的相同点与不同点:

  • 相同点:都与状态相干
  • 不同点:属于不同形象层级的状态相干问题

要答复这些问题须要哪些常识呢?只须要晓得问题波及的 状态的形象层级 ,以及 比该层级更低的形象层级 对应的常识即可。

比方答复:为什么我的项目中用 Redux 而不必Mobx

思考以后形象层级

ReduxMobx 都属于 Model 的实现,前者带来一套 类 Flux 的状态治理理念 ,后者为React 带来 响应式更新 能力,在设计 Model 时我的我的项目更适宜哪种类型?

或者两种类型我都不在乎,那么要不要应用更高形象的解决方案(比方MSTRedux Toolkit)抹平这些差别?

思考低一级形象层级

我的项目用的 ClassComponent 还是 FunctionComponentReduxMobx 与他们联合应用时哪个组合更能协调好 UI 与逻辑的涣散耦合?

思考再低一级形象层级

React的实现原理决定了他原生与 不可变类型状态 更亲和。Redux更符合 不可变数据 Mobx 更符合 可变数据。我的我的项目须要思考这些差别么?

当理解不同形象层级须要思考的问题后,任何宽泛的、状态相干问题都能转化成具体的、多形象层级问题。

从不同形象层级登程思考,就能更全面的答复问题。

退出移动版