大家好,我是Kason。最近看到一个写的很好的知乎回答。Hooks被高估了吗?前端应该跟React还是跟JS、TS?-beeplin的回答。在这个回答的基础上,我想提出一个问题——如何更全面地思考前端身份相关的问题?今天,我们尝试从多个抽象层次来回答这个问题。欢迎加入人类优质前端框架群。飞车问题的起源有相当一部分前端从业者是从学习前端框架的使用开始的。也就是说,在他们的知识体系中,最底层是如何使用前端框架,其他的业务知识都是建立在其之上的。在此基础上回答有关前端状态的问题并不容易。比如你问组长:为什么在项目中使用Redux而不是Mobx?为什么使用Hooks而不是ClassComponent?很多时候得到的是一个既定的事实(就是这样,没有为什么),而不是分析的结果。分析这类问题,需要了解一些较低抽象层次的知识。几乎所有主流前端框架的实现原理都在践行UI=f(state)这个公式。通俗地说——UI是状态的映射。这应该是前端状态会出现的最低抽象层次,所以我们从这个层次开始。前端框架的实现原理限于篇幅。这里我们以最常见的React和Vue为例。在实现UI是状态映射的过程中,两个方向是不同的。React不关心状态如何变化。每当调用更新状态的方法(例如this.setState或useStatedispatch...)时,整个应用程序都会被diff。所以在React中,传递给更新状态的方法的是状态的快照,换句话说,是不可变数据。Vue关心状态如何变化。每当更新状态时,与状态相关联的组件就会被区分。所以在Vue中,直接改变状态的是值。换句话说,状态是可变数据。这种底层实现的差异在单独使用框架时不会有太大的区别,但是会影响上层库(比如状态管理库)的实现。现在我们知道使用前端框架,我们可以将状态映射到UI。那么如何管理对应的映射关系呢?换句话说,您如何将状态与其关联的UI联系起来?让我们看看更高层次的抽象。如何封装组件前端开发一般将组件作为状态和UI的松散耦合单元。至此我们可以发现,如果只使用前端框架,只能将组件视为前端框架中既定的设计。但是如果从更底层的抽象(前端框架的实现原理)出发,你会发现组件是框架实现原理中解决UI到状态映射的方式。那么组件如何实现,它的载体是什么?从软件工程的角度来看,有两个方向可以探索:面向对象编程函数式编程面向对象编程的特点包括:继承、封装和多态。封装的特性使得面向对象编程自然而然地成为了组件的首选实现方式。组件的本质是一个松耦合的单元,封装了状态和UI。React的ClassComponent和Vue的OptionsAPI是类似的实现。但毕竟组件的本质是松耦合的状态和UI单元。在考虑复用性的时候,不仅要考虑逻辑的复用(逻辑是指运行状态的业务代码),还要考虑UI的复用。所以面向对象编程的另外两个特性不适用于组件。框架根据自身特点,在类面向对象编程组件的实现上扩展了可重用性:React通过HOC,renderPropsVue2通过mixin经过长期的实践,框架逐渐发现在类面向对象编程中的封装组件实现带来的好处不足以抵消可重用性的缺点。于是React引入了Hooks,以函数作为组件封装的载体,借用函数式编程的理念来提高复用性。类似的还有Vue3中的CompositionAPI。无论是ClassComponent还是FunctionComponent,OptionsAPI还是CompositionAPI,它们的本质都是松耦合的状态和UI单元。当组件数量增加,逻辑变得复杂时,一种常见的解耦方法是从组件中提取可重用的逻辑,并将它们放在单独的Model层中。UI直接调用Model层的方法。Model层的管理也称为状态管理。状态的管理比组件中状态和UI的耦合更高层次的抽象。状态管理问题状态管理要考虑的最基本的问题是——如何尽可能匹配框架实现原则?比如我们要设计一个UserModel,如果写成类的形式:classUser{name:String;构造函数(名称:字符串){this.name=name;}changeName(name:string){returnthis.name=name;}}你只需要将这个Model实例包装成一个响应式对象,就可以轻松访问Vue3:import{reactive}from'vue'setup(){constuser=reactive(newUser('KaSong')asUser;return()=>(
