React Hooks时代,怎么实现视图与逻辑分离呢?
背景介绍
这是设计模式系列的第四节,学习的是patterns.dev里设计模式中原型模式内容,由于是资料是英文版,所以我的学习笔记就带有翻译的性质,但并不是翻译,记录的是自己的学习过程和理解。
第二节:JS和迪丽热巴一样有专业替身?没听过的快来补补课...
第三节:还在层层传递props?来学学非常实用的供应商模式吧
第四节:都知道JavaScript原型,但原型模式你会用吗?
写在前面
无论是前端,还是移动端在开发交互逻辑时都会比较注意视图与逻辑分离,比较著名的有MVC模式/MVP模式/MVVM模式,这些模式都在更合理的让视图和逻辑进行分离。今天让我们一起来学习一下React中视图与逻辑的分离模式。
这是一张MVP模式的示意图。
下面我们通过一个常用页面来分析下视图与逻辑分离模式。
示例分析
比如说有这样一个页面,展示6张可爱狗狗的照片,这些照片都是从网络请求下来的。
理想情况下,我们可以这功能拆分成两部分:
- 视图组件:向用户展示照片;
- 容器逻辑组件:请求照片数据,以及控制展示逻辑;

分离关注点(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方式能减少组件层级嵌套和简化数据流;
- 视图组件复用性更强;
- 视图组件不包含逻辑,从而降低了开发或使用难度,并且风格全局保持统一;
- 视图组件便于测试;
视图与逻辑分离模式缺点:
- 在较小的应用程序中会过度设计;
这就是所有关于视图与逻辑分离模式的全部内容,你学废了吗?欢迎大家在评论区友好讨论!
转载自:https://juejin.cn/post/7183979095691100218