前端技术

现代 React Web 开发实践 札记

2022-11-03  本文已影响0人  吴摩西

写在前面

这是一个收费课程的学习札记,课程在极客时间。是宋一玮老师的课程。笔者并非时初学者,所记多有残断,如要系统的学习,还请参加宋一玮老师的课程:https://time.geekbang.org/column/intro/100119601?tab=catalog

01 | React 如何学习以及前端在做什么

  1. 学习一个新框架,学习它的基本概念,上手调用 API 写代码,属于技术表面。理清概念联系,理解 API 内部的原理,则属于技术底层。如果学习实践只停留在表层,那一定会遇到瓶颈。

React 新版本 + 函数组件 + Hooks 优先 + 团队协作 = 高效进阶

  1. React 开发者不仅是 React 框架本身,还包括对前端整体框架和 React 技术边界的把握。对新老前端技术差异的理解,与历史遗留代码的整合,与多个前端开发团队的协作,等等。

  2. 应对前端技术迭代过快

    1. 前端技术不只是技术,应该全面了解,有效总结,将其变为自己的知识点。
    2. 掌握技术的广度和深度一样重要。
  3. GUI 设计的一般原则

    1. 可用性:自解释,无门槛
    2. 一致性:除非有真正出众的替代方案,否则还是遵循标准
    3. 遵循用户心智模型,避免实现模型。即颜色的输入最好是取色器,而不是 RGB 录入。
    4. 最小惊讶原则:用户也是系统的一部分,设计应该符合用户的经验,预期和心智。设计时应该面向一般用户,而不是面向对系统有深入理解的用户。
    5. 及时反馈,用户点击了提交按钮就要告诉他是否成功,需要时间处理,就告知用户正在处理。任何时候都要防止页面冻结无法交互。
  4. 前端开发的领域知识。

    1. 例如按钮图标。
    2. Web 浏览器,了解浏览器能做什么,不能做什么。不能轻易想当然。
  5. 前端领域的变与不变

    1. 模版,从 JSP 就已经存在的,通过模版生产 HTML。
    2. 模版的条件和循环,JSTL 和 vue 都支持类似的语法。实际还还有其他的语法支持,例如 handlebar。还有一些 ASP, C#, ejs 等的语法支持。
    3. 代码分层,Java 端有 Sevlet + Java Bean + JSP 的 MVC 实现。我还记得有 POJO 代替 Java Bean 的实现。Angular 有 MVVM 的实现。
    4. 软件分发,JSP 需要编译成 .war 包,部署到 Tomcat 内提供服务。虽然目标和实际渲染过程不同,但 React,Vue.js,Angular 一般而言也需要进行构建,现在比较流行的是 Webpack, Vite 等工具。更早之前,流行的是 jQuery + YUI Compressor.
    5. 项目依赖管理,Java 需要拷贝 .jar 文件完成依赖,后来引入了 Maven。JS 项目使用 npm 管理依赖

02 | 前端开发要点,React 的应对

前端开发的各高度视图

三万英尺视图一千英尺视图 这是商业战略中的概念,后被 <<97 Things Every Software Architect Should Know>> 这本合著中引入到软件架构设计领域。

前端应用分类

B/S , C/S 及 Hybrid (混杂)应用。React 善于 B/S 浏览器渲染。

前端逻辑框架

对业务进行分块,对可能用到的大块的功能进行区分,例如 CI/CD,打包变异,自动化测试,运维工具;UX 设计系统,响应式布局,可访问性等。

应用框架

解决各个模块内部的逻辑拆分,例如 SPA 中一般包含 MVC 框架,服务器端交互模块,前端路由,错误处理等。一个纯粹的 MVC 架构,视图会触发控制器,控制器修改模型,模型在触发视图更新。

软件架构

所谓软件架构,即是在软件开发之前进行计划,所谓软件架构师即是给出软件开发计划的人。

设计模式

将软件分隔为独立并能完成单独责任的模块,设计模式可以帮助创建可管理的,可测试的,可复用的并且优化的软件。MVC 是比较重要的设计模式。

MVC

将数据模型与数据展现相分隔。单独的修改可以不影响另一个。MVC 还有各种变体,MVVM, MVP, MVI, 其中细节值得进一步的研究。


image.png

08 | 组件生命周期

  1. 值得注意的是,更新之前,会执行一轮 Effect 的清除函数。
  2. useState 是基于 useReducer 实现的,尚未注意,值得再看看。

09 / 10 | React Hooks

副作用: 当调用函数时,除了返回可能的函数值之外,还对主调用函数产生附加的影响。例如修改全局变量,修改参数,向主调用方的终端、管道改变外部存储信息等。

11 | 合成事件

14 | 工程化

CRA (Create React Application) 包括

倾向性:工具或者框架具有倾向性,意味着它对你的使用场景做了假设和限定,为你提供了它认为是最有效或是最佳实践的默认配置。

可以使用 npm init @eslint/config -y 初始化 eslint 的配置。

15 | 不可变数据

React.memo 有另一个签名:const MyPureComponent = React.memo(MyComponent, compare)compare 可以自定义比较函数。compare 含有 oldProps, newProps

16 | 应用状态管理

Redux 使用了 useSyncExternalStore,参阅 React 18 useSyncExternalStore API 可知。
useSyncExternalStore 主要是解决撕裂(tearing)的问题

image.png

如果使用了类似于 startTransition 之类的调用,或者使用了外部的 store,由于并发渲染,可能会导致 React 18 渲染结果撕裂。

18 | 数据类型:活用 Typescript

type 与 interface 不同:

  1. type 可以作为联合 Union 类型的别名,但 interface 不可以;
type Pet = Cat | Dog; // 可以
interface IPet extends Cat | Dog {} // 不可以
  1. interface 可以重复声明 (Redeclaration),但 type 不可以;
interface ICat {
  age: number;
}
interface ICat {
  color: string;
} // 可以,会合并
const cat: ICat = { age: 4, color: 'silver shaded' };

type Cat = { age: number } ;
type Cat = { color: string }; // 不可以,会报错

越是希望组建封闭,越倾向于用 type,越是认为组件开放灵活,越倾向于 interface。开源组件库中用 interface 声明 props 的较多。就宋一玮老师而言,没有什么特别想法的时候,会使用 type。

19 | 代码复用:设计 Hooks 和组件

组件存在的问题。

具体表现为:

抽象不仅是为了复用代码,更是为了开发出更有效,更易读,更好维护的代码

组件的抽象应以能被其他组件/页面组合为目的。

20 | 大型项目

node.js 提供了 subpath import

// ./node_modules/es-module-package/package.json

{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}
import internalZ from '#internal/z.js';
// Loads ./node_modules/es-module-package/src/internal/z.js

22 | 质量保证

目前比较流行的 E2E 工具有 Cypress, Selenium, Playwright.

测试金字塔之的是 E2E 测试 (10%), 整合测试 (20%), 单元测试 (70%)。

加餐 | 真自组件和 react/jsx-runtime

为了降低 props 的复杂度,使用 真子组件 或者 DSL,或者组件组合。

<Dialog>
  <Dialog.Title>标题<Dialog.Title>
  <Dialog.Content>内容<Dialog.Content>
  <Dialog.Action type="confirm">确定<Dialog.Action>
  <Dialog.Action type="cancel">取消<Dialog.Action>
</Dialog>

React 17 / 18 中的 react/jsx-runtime

React 17 开始使用全新的 JSX 运行时来替换 React.createElement。在启用新的 JSX 运行时的状态下,用代码编译器编译 JSX:

jsx-runtime 和 React.createElement 函数,他们返回的也同样是 React 元素。如果开发者手工调用 React 元素,依旧应该调用 React.createElement。此 API 并不会移除。而 jsx-runtime 代码只应由编译器生成,开发者不应直接调用。

React 17 的 JSX 较 React.createElement 相比的变化包括:

加餐02 | Fiber 协调引擎

Fiber 协调(Reconciliation)引擎主要的工作包括并不限于:


type Fiber = {
  // ---- Fiber类型 ----

  /** 工作类型,枚举值包括:函数组件、类组件、HTML元素、Fragment等 */
  tag: WorkTag,
  /** 就是那个子元素列表用的key属性 */
  key: null | string,
  /** 对应React元素ReactElmement.type属性 */
  elementType: any,
  /** 函数组件对应的函数或类组件对应的类 */
  type: any,

  // ---- Fiber Tree树形结构 ----

  /** 指向父FiberNode的指针 */
  return: Fiber | null,
  /** 指向子FiberNode的指针 */
  child: Fiber | null,
  /** 指向平级FiberNode的指针 */
  sibling: Fiber | null,
  
  // ---- Fiber数据 ----

  /** 经本次渲染更新的props值 */  
  pendingProps: any,
  /** 上一次渲染的props值 */
  memoizedProps: any,
  /** 上一次渲染的state值,或是本次更新中的state值 */
  memoizedState: any,
  /** 各种state更新、回调、副作用回调和DOM更新的队列 */
  updateQueue: mixed,
  /** 为类组件保存对实例对象的引用,或为HTML元素保存对真实DOM的引用 */
  stateNode: any,

  // ---- Effect副作用 ----

  /** 副作用种类的位域,可同时标记多种副作用,如Placement、Update、Callback等 */
  flags: Flags,
  /** 指向下一个具有副作用的Fiber的引用,在React 18中貌似已被弃用 */
  nextEffect: Fiber | null,

  // ---- 异步性/并发性 ----
  
  /** 当前Fiber与成对的进行中Fiber的双向引用 */
  alternate: Fiber | null,
  /** 标记Lane车道模型中车道的位域,表示调度的优先级 */
  lanes: Lanes
};

workLoop 可以随时停,通过 shouldYield() 标记决定是否暂停工作,释放计算资源给更紧急的任务。

提交阶段分为3个县后同步执行的子阶段:

提交阶段会多轮执行 flushPassiveEffects()

直播加餐1 | 前端为什么要工程化

软件开发生命周期

工程化,简单来说就是能让软件工程做成,让它做的快,做得好,再让做的过程可以被预期,可以被管理。最后质量可以被保证,让客户满意。

打包

直播加餐2 | Freewheel 前端工程化的演进

结束语 | 对 React 和前端技术未来展望

学习一门技术,务必有大于一门技术的收获。
软件开发的从业者要具有终身学习的能力和决心。 不论是追逐理想还是直面功利。

上一篇下一篇

猜你喜欢

热点阅读