如何结合整洁架构和MVP模式提升前端开发体验(三) - 项目工程化配置、规范篇
工程化配置
还是开发体验的问题,跟开发体验有关的项目配置无非就是使用 eslint、prettier、stylelint 统一代码风格。
formatting and lint
eslint、prettier、stylelint 怎么配这里就不说了,网上文章太多了。想说的是eslint rule 'prettier/prettier': 'error'一定要开启,以及 stylelint rule 'prettier/prettier': true 也一定要开启。
虽然配置了eslint、prettier、stylelint,但是可能你队友的编辑器并没有装相应的插件,格式化用的也不是 prettier,然后他修改一行代码顺便把整个文件格式化了一遍。所以还得配置 husky + lint-staged,提交代码的时候按规范格式化回去,不符合规范的代码不允许提交。
如果公司的电脑配置还行的话,可以开发阶段就做相应的 lint, 把错误抛出来,中断编译。webpack 可以使用 eslint-loader,stylelint-webpack-plugin;vite 可以使用 vite-plugin-eslint,vite-plugin-stylelint;vue-cli 配置几个参数就可以开启,具体看文档。
ts-check
什么是 ts-check?举个例子,有一个后端接口的某个字段名称变了,由 user_name 改为了 userName,如果没有配置开发阶段进行 ts-check 并把错误抛出来,那么只能全局查找调用接口的地方去修改,如果改漏了,那就喜提一个 BUG。
ts-check 可以开发阶段就做,也可以提交代码的时候做。开发阶段 webpack 安装 fork-ts-checker-webpack-plugin ,vite 也是找相应的插件(暂时没找到用的比较多的)。提交代码的时候,结合 husky 做一次全量的 check (比较耗时),react 项目执行 tsc --noEmit --skipLibCheck,vue 项目执行 vue-tsc --noEmit --skipLibCheck
ts-check 能好用的前提是你的项目是 TS 写的,接口返回值有具体的类型定义,而不是 any。
代码规范
主要讲讲 model,service,presenter,view 这几层的代码规范,之前的文章也有简单提到过,这里做个归纳。
model
import { reactive, ref } from "vue";
import { IFetchUserListResult } from "./api";
export const useModel = () => {
const userList = reactive<{ value: IFetchUserListResult["result"]["rows"] }>({
value: [],
});
return {
userList,
};
};
export type Model = ReturnType<typeof useModel>;
- 每一个字段都要声明类型,不要因为字段多就用
Object,[k: string]: string | number | boolean,Record<string, string>之类的来偷懒。 - 可以包含一些简单逻辑的方法,比如重置 state。
- vue 中字段声明可以移到 useModel 外面,达到状态共享的作用,在 useModel 中 return 出去使用。
service
- react 技术栈,presenter 层调用的时候使用单例方法,避免每次re-render 都生成新的实例。
- service 要尽量保持“整洁”,不要直接调用特定环境,端的 API,尽量遵循 依赖倒置原则。比如 fetch,WebSocket,cookie,localStorage 等 web 端原生 API 以及 APP 端 JSbridge,不建议直接调用,而是抽象,封装成单独的库或者工具函数,保证是可替换,容易 mock 的。Taro,uni-app 等框架的 API 也不要直接调用,可以放到 presenter 层。组件库提供的命令式调用的组件,也不要使用。
- service 方法的入参要合理,不要为了适配组件库而声明不合理的参数,比如某个组件返回 string[] 类型的数据,实际只需要数组第一个元素,参数声明为 string 类型即可。2个以上参数改为使用对象。
- 业务不复杂可以省略 service 层。
service 保证足够的“整洁”,model 和 service 是可以直接进行单元测试的,不需要去关心是 web 环境还是小程序环境。
import { Model } from './model';
export default class Service {
private static _indstance: Service | null = null;
private model: Model;
static single(model: Model) {
if (!Service._indstance) {
Service._indstance = new Service(model);
}
return Service._indstance;
}
constructor(model: Model) {
this.model = model;
}
}
presenter
import { message, Modal } from 'antd';
import { useModel } from './model';
import Service from './service';
const usePresenter = () => {
const model = useModel();
const service = Service.single(model);
const handlePageChange = (page: number, pageSize: number) => {
service.changePage(page, pageSize);
};
return {
model,
handlePageChange,
};
};
export default usePresenter;
- 处理 view 事件的方法以 handle 或 on 开头。
- 不要出现过多的逻辑。
- 生成 jsx 片段的方法以 render 开头,比如 renderXXX。
- 不管是 react 还是 vue 不要解构 model,直接 model.xxxx 的方式使用。
view
- 组件 props 写完整类型。
- jsx 不要出现嵌套的三元运算。
- 尽量所有的逻辑都放到 presenter 中。
- 不要解构 presenter 以及 model,以 presenter.xxx,model.xxxx 方式调用。
store
- 不要在外层去使用内层的 store。
接口请求方法
- 封装的接口请求方法支持泛型
import axios, { AxiosRequestConfig } from "axios";
import { message } from "ant-design-vue";
const instance = axios.create({
timeout: 30 * 1000,
});
// 请求拦截
instance.interceptors.request.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
},
);
// 响应拦截
instance.interceptors.response.use(
(res) => {
return Promise.resolve(res.data);
},
(error) => {
message.error(error.message || "网络异常");
return Promise.reject(error);
},
);
type Request = <T = unknown>(config: AxiosRequestConfig) => Promise<T>;
export const request = instance.request as Request;
- 具体接口的请求方法,入参及返回值都要声明类型,参数量最多两个,body 数据命名为 data,非 body 数据命名为 params,都是对象类型。
- 参数类型及返回值类型都声明放在一起,不需要用单独的文件夹去放,觉得代码太多不好看可以用 region 注释块折叠起来(vscode 支持)。
- 接口请求方法以 fetch,del,submit,post 等单词开头。
- 建议接口请求方法直接放在组件同级目录里,建一个 api.ts 的文件。很多人都习惯把接口请求统一放到一个 servcies 的文件夹里,但是复用的接口又有几个呢,维护代码的时候在编辑器上跨一大段距离来回切换文件夹真的是很糟糕的开发体验。
// #region 编辑用户
export interface IEditUserResult {
code: number;
msg: string;
result: boolean;
}
export interface IEditUserParams {
id: number;
}
export interface IEditUserData {
name: string;
age: number;
mobile: string;
address?: string;
tags?: string[];
}
/**
* 编辑用户
* http://yapi.smart-xwork.cn/project/129987/interface/api/1796964
* @author 划水摸鱼糊屎工程师
*
* @param {IEditUserParams} params
* @param {IEditUserData} data
* @returns
*/
export function editUser(params: IEditUserParams, data: IEditUserData) {
return request<IEditUserResult>(`${env.API_HOST}/api/user/edit`, {
method: 'POST',
data,
params,
});
}
// #endregion
上面代码是工具生成的,下篇说说提升开发效率及体验的工具。
如何结合整洁架构和MVP模式提升前端开发体验(三) - 项目工程化配置、规范篇的更多相关文章
- 如何结合整洁架构和MVP模式提升前端开发体验(二) - 代码实现篇
上一篇文章介绍了整体架构,接下来说说怎么按照上图的分层结构实现下面的增删改查的功能. 代码结构 vue userManage └── List ├── api.ts ├── EditModal │ ├ ...
- 如何结合整洁架构和MVP模式提升前端开发体验 - 整体架构篇
本文不详细介绍什么是整洁架构以及 MVP 模式,自行查看文章结尾相关链接文章. 整洁架构粗略介绍 下图为整洁架构最原始的结构图: Entities/Models:实体层,官方说法就是封装了企业里最通用 ...
- MVP模式在Android开发中的应用
一.MVP介绍 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互.同一 ...
- 基于ASP.NET WPF技术及MVP模式实战太平人寿客户管理项目开发(Repository模式)
亲爱的网友,我这里有套课程想和大家分享,假设对这个课程有兴趣的.能够加我的QQ2059055336和我联系. 课程背景 本课程是教授使用WPF.ADO.NET.MVVM技术来实现太平人寿保险有限公司 ...
- [译]Google官方关于Android架构中MVP模式的示例
概述 该示例(TODO-MVP)是后续各种示例演变的基础,它主要演示了在不带架构性框架的情况下实现M-V-P模式.其采用手动依赖注入的方式来提供本地数据源和远程数据源仓库.异步任务通过回调处理. 注意 ...
- Google官方关于Android架构中MVP模式的示例续-DataBinding
基于前面的TODO示例,使用Data Binding库来显示数据并绑定UI元素的响应动作. 这个示例并未严格遵循 Model-View-ViewModel 或 Model-View-Presenter ...
- 通过Swagger文档生成前端service文件,提升前端开发效率
在企业级的项目开发过程中,一般会采用前后端分离的开发方式,前后端通过api接口进行通信,所以接口文档就显得十分的重要. 目前大多数的公司都会引入Swagger来自动生成文档,大大提高了前后端分离开发的 ...
- .Net平台-MVP模式初探(一)
为什么要写这篇文章 笔者当前正在负责研究所中一个项目,这个项目基于.NET平台,初步拟采用C/S部署体系,所以选择了Windows Forms作为其UI.经过几此迭代,我们发现了一个问题:虽然业务逻辑 ...
- 说说Android的MVP模式
http://toughcoder.NET/blog/2015/11/29/understanding-Android-mvp-pattern/ 安卓应用开发是一个看似容易,实则很难的一门苦活儿.上手 ...
随机推荐
- VirtualBox虚拟机安装Ubuntu系统后,增加内存空间和处理器核心数
对于Linux爱好者而言,初次使用虚拟机时,一般都会使用默认的设置,例如硬盘空间.内存空间等等. 而往往在熟悉之后,安装了某些必要的软件,或者熟悉了实际的开发场景后,却发现原本给虚拟机分配的物理资源是 ...
- Java Web servlet 详解
执行原理 当服务器接收到客户端浏览器的访问时,会解析请求的URL路径,获取访问的Servlet的资源路径 查找web.xml文件,看是否有对应的<url-pattern>标签体内容 如果有 ...
- Java8 函数式【1】:一文读懂逆变
Java8 函数式[1]:一文读懂逆变 禁止转载 pure function 协变 逆变 Java8 引入了函数式接口,从此方法传参可以传递函数了,有人说: 不就是传一个方法吗,语法糖! lambda ...
- @Async注解的坑,小心
大家好,我是三友. 背景 前段时间,一个同事小姐姐跟我说她的项目起不来了,让我帮忙看一下,本着助人为乐的精神,这个忙肯定要去帮. 于是,我在她的控制台发现了如下的异常信息: Exception in ...
- Linux为所有用户安装Miniconda
如果以root身份默认安装,后续普通用户再安装的话,是直接用不起来的,需要改些东西,所以在安装时最好全局安装,所有用户都可用 执行安装脚本:sudo bash Miniconda3-latest-Li ...
- Pytorch分布式训练
用单机单卡训练模型的时代已经过去,单机多卡已经成为主流配置.如何最大化发挥多卡的作用呢?本文介绍Pytorch中的DistributedDataParallel方法. 1. DataParallel ...
- 创建私有CA,我就用openSSL
目录 简介 搭建root CA 生成root CA 使用CRL 使用OSCP 总结 简介 一般情况下我们使用的证书都是由第三方权威机构来颁发的,如果我们有一个新的https网站,我们需要申请一个世界范 ...
- 新建 Microsoft Office Word 文档 来源:牛客网
题目 链接:https://ac.nowcoder.com/acm/contest/28886/1015 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其 ...
- Dubbo源码(二) - SPI源码
前情提要 假设你已经知道Dubbo SPI的使用方式,不知道的请出门左转: Dubbo源码(一) - SPI使用 Dubbo源码地址: apache/dubbo 本文使用版本:2.6.x 测试Demo ...
- DQL分组查询和DQL分页查询
分组查询: 1.语法:group by 分组字段: 2.注意: 分组之后查询的字符按:分组字段.聚合函数 where 和having 的区别 where再分组前进行限定,如果不满足条件则不参与分组.h ...