工程化配置
還是開發(fā)體驗(yàn)的問題,跟開發(fā)體驗(yàn)有關(guān)的項(xiàng)目配置無非就是使用 eslint、prettier、stylelint 統(tǒng)一代碼風(fēng)格
formatting and lint
eslint、prettier、stylelint 怎么配這里就不說了,網(wǎng)上文章太多了。想說的是eslint rule 'prettier/prettier': 'error'一定要開啟,以及 stylelint rule 'prettier/prettier': true 也一定要開啟。
雖然配置了eslint、prettier、stylelint,但是可能你隊(duì)友的編輯器并沒有裝相應(yīng)的插件,格式化用的也不是 prettier,然后他修改一行代碼順便把整個(gè)文件格式化了一遍。所以還得配置 husky + lint-staged,提交代碼的時(shí)候按規(guī)范格式化回去,不符合規(guī)范的代碼不允許提交。
如果公司的電腦配置還行的話,可以開發(fā)階段就做相應(yīng)的 lint, 把錯(cuò)誤拋出來,中斷編譯。webpack 可以使用 eslint-loader,stylelint-webpack-plugin;vite 可以使用 vite-plugin-eslint,vite-plugin-stylelint;vue-cli 配置幾個(gè)參數(shù)就可以開啟,具體看文檔。
ts-check
什么是 ts-check?舉個(gè)例子,有一個(gè)后端接口的某個(gè)字段名稱變了,由 user_name 改為了 userName,如果沒有配置開發(fā)階段進(jìn)行 ts-check 并把錯(cuò)誤拋出來,那么只能全局查找調(diào)用接口的地方去修改,如果改漏了,那就喜提一個(gè) BUG。
ts-check 可以開發(fā)階段就做,也可以提交代碼的時(shí)候做。開發(fā)階段 webpack 安裝 fork-ts-checker-webpack-plugin ,vite 也是找相應(yīng)的插件(暫時(shí)沒找到用的比較多的)。提交代碼的時(shí)候,結(jié)合 husky 做一次全量的 check (比較耗時(shí)),react 項(xiàng)目執(zhí)行 tsc --noEmit --skipLibCheck,vue 項(xiàng)目執(zhí)行 vue-tsc --noEmit --skipLibCheck
ts-check 能好用的前提是你的項(xiàng)目是 TS 寫的,接口返回值有具體的類型定義,而不是 any。
代碼規(guī)范
主要講講 model,service,presenter,view 這幾層的代碼規(guī)范,之前的文章也有簡單提到過,這里做個(gè)歸納。
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>;
- 每一個(gè)字段都要聲明類型,不要因?yàn)樽侄味嗑陀?
Object,[k: string]: string | number | boolean,Record<string, string>之類的來偷懶。 - 可以包含一些簡單邏輯的方法,比如重置 state。
- vue 中字段聲明可以移到 useModel 外面,達(dá)到狀態(tài)共享的作用,在 useModel 中 return 出去使用。
service
- react 技術(shù)棧,presenter 層調(diào)用的時(shí)候使用單例方法,避免每次re-render 都生成新的實(shí)例。
- service 要盡量保持“整潔”,不要直接調(diào)用特定環(huán)境,端的 API,盡量遵循 依賴倒置原則。比如 fetch,WebSocket,cookie,localStorage 等 web 端原生 API 以及 APP 端 JSbridge,不建議直接調(diào)用,而是抽象,封裝成單獨(dú)的庫或者工具函數(shù),保證是可替換,容易 mock 的。Taro,uni-app 等框架的 API 也不要直接調(diào)用,可以放到 presenter 層。組件庫提供的命令式調(diào)用的組件,也不要使用。
- service 方法的入?yún)⒁侠?,不要為了適配組件庫而聲明不合理的參數(shù),比如某個(gè)組件返回 string[] 類型的數(shù)據(jù),實(shí)際只需要數(shù)組第一個(gè)元素,參數(shù)聲明為 string 類型即可。2個(gè)以上參數(shù)改為使用對象。
- 業(yè)務(wù)不復(fù)雜可以省略 service 層。
service 保證足夠的“整潔”,model 和 service 是可以直接進(jìn)行單元測試的,不需要去關(guān)心是 web 環(huán)境還是小程序環(huán)境。
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 開頭。
- 不要出現(xiàn)過多的邏輯。
- 生成 jsx 片段的方法以 render 開頭,比如 renderXXX。
- 不管是 react 還是 vue 不要解構(gòu) model,直接 model.xxxx 的方式使用。
view
- 組件 props 寫完整類型。
- jsx 不要出現(xiàn)嵌套的三元運(yùn)算。
- 盡量所有的邏輯都放到 presenter 中。
- 不要解構(gòu) presenter 以及 model,以 presenter.xxx,model.xxxx 方式調(diào)用。
store
- 不要在外層去使用內(nèi)層的 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);
},
);
// 響應(yīng)攔截
instance.interceptors.response.use(
(res) => {
return Promise.resolve(res.data);
},
(error) => {
message.error(error.message || "網(wǎng)絡(luò)異常");
return Promise.reject(error);
},
);
type Request = <T = unknown>(config: AxiosRequestConfig) => Promise<T>;
export const request = instance.request as Request;
- 具體接口的請求方法,入?yún)⒓胺祷刂刀家暶黝愋?,參?shù)量最多兩個(gè),body 數(shù)據(jù)命名為 data,非 body 數(shù)據(jù)命名為 params,都是對象類型。
- 參數(shù)類型及返回值類型都聲明放在一起,不需要用單獨(dú)的文件夾去放,覺得代碼太多不好看可以用 region 注釋塊折疊起來(vscode 支持)。
- 接口請求方法以 fetch,del,submit,post 等單詞開頭。
- 建議接口請求方法直接放在組件同級(jí)目錄里,建一個(gè) api.ts 的文件。很多人都習(xí)慣把接口請求統(tǒng)一放到一個(gè) servcies 的文件夾里,但是復(fù)用的接口又有幾個(gè)呢,維護(hù)代碼的時(shí)候在編輯器上跨一大段距離來回切換文件夾真的是很糟糕的開發(fā)體驗(yàn)。
// #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
上面代碼是工具生成的,下篇說說提升開發(fā)效率及體驗(yàn)的工具。