vue 失去聚焦(软键盘消失)_如何避免在Vue应用中违反SOLID原则

在这篇文章中,我将讨论如何在 Vue 应用中使用 SOLID 原则。

SOLID 包括以下观点:

  • 单一职责原则

  • 开闭原则

  • 里氏替换原则

  • 依赖倒置原则

  • 接口隔离原则

接下来我们看看如何在 Vue 实战中避免这些原则,我们从一个 TODO LIST 项目中去体会这些观点。

准备

用 vue cli 初始化一个 Vue 项目。

vue create todo-app

我们用 vue2.6.10 + typescript3.4.3 构建我们的应用。如果你对 typescript 不熟悉,可以查看typescriptlang。

安装完成之后,将目录中的组件都删除掉,然后我们的 src 目录如下图所示:

236bf8fb73f25669055e0dc261c9b38a.png

App.vue:

6f537f2a43d5f98082f9281fee3bc352.png

views/Home.vue:

9d9a0f22a476b13cedf816812cd49a80.png

准备工作就绪,接下来正式进入正题。

单一职责原则(SRP)

首先我们将 views/Home.vue 组件改成如下代码,通过API获取一个任务列表并展示出来:

b3366ae8d2c1edf3a3a4d94b56799b61.png

基本上所有的功能我们都在 views/Home.vue 中完成了。单一职责原则规定的是一个类应该有且仅有一个引起变化的原因,否则应该被拆分。接下来我们看看有多少个理由可以改变我们的组件:

  1. 我们想要改变请求函数 fetchTodos 来获取待办事项:用其他库比如 axios、api、方法本身等;

  2. 我们想要添加更多的元素,比如sidebar、menu、footer等等;

  3. 我们想要修改已存在的元素:header 或者 todo list;

如上面所述,我们轻易地找到3条理由去修改这个 views/Home.vue。
当这个应用的功能越来越丰富时,真正的问题将开始:代码越来越多直到我们都不知道自己写了什么(这就意味着该组件失去了控制)。

通过将上述可能存在的变动提取到不同的函数、类或者组件中,我们就可以避免违反单一职责原则。接下来进行重构:

第一步,将我们的请求函数放到新的API文件中(新建 src\api\api.ts):

fa26b7218da084df7aceb5adb14648a8.png

第二步,我们将 header 组件提取成一个新的函数组件 components/Header.vue:

f2c11c9978d0aaba40191e2d3831611a.png

最后一步,我们提取 TODO LIST 组件 components/TodoList.vue:

5303ac955968e029523c37b3df272dec.png

然后修改 views/Home.vue:

a5734786477021fefb9a5242a88aef07.png

重构之后,可以看到我们的 Home 组件更加的单一和可读。

开闭原则(OCP)

让我们把目光聚焦在 components/TodoList.vue。这个组件展示了一系列待做任务卡片。如果需求变更,我们要改变这些卡片或者将它们用表格的形式展示,会发生什么?答案是我们必须修改这个组件(它看起来非常死板)。开闭原则规定“当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。”现在我们来重构 TodoList 组件,达到避免这种窘境!

我们通过插槽来改变这个情况:

843478f601b38ba7c909e70fda37562a.png

然后将我们的卡片独立成一个组件 components/TodoCard.vue:

304c5cefe7d59664424d5886bfd3f50e.png

最后更新我们的 Home 组件:

bf03f8c195962ff798c4055561d97195.png

这样一来,我们就能够轻易地通过其它组件来修改任务显示。

里氏替换原则(LSP)

这节聚焦在 API 部分。首先我们将 api.ts 重新命名并将它放到一个独立的文件 api/BaseApi.ts:

3068a535e508c59c88d2d8f28bb8b60d.png

如你所见, BaseApi 类有一个 fetch 方法需要一个参数 url。因为一些原因,我们决定使用 axios 库。

yarn add axios

然后创建一个 BaseApi 的子类 AxiosApi( api/AxiosApi.ts):

add2dcfe57b7fc63ca284d2c1f740a9e.png

如果我们要将 views/Home.vue 的 BaseApi(父类)类用 AxiosApi(子类)替换掉:

a1c9c5aa73784cd369f2ad1b92890036.png

这样做已经破坏了我们的应用,因为我们没有遵从里氏原则:“子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

如你所见,我们将对象作为参数传递给 AxiosApi 类的 fetch()方法,但是 BaseApi 类改为使用字符串。在这种情况下,我们不能毫不费力地用父类替换子类。我们只需要做下面的改变即可:

af11bf61552f9df7a1334de49e32688c.png

现在我们就可以随意无缝切换 BaseApi 或 AxiosApi。更深入一些,我们可以创建一个 Api 类来控制子类。

dc27c943fde08a12902db43d6c93c864.png

接口隔离原则(ISP)

我们将任务可视为卡片。让我们在 components/TodoRaw.vue 添加一个列表:

2f4e60dbd3249d10ada2dd1b0d9d6eb1.png

然后用列表替换掉卡片:

0a79b3d00fec621ffa79c83bbcb2dea7.png

如你所见,我们在 TodoCard.vue 和 TodoRow.vue 中将整个 todo 对象作为 prop 发送,但是我们仅使用此对象的一部分。 userId 在两个组件中都没用到, id 仅在 TodoCard.vue 中使用。我们这就违反了接口隔离原则“组件不应该依赖没有使用到的属性和方法”。

有两种方式可以解决这个问题:

  • 将 TODO 接口缩减成更小的接口

  • 仅仅传输使用到的属性给组件

使用函数式组件来重构可视化组件, views/Home.vue:

0820485440610e72abd2bd9ce8d7c264.png

components/TodoCard.vue:

0b704c4ca230b762a5aa8c7a912fee38.png

components/TodoRow.vue:

d2bb4e065cfb9ad93995c0658f4d45f3.png

依赖倒置原则(DIP)

依赖倒置原则的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

高层模块和低层模块是什么意思?

  • 低层模块实现最基本的操作,例如API;

  • 高层模块包含复杂的业务逻辑,该逻辑指导低层陌路爱类执行某项操作。

我们在 types 中为 Api 类创建一个新的接口:

8c2be237224ba47ad4fac5d65490936e.png

接着更新我们所有的 api 类和 views/Home.vue:
更新 api/api.ts:

df805a3382dbe9b841bdc51b6b15fdd2.png

api/AxiosApi.ts:

ed6f210302d570df62b7a43e3a1aaf09.png

api/BaseApi.ts:

d1cb8923529085cd62fd6c6af85bbdec.png

views/Home.vue:

8506cb6019b22e122e54e69359a3cc1b.png

现在,低级类 Api 和高级 views/Home.vue 类依赖同一个接口 IApi。

原始依赖关系的方向已经颠倒了:低级 Api 现在依赖于高级抽象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值