工程化配置

还是开发体验的问题,跟开发体验有关的项目配置无非就是使用 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>;
  1. 每一个字段都要声明类型,不要因为字段多就用 Object[k: string]: string | number | booleanRecord<string, string> 之类的来偷懒。
  2. 可以包含一些简单逻辑的方法,比如重置 state。
  3. vue 中字段声明可以移到 useModel 外面,达到状态共享的作用,在 useModel 中 return 出去使用。

service

  1. react 技术栈,presenter 层调用的时候使用单例方法,避免每次re-render 都生成新的实例。
  2. service 要尽量保持“整洁”,不要直接调用特定环境,端的 API,尽量遵循 依赖倒置原则。比如 fetch,WebSocket,cookie,localStorage 等 web 端原生 API 以及 APP 端 JSbridge,不建议直接调用,而是抽象,封装成单独的库或者工具函数,保证是可替换,容易 mock 的。Taro,uni-app 等框架的 API 也不要直接调用,可以放到 presenter 层。组件库提供的命令式调用的组件,也不要使用。
  3. service 方法的入参要合理,不要为了适配组件库而声明不合理的参数,比如某个组件返回 string[] 类型的数据,实际只需要数组第一个元素,参数声明为 string 类型即可。2个以上参数改为使用对象。
  4. 业务不复杂可以省略 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;
  1. 处理 view 事件的方法以 handle 或 on 开头。
  2. 不要出现过多的逻辑。
  3. 生成 jsx 片段的方法以 render 开头,比如 renderXXX。
  4. 不管是 react 还是 vue 不要解构 model,直接 model.xxxx 的方式使用。

view

  1. 组件 props 写完整类型。
  2. jsx 不要出现嵌套的三元运算。
  3. 尽量所有的逻辑都放到 presenter 中。
  4. 不要解构 presenter 以及 model,以 presenter.xxx,model.xxxx 方式调用。

store

  1. 不要在外层去使用内层的 store。

接口请求方法

  1. 封装的接口请求方法支持泛型
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;
  1. 具体接口的请求方法,入参及返回值都要声明类型,参数量最多两个,body 数据命名为 data,非 body 数据命名为 params,都是对象类型。
  2. 参数类型及返回值类型都声明放在一起,不需要用单独的文件夹去放,觉得代码太多不好看可以用 region 注释块折叠起来(vscode 支持)。
  3. 接口请求方法以 fetch,del,submit,post 等单词开头。
  4. 建议接口请求方法直接放在组件同级目录里,建一个 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模式提升前端开发体验(三) - 项目工程化配置、规范篇的更多相关文章

  1. 如何结合整洁架构和MVP模式提升前端开发体验(二) - 代码实现篇

    上一篇文章介绍了整体架构,接下来说说怎么按照上图的分层结构实现下面的增删改查的功能. 代码结构 vue userManage └── List ├── api.ts ├── EditModal │ ├ ...

  2. 如何结合整洁架构和MVP模式提升前端开发体验 - 整体架构篇

    本文不详细介绍什么是整洁架构以及 MVP 模式,自行查看文章结尾相关链接文章. 整洁架构粗略介绍 下图为整洁架构最原始的结构图: Entities/Models:实体层,官方说法就是封装了企业里最通用 ...

  3. MVP模式在Android开发中的应用

    一.MVP介绍      随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互.同一 ...

  4. 基于ASP.NET WPF技术及MVP模式实战太平人寿客户管理项目开发(Repository模式)

    亲爱的网友,我这里有套课程想和大家分享,假设对这个课程有兴趣的.能够加我的QQ2059055336和我联系.  课程背景 本课程是教授使用WPF.ADO.NET.MVVM技术来实现太平人寿保险有限公司 ...

  5. [译]Google官方关于Android架构中MVP模式的示例

    概述 该示例(TODO-MVP)是后续各种示例演变的基础,它主要演示了在不带架构性框架的情况下实现M-V-P模式.其采用手动依赖注入的方式来提供本地数据源和远程数据源仓库.异步任务通过回调处理. 注意 ...

  6. Google官方关于Android架构中MVP模式的示例续-DataBinding

    基于前面的TODO示例,使用Data Binding库来显示数据并绑定UI元素的响应动作. 这个示例并未严格遵循 Model-View-ViewModel 或 Model-View-Presenter ...

  7. 通过Swagger文档生成前端service文件,提升前端开发效率

    在企业级的项目开发过程中,一般会采用前后端分离的开发方式,前后端通过api接口进行通信,所以接口文档就显得十分的重要. 目前大多数的公司都会引入Swagger来自动生成文档,大大提高了前后端分离开发的 ...

  8. .Net平台-MVP模式初探(一)

    为什么要写这篇文章 笔者当前正在负责研究所中一个项目,这个项目基于.NET平台,初步拟采用C/S部署体系,所以选择了Windows Forms作为其UI.经过几此迭代,我们发现了一个问题:虽然业务逻辑 ...

  9. 说说Android的MVP模式

    http://toughcoder.NET/blog/2015/11/29/understanding-Android-mvp-pattern/ 安卓应用开发是一个看似容易,实则很难的一门苦活儿.上手 ...

随机推荐

  1. 【python基础】第02回 计算机基础2

    上节内容回顾 1.绝对路径与相对路径 1.路径的概念 用来标识资源的位置 2.绝对路径 类似于全球GPS定位(给到任何人都可以顺利的找到相应的资源) eg: D:\aaa\a.txt 3.相对路径 需 ...

  2. Python程序入口 __name__ == ‘__main__‘ 有重要功能(多线程)而非编程习惯

    文章来源于互联网(https://jq.qq.com/?_wv=1027&k=rX9CWKg4) 在Python中,被称为「程序的入口」的 if name =='main': 总是出现在各种示 ...

  3. 如何用Fiddler对APP进行网络测试

    什么是Fiddler Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的"进出"Fiddler的数据(指co ...

  4. 线程池的概念&原理和线程池的代码实现

    线程池:一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源.工作原理:可以用一张图来简洁明了说明: 合理利用线程池能够带来三个好处∶1.降低 ...

  5. jdbc 05: 查询结果集

    jdbc连接mysql,查询结果集 package com.examples.jdbc.o5_结果集查询; import java.sql.*; import java.util.ResourceBu ...

  6. RabbitMQ细说之开篇

    前言 关于消息中间件的应用场景,小伙伴们应该都耳熟能详了吧,比如经常提到的削峰填谷.分布式事务.异步业务处理.大数据分析等等,分布式消息队列成为其中比较关键的桥梁,也就意味着小伙伴们得掌握相关技能:当 ...

  7. 字节输出流的续写和换行和字节输入流InputStream类&FileInputStream类介绍

    数据追加续写 每次程序运行,创建输出流对象,都会清空目标文件中的数据.如何保目标文件中的数据,还能继续添加新数据呢? public FileOutputStream(File file,boolean ...

  8. MySQL建表DDL规范(欢迎补充)

    MySQL建表DDL规范(欢迎补充) 基本规范: 表名和字段名全大写,一般表名以T开头 脚本需支持可重复执行,带IF NOT EXISTS ,但不可带DROP语句 字符集使用utf8mb4 (CHAR ...

  9. 2521-Shiro系列-基本使用

    在springboot中使用SHiro作为安全框架,非常简单,灵活性也比较高 在springboot中使用Shiro不用像官网教程那样,官网是将配置文件写在INI文件和XML配置中 springboo ...

  10. mysql导出bug备注

    注:yiicms库和area表均存在