如何结合整洁架构和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/ 安卓应用开发是一个看似容易,实则很难的一门苦活儿.上手 ...
随机推荐
- 监听 Markdown 文件并热更新 Next.js 页面
Next.js 提供了 Fast-Refresh 能力,它可以为您对 React 组件所做的编辑提供即时反馈. 但是,当你通过 Markdown 文件提供网站内容时,由于 Markdown 不是 Re ...
- <%= %> <%- %> <% %>是什么意思?
.ejs文件后缀的数据渲染,这是服务器端的.把 .html改成 .ejs, (1)<%= %>相当于html中的innerTEXT,导出不包含标签 . (2)<%- %>相当于 ...
- 关于C标准库stdarg.h
看<数据结构(C语言版)>(严蔚敏)时看到p94上面va_list啥的,看不懂了,于是整理一下这一部分的知识. 1/当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表. i ...
- java包机制
- idea201903License
License name: https://zhile.io Activation code: 3AGXEJXFK9-eyJsaWNlbnNlSWQiOiIzQUdYRUpYRks5IiwibGljZ ...
- Vue生命周期和MVVM框架
生命周期 组件从开始到结束的全过程 创建阶段:beforeCreate.created 挂载阶段:beforeMount.mounted 更新阶段:beforeUpdate.updated 销毁阶段: ...
- 5-12 RabbitMQ 消息队列
RabbitMQ 什么是RabbitMQ RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现. AMQP :Advanced Message Queue,高级消息队列协议.它是 ...
- ooday02构造方法_this_引用类型数组
笔记: 构造方法:构造函数.构造器.构建器---------复用给成员变量赋初值代码 作用:给成员变量赋初始值 与类同名,没有返回值类型(连void都没有) 在创建(new)对象时被自动调用 若自己不 ...
- K阶斐波那契数列--------西工大NOJ习题.10
K阶斐波那契数列--------西工大NOJ习题.10 原创不易,转载请说明出处!!! 科普:k阶斐波那契数列的0到n-1项需要有初始值. 其中,0到n-2项初始化为0,第n-1项初始化为1. 在这道 ...
- springmvc源码笔记-HandlerMapping注入
在springmvc中,如何根据url找到controller以及对应方法,依赖的是HandlerMapping接口的getHandler方法 在spring容器中默认注册的HandlerMappin ...