如何結(jié)合整潔架構(gòu)和MVP模式提升前端開(kāi)發(fā)體驗(yàn)(三) - 項(xiàng)目工程化配置蟹地、規(guī)范篇

工程化配置

還是開(kāi)發(fā)體驗(yàn)的問(wèn)題,跟開(kāi)發(fā)體驗(yàn)有關(guān)的項(xiàng)目配置無(wú)非就是使用 eslint藤为、prettier怪与、stylelint 統(tǒng)一代碼風(fēng)格

formatting and lint

eslint、prettier缅疟、stylelint 怎么配這里就不說(shuō)了分别,網(wǎng)上文章太多了遍愿。想說(shuō)的是eslint rule 'prettier/prettier': 'error'一定要開(kāi)啟,以及 stylelint rule 'prettier/prettier': true 也一定要開(kāi)啟耘斩。

雖然配置了eslint沼填、prettier、stylelint括授,但是可能你隊(duì)友的編輯器并沒(méi)有裝相應(yīng)的插件坞笙,格式化用的也不是 prettier,然后他修改一行代碼順便把整個(gè)文件格式化了一遍刽脖。所以還得配置 husky + lint-staged羞海,提交代碼的時(shí)候按規(guī)范格式化回去,不符合規(guī)范的代碼不允許提交曲管。

如果公司的電腦配置還行的話却邓,可以開(kāi)發(fā)階段就做相應(yīng)的 lint, 把錯(cuò)誤拋出來(lái)院水,中斷編譯腊徙。webpack 可以使用 eslint-loader,stylelint-webpack-plugin檬某;vite 可以使用 vite-plugin-eslint撬腾,vite-plugin-stylelint;vue-cli 配置幾個(gè)參數(shù)就可以開(kāi)啟恢恼,具體看文檔民傻。

ts-check

什么是 ts-check?舉個(gè)例子场斑,有一個(gè)后端接口的某個(gè)字段名稱變了漓踢,由 user_name 改為了 userName,如果沒(méi)有配置開(kāi)發(fā)階段進(jìn)行 ts-check 并把錯(cuò)誤拋出來(lái)漏隐,那么只能全局查找調(diào)用接口的地方去修改喧半,如果改漏了,那就喜提一個(gè) BUG青责。

ts-check 可以開(kāi)發(fā)階段就做挺据,也可以提交代碼的時(shí)候做。開(kāi)發(fā)階段 webpack 安裝 fork-ts-checker-webpack-plugin 脖隶,vite 也是找相應(yīng)的插件(暫時(shí)沒(méi)找到用的比較多的)扁耐。提交代碼的時(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 寫(xiě)的,接口返回值有具體的類型定義心墅,而不是 any酿矢。

代碼規(guī)范

主要講講 model榨乎,service,presenter瘫筐,view 這幾層的代碼規(guī)范蜜暑,之前的文章也有簡(jiǎn)單提到過(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>;

  1. 每一個(gè)字段都要聲明類型肛捍,不要因?yàn)樽侄味嗑陀?Object[k: string]: string | number | boolean之众,Record<string, string> 之類的來(lái)偷懶拙毫。
  2. 可以包含一些簡(jiǎn)單邏輯的方法,比如重置 state棺禾。
  3. vue 中字段聲明可以移到 useModel 外面缀蹄,達(dá)到狀態(tài)共享的作用,在 useModel 中 return 出去使用膘婶。

service

  1. react 技術(shù)棧缺前,presenter 層調(diào)用的時(shí)候使用單例方法,避免每次re-render 都生成新的實(shí)例悬襟。
  2. service 要盡量保持“整潔”衅码,不要直接調(diào)用特定環(huán)境,端的 API脊岳,盡量遵循 依賴倒置原則逝段。比如 fetch,WebSocket割捅,cookie惹恃,localStorage 等 web 端原生 API 以及 APP 端 JSbridge,不建議直接調(diào)用棺牧,而是抽象,封裝成單獨(dú)的庫(kù)或者工具函數(shù)朗儒,保證是可替換颊乘,容易 mock 的。Taro醉锄,uni-app 等框架的 API 也不要直接調(diào)用乏悄,可以放到 presenter 層。組件庫(kù)提供的命令式調(diào)用的組件恳不,也不要使用檩小。
  3. service 方法的入?yún)⒁侠恚灰獮榱诉m配組件庫(kù)而聲明不合理的參數(shù)烟勋,比如某個(gè)組件返回 string[] 類型的數(shù)據(jù)规求,實(shí)際只需要數(shù)組第一個(gè)元素筐付,參數(shù)聲明為 string 類型即可。2個(gè)以上參數(shù)改為使用對(duì)象阻肿。
  4. 業(yè)務(wù)不復(fù)雜可以省略 service 層瓦戚。

service 保證足夠的“整潔”,model 和 service 是可以直接進(jìn)行單元測(cè)試的丛塌,不需要去關(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;

  1. 處理 view 事件的方法以 handle 或 on 開(kāi)頭。
  2. 不要出現(xiàn)過(guò)多的邏輯赴邻。
  3. 生成 jsx 片段的方法以 render 開(kāi)頭印衔,比如 renderXXX。
  4. 不管是 react 還是 vue 不要解構(gòu) model姥敛,直接 model.xxxx 的方式使用奸焙。

view

  1. 組件 props 寫(xiě)完整類型。
  2. jsx 不要出現(xiàn)嵌套的三元運(yùn)算徒溪。
  3. 盡量所有的邏輯都放到 presenter 中忿偷。
  4. 不要解構(gòu) presenter 以及 model,以 presenter.xxx臊泌,model.xxxx 方式調(diào)用鲤桥。

store

  1. 不要在外層去使用內(nèi)層的 store。

接口請(qǐng)求方法

  1. 封裝的接口請(qǐng)求方法支持泛型
import axios, { AxiosRequestConfig } from "axios";
import { message } from "ant-design-vue";

const instance = axios.create({
  timeout: 30 * 1000,
});

// 請(qǐng)求攔截
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;

  1. 具體接口的請(qǐng)求方法渠概,入?yún)⒓胺祷刂刀家暶黝愋筒璧剩瑓?shù)量最多兩個(gè),body 數(shù)據(jù)命名為 data播揪,非 body 數(shù)據(jù)命名為 params贮喧,都是對(duì)象類型。
  2. 參數(shù)類型及返回值類型都聲明放在一起猪狈,不需要用單獨(dú)的文件夾去放箱沦,覺(jué)得代碼太多不好看可以用 region 注釋塊折疊起來(lái)(vscode 支持)。
  3. 接口請(qǐng)求方法以 fetch雇庙,del谓形,submit,post 等單詞開(kāi)頭疆前。
  4. 建議接口請(qǐng)求方法直接放在組件同級(jí)目錄里寒跳,建一個(gè) api.ts 的文件。很多人都習(xí)慣把接口請(qǐng)求統(tǒng)一放到一個(gè) servcies 的文件夾里竹椒,但是復(fù)用的接口又有幾個(gè)呢童太,維護(hù)代碼的時(shí)候在編輯器上跨一大段距離來(lái)回切換文件夾真的是很糟糕的開(kāi)發(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 劃水摸魚(yú)糊屎工程師
 *
 * @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

上面代碼是工具生成的,下篇說(shuō)說(shuō)提升開(kāi)發(fā)效率及體驗(yàn)的工具书释。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末翘贮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子征冷,更是在濱河造成了極大的恐慌择膝,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件检激,死亡現(xiàn)場(chǎng)離奇詭異肴捉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)叔收,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門齿穗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人饺律,你說(shuō)我怎么就攤上這事窃页。” “怎么了复濒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵脖卖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我巧颈,道長(zhǎng)畦木,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任砸泛,我火速辦了婚禮十籍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唇礁。我一直安慰自己勾栗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上线椰,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛陶耍,可吹牛的內(nèi)容都是我干的奋蔚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼泊碑!你這毒婦竟也來(lái)了坤按?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤馒过,失蹤者是張志新(化名)和其女友劉穎臭脓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體腹忽,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡来累,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窘奏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘹锁。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖着裹,靈堂內(nèi)的尸體忽然破棺而出领猾,到底是詐尸還是另有隱情,我是刑警寧澤骇扇,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布摔竿,位于F島的核電站,受9級(jí)特大地震影響少孝,放射性物質(zhì)發(fā)生泄漏继低。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一韭山、第九天 我趴在偏房一處隱蔽的房頂上張望郁季。 院中可真熱鬧,春花似錦钱磅、人聲如沸梦裂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)年柠。三九已至,卻和暖如春褪迟,著一層夾襖步出監(jiān)牢的瞬間冗恨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工味赃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掀抹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓心俗,卻偏偏與公主長(zhǎng)得像傲武,于是被迫代替她去往敵國(guó)和親蓉驹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容