🤔 技术背后隐含的思维方式
!本篇文章过于久远,其中观点和内容可能已经不准确,请见谅!~
真正的面向接口编程,更自然完备的模块和逻辑抽象,让前端开发的思维方式从怎么实现结果,变成了更高质量的需求交付,去思考接口的输入输出,去设计模块和代码,更何况仅仅是代码提示和对接联调就足够让你爽翻天了。
传统的前端开发,很多没有享受过静态类型语言带来的操作差异,调用方法实现功能的逻辑也并没说多复杂,文件结构组织的模块化、组件化也能满足需求实现高质量的代码。
但是复杂模块之间的集成从来不是那么顺利,功能的升级重构、项目的交接一直是开发很重要的一部分,这块经常会出现问题,因为并不是每个人、每次都能清楚的记住项目的接口、参数、类型等,这块出现的错误在前端占了很大一个比例,所以很多的测试才能覆盖这块的质量保证。可是,这对于静态语言其实并不是问题。
之前的前端代码提示依赖于 document 注释,这其实对很多人是一种负担,很多人只使用内置的官方提示,不能享受类型思维的好处。TS 使用一些标记和类型声明能够很好的实现完整的代码提示,带有代码提示的编程体验能让效率和质量更好,至少代码质量里面静态检查没有问题了。
这都是 Typescript 带来的重要特性,TS 能帮我们梳理清不同的接口、天生的让你知道这个接口应该怎么样的,在运行时才能看到的问题在开发的时候就很明显的暴露了。
- 另外也写了一篇详细的文章说 🏃♂️ TypeScript 以及一些理解和技巧
hooks 的思想是开发者不再需要去理清每一个生命周期函数的触发时机,以及在里面处理逻辑会有哪些影响。而是更关注去思考哪些是状态,哪些是副作用,哪些是需要缓存的复杂计算和不必要的渲染。
相比 class 的生命周期的逻辑组织形式,从整个组件的挂载到卸载的事件驱动。hooks 代表的是一个组件中状态、行为和副作用,这也是为什么没有用 class 生命周期那一套现成的逻辑实现类似
onComponentMounted(callback)
的接口。const [state, setState] = useState(0)useEffect(()=>{// 执行一些副作用逻辑return ()=>{// 清理副作用逻辑}})// ============ 要比下面优雅的多const statesonComponentDidMount((){// 执行一些副作用逻辑})onComponentDidUpdate((){// 执行一些副作用逻辑// 更新状态})onComponentWillUnmount((){// 清理副作用逻辑})
比较之后就能感觉到不同的代码组织,代表着不同的思维模式。
- 另外也写了一篇详细的文章说 ⚓ React Hooks 理解
在确实很复杂的状态管理中,强制性的集中登记每一次修改和分发,能够将 数据状态 、数据状态的使用 和 修改数据状态的行为 分离,对整个框架的解耦是很有效的。
flux 的模式本身可能在很多地方都有相似的实现,比如带有数据操作方法的发布订阅模式、观察者模式都可以实现这个能力,这里强调的是数据解耦的思想。
flux 将一个约定限制死,能达到意想不到的好处:数据、使用数据、修改数据的逻辑分离。实践起来就是视图层不允许直接修改数据,只能调用 action 修改后再分发给视图层使用。这样一个约定就实现了单向数据流的概念,数据不再是想改就改,只能通过统一维护的修改动作,这样数据的变动就变得可以预测。
在不引入单独数据层的时候,很多组件之间的数据通信都是通过 props 下发,然后通过 onCallback 来获取更新事件,甚至直接在子组件中修改 props 的引用。这样的后果就是没办法知道一个状态的改变是由谁引起的,链路很长不规范很难去调试。
而引入 flux 概念之后的约束就是,数据这个东西你们父子组件通信可以,但是全局的应用状态 store 必须交由中心化管理,每次修改都需要通过标准的修改函数 action 来执行,最后的结果更新需要通过 reducer 同步更新到数据中心,每个使用的界面组件会收到状态更新。
在前端实现任务的调度和控制权的转移,避免调用栈阻塞用户响应。
React 16 版本采用新的 Fiber 架构,React Fiber 是对核心算法的一次重新实现。之前的架构是同步渲染组件树的,同步的概念是一旦进入调用栈开始就要执行完毕,一旦组件树的深度很深 js 需要计算的渲染越多,即使添加 shouldComponenntUpdate 等方法来优化这个问题,但是必要的更新依然没办法解决,浏览器的响应只能要依赖于 js 的原生执行堆栈了。
JavaScript 原生的执行模型是通过Call stack(调用栈)来管理函数执行状态,由引擎管理,某一个执行栈一旦开始,就会一直执行,直到这个执行栈清空,无法按需中止。全部的执行栈可能会有一些任务调度,但是大体上很容易出现执行栈耗时很长导致界面无响应问题。
而一旦原生调用栈中,组件渲染的调用栈执行超过 16.67ms,依然没能够将响应用户操作(比如 onClick)的任务队列取出来执行,界面就会出现低于 60fps 的卡顿。
这个是原生任务队列的机制决定的,浏览器的 Event Loop 模型没办法区分哪个任务才是用户需要的,执行栈也没办法终端切换。
所以这个事情需要框架本身来掌控,也就是让框架来调度渲染(与浏览器渲染不同,强调的是组件的 render)的部分,所以 Fiber 是用来控制每个组件的渲染时机。
在思维方式上这是一个更精细化的执行方式,让每一帧的渲染能够可控,每次渲染完之后判断还有没有剩余时间,没有了的话就把控制权交给浏览器来执行用户操作响应,处理完之后再继续渲染,实现了分片渲染的逻辑,最终实现这种在前端中接管整个调度的效果,brilliant!!!
参考:
感谢您的阅读,本文由 Ubug 版权所有。如若转载,请注明出处:Ubug(https://ubug.io/blog/thinking-behind-tech)