React Hooks时代,怎么实现视图与逻辑分离呢?

4,741 阅读4分钟

背景介绍

这是设计模式系列的第四节,学习的是patterns.dev里设计模式中视图与逻辑分离模式内容,由于是资料是英文版,所以我的学习笔记就带有翻译的性质,但并不是翻译,记录的是自己的学习过程和理解

第一节:高并发造成的数据统计困难?看我单例模式一招制敌

第二节:JS和迪丽热巴一样有专业替身?没听过的快来补补课...

第三节:还在层层传递props?来学学非常实用的供应商模式吧

第四节:都知道JavaScript原型,但原型模式你会用吗?

写在前面

无论是前端,还是移动端在开发交互逻辑时都会比较注意视图与逻辑分离,比较著名的有MVC模式/MVP模式/MVVM模式,这些模式都在更合理的让视图和逻辑进行分离。今天让我们一起来学习一下React中视图与逻辑的分离模式。

这是一张MVP模式的示意图。

image.png

下面我们通过一个常用页面来分析下视图与逻辑分离模式。

示例分析

比如说有这样一个页面,展示6张可爱狗狗的照片,这些照片都是从网络请求下来的。

理想情况下,我们可以这功能拆分成两部分:

  1. 视图组件:向用户展示照片;
  2. 容器逻辑组件:请求照片数据,以及控制展示逻辑;
image.png

分离关注点(separation of concerns)设计原则:一部分处理网络请求逻辑;而另一部分只展示图片;

视图组件是无状态组件

视图组件通过props接收数据,视图组件的基础功能是展示数据,这些数据是通过我们预期的方式接收到的,可能包含styles,并且无需修改数据

import React from "react";

export default function DogImages({ dogs }) {
  return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
}

我们可以看下上面的这个视图组件,通过prop接收所有的图片数组,然后通过map渲染每张图片。视图组件通常都是无状态组件(stateless)。

那什么是无状态组件呢?当然这里准确来说应该叫趋向于无状态组件,也就是说可以有少量自己的state的:

  • 除非为了更新UI,自己一般不需要使用state;
  • 接收到的数据(props),自己不能修改

无状态组件的数据(props),通常由包含逻辑的容器组件传递过来。

容器逻辑组件

容器逻辑组件的基本职能是向内部视图组件传递所需要的数据(props);容器逻辑组件通常不渲染除视图组件之外的其他组件,甚至自己不渲染任何东西,也往往不需要任何样式

在前面的示例分析中,我们可以看到:DogImages视图组件接收数据dogs, 然后渲染所有图片,那么就需要容器逻辑组件DogImagesContainer异步请求dogs数据,然后传递给DogImages视图组件,具体代码如下:

import React from "react";
import DogImages from "./DogImages";


export default class DogImagesContainer extends React.Component {
  constructor() {
    super();
    this.state = {
      dogs: []
    };
  }


  componentDidMount() {
    fetch("https://dog.ceo/api/breed/labrador/images/random/6")
      .then(res => res.json())
      .then(({ message }) => this.setState({ dogs: message }));
  }


  render() {
    return <DogImages dogs={this.state.dogs} />;
  }
}

在线示例

通过视图组件容器逻辑组件,我们就可以实现应用程序的逻辑和视图分离

应用Hooks实现

在很多场景中,视图逻辑分离模式可以使用hooks代替。采用hooks方法,开发者可以更简单地为视图组件添加状态,并不需要提供一个容器逻辑组件。

比如上面示例中的的DogImagesContainer可以使用Hook替代

export default function useDogImages() {
  const [dogs, setDogs] = useState([]);

  useEffect(() => {
    fetch("https://dog.ceo/api/breed/labrador/images/random/6")
      .then(res => res.json())
      .then(({ message }) => setDogs(message));
  }, []);

  return dogs;
}

然后我们可以直接在视图组件DogImages中使用这个Hook:

import React from "react";
import useDogImages from "./useDogImages";


export default function DogImages() {
  const dogs = useDogImages();

  return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
}

通过直接调用useDogImages这个Hook,拿取返回值的方式,我们也实现了视图和逻辑分离,并且在视图组件DogImages之中也不需要修改数据。

使用Hook可以很方便的实现视图和逻辑分离,同时也减少了应用的层级嵌套简化数据流

优缺点分析

先来看看视图与逻辑分离模式的优点

  • 符合分离关注点(separation of concerns)设计原则;
  • 使用Hook方式能减少组件层级嵌套和简化数据流
  • 视图组件复用性更强;
  • 视图组件不包含逻辑,从而降低了开发或使用难度,并且风格全局保持统一
  • 视图组件便于测试

视图与逻辑分离模式缺点

  • 在较的应用程序中会过度设计

这就是所有关于视图与逻辑分离模式的全部内容,你学废了吗?欢迎大家在评论区友好讨论!