想写好前端,先练好内功

    |     2021年7月26日   |   web前端技术, 优化   |     0 条评论   |    907

《笑傲江湖》里封不平剑招再快,没有内力也撑不住。前端同样:React、Vue、Redux、RN、Weex 像各门招式,真正能选型和扛复杂度的,是招式背后的工程思想。

文档还没看完又出新框架,先别慌。框架是手段;开闭原则、函数式编程、消息机制这三套「内功」在后端很常见,前端框架里也在用。下面结合例子把这三件事说清楚。

一、开闭原则

提到面向对象,很多人先想到 23 种设计模式。模式是实践,上面还有 SOLID:

  • Single Responsibility Principle 单一责任原则
  • The Open Closed Principle 开放封闭原则
  • The Liskov Substitution Principle 里氏替换原则
  • The Dependency Inversion Principle 依赖倒置原则
  • The Interface Segregation Principle 接口分离原则

出发点都是「高内聚、低耦合」。后端用得最多的往往是依赖倒置,相关模式大约五六个:

TB1rnGWt8LoK1RjSZFuXXXn0XXa-692-311

上图也可以看成从抽象到具体的演进。前端框架里用得更多的是开放封闭原则:A software artifact should be open for extension but closed for modification。

翻译:对扩展开放,对修改封闭。看一个面积计算的例子:

public abstract class Shape
{
	public abstract double Area();
}

public class Rectangle: Shape
{
	public double Width { get; set; }
	public double Height { get; set; }
	public override double Area()
	{
	 	return Width * Height;
	}
}

public class Circle: Shape
{
	public double Radius { get; set; }
	public override double Area()
	{
		return Radius * Radius * PI;
	}
}

public double Area(Shape [] shapes)
{
	double area = 0;
	foreach (var shape in shapes)
	{
		area += shape.Area();
	}
	return area;
}

场景再扩展,Area 汇总函数也不用改;每种 Shape 自己实现面积。核心逻辑不要轻易动,否则稳定性和测试成本都会炸。应当先抽象、统一接口,扩展时加实体类。

前端里,Ant Design 的 Form 就是开闭原则:它不像普通控件有固定长相,更像容器,提供接入标准和校验、提交。一项可以这样画:

<FormItem>
  {getFieldDecorator('userName', {
     rules: [{ required: true, message: 'Please input your username!' }],
     })(<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
		placeholder="Username" />
   )}
</FormItem>

库里已有 Select、Checkbox、Radio、Cascader 等,业务仍会做自定义表单项。Form 约定三条即可接入:

  • 提供受控属性 value,或与 valuePropName 同名的属性。
  • 提供 onChange,或与 trigger 同名的事件。
  • 不能是函数式组件。

具体例子

校验、提交封在 Form 里不变;自定义控件满足约定就能和官方控件一起用。这就是开闭:核心封闭,扩展走接口。

二、函数式编程

交互越来越复杂,FP 凭复用、好测、代码更短,成了很多框架的设计准则。特征大致是:

  • 函数为「一等公民」
  • 模块化、组合
  • 引用透明
  • 避免状态改变
  • 避免共享状态

JS 函数可当参数和返回值,天生是一等公民。后面四条要靠写法落地。

找出字符串中率先出现的四个非数字字符

非 FP:

var words = [], count = 0;
var text = str.split('');
for (var i = 0; count < 4 && i < text.length; i++) {
  if(!text[i].match(/[0-9]/)) {
    words = words.concat(text[i]);
    count++;
  }
}

FP:

var words = str.split('').filter(function(x){
  return (!x.match(/[0-9]/))}).slice(0,4);

用 filter、slice 拿掉 for,更顺。原文 FP 示例写成了 /[1-9]+/,会把 0 当成非数字留下,和题意「非数字」不一致,这里按 [0-9] 对齐。业务里最常用的是模块化组合。

数组元素相加、相乘、相与

非 FP:每种运算复制一份循环。

function plus(array) {
  var res = array[0];
  for (let i = 1; i < array.length; i++) {
    res += array[i];
  }
}

function mul(array) {
  var res = array[0];
  for (let i = 1; i < array.length; i++) {
    res *= array[i];
  }
}

function and (array) {
  var res = array[0];
  for (let i = 1; i < array.length; i++) {
    res = res & array[i];
  }
}

plus(array);
mul(array);
and(array);

FP:运算抽成模块,循环用 reduce:

var ops = {
  "plus": (x,y)=>x+y,
  "mul" : (x,y)=>x*y,
  "and" : (x,y)=>x&y
}

function operation(op, array) {
  return array.slice(1).reduce(ops[op], array[0]);
}

operation("plus", array);
operation("mul",  array);
operation("and",  array);

新运算只需在 ops 里加一项。复杂逻辑切小块再组合,复用往往比 OOD 更顺手。拆太细文件和沟通成本会涨,这是实践里常被抱怨的点。

React 里的 FP

用户操作或接口改数据后,React 做 virtual DOM diff,得到修改指令再打到真实 DOM:

TB1zEuZt4TpK1RjSZFGXXcHqFXa-1120-530

UI 是数据的映射,可以写成 UI = React(data)。

React 的本质是一种函数,而且接近「引用透明」:输出只看参数,不看外部环境。这样好测、好组合。组件由更小组件构成,高阶组件就是透明函数的组合。

三、消息机制

观察者模式、Windows 消息、Spring ApplicationListener、Objective-C 的方法调用,都靠消息驱动。好处是模块解耦,彼此只发消息。

没有消息时,预定系统里用户要知道订单、订单要知道支付,模块互相调用:

TB1Uvrot4naK1RjSZFtXXbC2VXa-692-413

改成消息后,下单、支付、预定都发给消息处理器,同时听回来的消息。模块不知道对方存在:

TB1Ose4t3HqK1RjSZJnXXbNLpXa-692-483

前端里常用 EventEmitter。同一页一块 React、一块 D3,要同步数据就可以走消息。不同生命周期的组件用消息管,耦合下去、关系代码收到一个文件,内聚上来。

消息还带出中间件:通用逻辑放中间件,业务更短。前端里最典型的是 Redux:交互先变成 action,经 reducer 和 middleware 改数据,再同步 UI:

TB1zeyZtYPpK1RjSZFFXXa5PpXa-692-439

Redux 的利弊社区讨论很多,这里不展开。

内功 一句话 前端落点
开闭原则 核心别改,扩展走接口 Ant Design Form 的 value / onChange 约定
函数式 小函数组合,引用透明 React:UI = f(data);reduce / 高阶组件
消息机制 模块只发消息、不互调 EventEmitter;Redux 的 action

四、参考

一句话总结:招式会过时,开闭、函数式、消息这三套内功能帮你看懂框架为什么这么设计,选型才有依据。

转载请注明来源:想写好前端,先练好内功
本文链接地址:https://ai.zhousir.top/?p=3273
回复 取消