本篇内容在国外的一篇博客的基础上修改的,基于Vue3 + JavaScript
实现,使用腾讯前端AlloyTeam
的代码规范对演示代码进行校验,Git
提交规范使用开源工具husky
来验证。本文涉及的代码均以上传到GitHub
和Gitee
中。文章中有不正确的地方,请大家不吝赐教,批评指正。
GitHub
访问地址Gitee
访问地址
1. SOLID原则
SOLID
原则是面向对象编程和面向对象设计的5大基本原则,分别代表的是:
S
:SRP
,单一职责原则O
:OCP
,开放封闭原则L
:LSP
,里氏替换原则I
:ISP
,接口隔离原则D
:DIP
,接口隔离原则
下面对5大基本原则做一个简单的描述,如果之前有了解,可以跳转到第2章,了解如何在Vue3
中一步步实践这5大原则。
1.1 单一职责原则
单一职责原则规定:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中,即定义有且仅有一个原因使类变更。
反例:A
类负责两个不同的职责:职责B
和职责C
。当由于职责B
需求发生了改变而修改A
类时,有可能会导致职责C
不能正常运行了,这就说明职责B
和职责C
被耦合在A
类。
1.2 开放封闭原则
开放封闭原则规定:一个实体应该对扩展开放,对修改是封闭的,也就说说,如果想对一个已完成或者已运行的实体添加新的功能,那么应该通过扩展的方式来实现对实体的变化,而不应该通过对现有的实体进行修改来实现变化。
1.3 里氏替换原则
里氏替换原则规定:一个对象出现的地方,都可以使用子类来替换,并且不会导致程序的错误,它的意义在于:子类可以扩展父类的功能,但是不能修改父类原有的功能。
1.4 接口隔离原则
接口隔离原则规定:客户端不应该被迫实现一些他们不需要的接口,而应该把胖接口中的方法分组,然后使用多个接口来替代它,确保每个接口服务于一个模块。
1.5 依赖倒置原则
依赖倒置原则规定:抽象不应该依赖于细节,而细节应该依赖于抽象,也就说,应该针对抽象(接口)编程,而不是针对实现细节编程。
依赖倒置原则和开放封闭原则的关系:开放封闭原则是面向对象设计原则的基础,而依赖倒置原则是实现开放封闭原则的一个基础。
2 初始Vue3项目
这里使用vite
来初始化Vue3
项目,这里不介绍vite
是什么,可移步vite官网查看。
2.1 配置AlloyTeam代码规范检测
这里以VSCode
编辑器为例,介绍如何在Vue3
项目中配置AlloyTeam
代码规范检测。
VSCode
需要安装的插件:ESLint
、Prettier - Code formatter
、Vetur
、Auto Close Tag
(可选)、Auto Rename Tag
(可选)和Debugger for Chrome
(可选)VSCode
配置:使用组合键Ctrl+Shift+P
打开Command Palette
,选择Preference: Open Settings (JSON)
,在文件中配置一下内容:
- 项目安装依赖:
package.json
文件内容如下,可直接替换devDependencies
中的内容,然后使用npm install
安装依赖
- 在
Vue3
项目根目录下增加两个文件:.prettierrc.js
、.eslintrc.js
.eslintrc.js
.prettierrc.js
- 通过以上配置,在保存文件时,会自动检测相关代码,并按照约定的格式格则自动调整。
2.2 配置Git提交规范验证工具:husky
- 安装依赖项:
npm i -D husky
- 修改
package.json
:在devDependencies
后添加husky
,具体内容如下:
- 在项目根目录下创建
script
文件夹,并在其中创建verify-commit.js
文件
2.3 实现代码事件列表
- 删除
components
文件夹 - 增加
views
文件夹,并在其中创建Home.vue
文件
- 修改
App.vue
- 在
src
目录下新建router.js
- 修改
main.js
- 实现效果:
3 SRP改造
在上面的示例中,前面页面的工作都是在Home.vue
这个文件中实现,包括:header bar
、nav bar
、todo list
等。如果将SRP
原则应用于当前现有的项目中:每个组件都应该只有一个改变的理由。很明显,当前的Home.vue
组件不能满足SRP
理由,因为它有多个可以改变的理由:
- 使用
axios
替换fetch
- 根据需求添加其他元素,例如:
sidebar
、img
- 改变列表的展示形式
- ...
当整个系统功能越来越多,系统越来越庞大的时候,我们可能对Home.vue
文件失去控制,因此,尝试使用SRP
原则对其进行改造。
3.1 抽取访问RESTful接口的内容
在项目根目录创建services
文件夹,然后在该文件夹中创建api.js
文件,内容如下:
3.2 拆分页面组件
将header
从Home.vue
中拆分出来,在components
文件夹中创建MyHeader.vue
文件:
将todo-list
也从Home.vue
中拆分出来,在components
文件夹中创建MyTodoList.vue
文件:
3.3 修改Home.vue
修改Home.vue
文件,引入刚刚拆分的组件和API
接口:
通过以上步骤,使演示示例基本满足SRP
原则的规定,即:
api.js
文件对应着RESTful
接口的调用MyHeader.vue
文件对应着header
MyTodoList.vue
文件对应着todo-list
代办事项列表
4 OCP改造
OCP
原则规定:对扩展开放,对修改关闭。会看下当前示例,现在展示具体的代办事项使用Card
的形式来展示的,那么如果想用Row
的方式来展示,就要修改现有的MyTodoList.vue
文件,这显然与不符合OCP
原则。具体的改造方法如下:
4.1 拆分Card表示形式
将MyTodoList.vue
文件中涉及的Card
表现形式的相关代码抽离出来,添加到具体的文件中。在components
文件夹中新建文件MyTodoCard.vue
文件,内容如下:
4.2 修改MyTodoList
修改MyTodoList.vue
文件,主要是使用slot
插槽的形式替换之前具体的Card
表现形式,并移除多余的CSS
代码:
4.3 修改Home.vue
修改Home.vue
文件,引入MyTodoCar.vue
文件,MyTodoList
元素中引入MyTodoCar
,为代办事项设置对应的表示形式:
4.4 新增Row.vue文件
增加一种新的代办事项的展示形式,来验证经过上述修改是否能满足OCP
原则的规定。
- 在
components
文件夹中创建MyTodoRow.vue
文件
- 在
Home.vue
文件中使用MyTodoRow
替换MyTodoCard
可以看到,修改代办事项的表现形式,就需要增加一个.vue
文件来实现具体的表现形式,然后在Home.vue
文件中做简单的修改,并且MyTodoList.vue
文件保持不变。到这里,基本实现OCP
原则。
5 LSP改造
LSP
原则规定:当对父类进行扩展时,能够使用子类的对象来替换之前使用的父类的对象,从而不会破坏客户机的代码。具体到当前演示示例该如何实现呢?考虑这种情况:现在基于axios
来从服务器获取todo-list
,并且不修改Home.vue
的文件,这种情况该怎么实现呢?
5.1 修改API传参方式
将传递URL
具体地址的入口由new Api()
转为fetch
函数,即:api.js
的内容:
Home.vue
也做相应的修改,主要是用来适配api.js
的修改,修改后的setup
的内容为:
5.2 添加基类BaseApi
在services
文件夹中创建文件BaseApi.js
作为API
的基类,主要存储URL
中的服务器信息,并使用fetch
的方式来获取服务器返回数据,内容如下:
5.3 扩展基类使用axios查询RESTful接口
在services
文件夹中创建文件AxiosApi.js
,使用axios
来查询RESTful
接口。
5.4 修改api.js
接下来,需要修改api.js
文件,用来调用BaseApi.js
或者AxiosApi.js
如果想替换成axios
的方式来获取服务器数据,只需要将引入import { AxiosApi } from './AxiosApi'
,并修改apiProvider
的实例化方式即可。这样我们就完成参照LSP
规则对演示示例的改造。
6 ISP改造
ISP
规则应用在组件情形时,表示:不应该强迫组件依赖于它们不使用的属性或者方法。来回顾之前的代码,主要观察对于MyTodoRow
和MyTodoCard
的传参方式,是把整个todo
对象传递给了这两个组件,但是对象中的userId
属性在这两个组件中都没有用到,并且MyTodoCard
没有使用到id
这个属性。可见,当前的演示示例违反了ISP
原则。
解决这个问题的两种方法:
- 将
todo
实例切分成更小的实例 - 向组件传递它需要的数据
这里采用第2种解决方法。分别修改MyTodoCard.vue
和MyTodoRow.vue
文件,将需要使用的数据属性添加到props
中,然后修改Home.vue
文件,修改向其传递数据的方式。
MyTodoCard.vue
MyTodoCard.vue
Home.vue
7 DIP改造
DIP
规则规定:高级类(组件)不能依赖低级类(组件),他们两个都应该依赖抽象,抽象不能依赖细节,而是细节应该依赖抽象。其中:
- 低级类用来实现基础的操作,例如:与
API
对接; - 高级类包含了复杂的业务逻辑,这些逻辑用来指导低级类进行某些操作。
参照DSP
规则,修改api
相关的类文件。
作者原文中使用TypeScript
实现的DIP
规则。JavaScript
中没有接口这个功能,但是可以使用模块封装来实现类似的行为,例如:导出类或者方法。这里使用导出类的方法来实现DIP
规则。
- 修改
BaseApi.js
文件,实现类似接口的功能
- 在
services
文件夹创建FetchApi.js
文件,继承BaseApi.js
方法,使用fetch
方法来实现获取数据
- 修改
api.js
文件,在这个文件中,可以切换AxiosApi.js
和FetchApi.js
文件的引用,调用不同的方式来获取服务器数据
8 参考
- 设计模式之SOLID原则
- 前端工程(二):统一规范
- How to avoid SOLID principles violations in Vue. JS application
- Decoupling code in JavaScript with the Dependency Inversion Principle
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论