Engineering configuration

It is still a problem of development experience. The project configuration related to development experience is nothing more than the use of eslint, prettier, and stylelint to unify the code style.

formatting and lint

How to match eslint, prettier, and stylelint will not be discussed here. There are too many articles on the Internet. What I want to say is that eslint rule 'prettier/prettier': 'error' must be turned on, and stylelint rule 'prettier/prettier': true must also be turned on.

Although eslint, prettier, and stylelint are configured, your teammate's editor may not have the corresponding plug-ins installed, and the formatting is not prettier. Then he modified a line of code to format the entire file by the way. So you have to configure husky + lint-staged, and format the code back according to the specification when submitting the code. Code that does not meet the specification is not allowed to be submitted.

If the company's computer configuration is OK, you can do the corresponding lint in the development phase, throw errors out, and interrupt the compilation. Webpack can use eslint-loader, stylelint-webpack-plugin; vite can use vite-plugin-eslint, vite-plugin-stylelint; vue-cli can be turned on by configuring a few parameters, see the documentation for details.

ts-check

What is ts-check? For example, a field name of a back-end interface has been changed from user_name to userName. If ts-check is not configured in the development phase and an error is thrown, then only the place where the interface is called can be modified globally. If it is corrected, I would like to mention a bug.

ts-check can be done during the development phase, or when the code is submitted. In the development phase, webpack installs fork-ts-checker-webpack-plugin, and vite also finds the corresponding plug-ins (there are not many useful ones for the time being). When submitting the code, do a full check in combination with husky (more time-consuming), the react project executes tsc --noEmit --skipLibCheck, and the vue project executes vue-tsc --noEmit --skipLibCheck

The premise that ts-check can be useful is that your project is written by TS, and the interface return value has a specific type definition, not any.

code specification

It mainly talks about the code specifications of the layers of model, service, presenter, and view. The previous article also briefly mentioned it. Here is a summary.

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. Each field must declare the type. Don't use Object , [k: string]: string | number | boolean , Record<string, string> because there are many fields to be lazy.
  2. Can include some simple logic methods, such as resetting state.
  3. The field declaration in vue can be moved outside of useModel to achieve the role of state sharing, and returned to use in useModel.

service

  1. In the react technology stack, the singleton method is used when the presenter layer is called to avoid generating a new instance each time re-render.
  2. The service should try to keep it "clean", do not directly call the API of a specific environment, and try to follow the principle of dependency inversion . For example, web-side native APIs such as fetch, WebSocket, cookie, localStorage, and APP-side JSbridge are not recommended to be called directly, but abstracted and encapsulated into separate libraries or tool functions, which are guaranteed to be replaceable and easy to mock. The APIs of frameworks such as Taro and uni-app should not be called directly, but can be placed in the presenter layer. Do not use the imperatively invoked components provided by the component library.
  3. The input parameters of the service method should be reasonable. Do not declare unreasonable parameters in order to adapt to the component library. For example, if a component returns data of type string[], in fact, only the first element of the array is required, and the parameter is declared as type string. 2 or more parameters use objects instead.
  4. If the business is not complicated, the service layer can be omitted.

The service ensures enough "cleanliness", and the model and service can be directly unit tested, without needing to care whether it is a web environment or a small program environment.

 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. Methods for handling view events begin with handle or on.
  2. Don't show too much logic.
  3. Methods that generate jsx fragments start with render, such as renderXXX.
  4. Whether it's react or vue, don't deconstruct the model and use it directly in model.xxxx.

view

  1. Component props write full type.
  2. jsx Do not have nested ternary operations.
  3. Try to put all the logic in the presenter.
  4. Don't deconstruct presenter and model, call it as presenter.xxx, model.xxxx.

store

  1. Don't use the inner store on the outer layer.

interface request method

  1. The encapsulated interface request method supports generics
 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. For the request method of the specific interface, the input parameters and return values must be declared with the type, and the number of parameters is at most two. The body data is named data, and the non-body data is named params, both of which are object types.
  2. The parameter types and return value types are declared together, and there is no need to put them in a separate folder. If you feel that the code is too much, you can use the region comment block to fold it (vscode support).
  3. Interface request methods start with words like fetch, del, submit, post, etc.
  4. It is recommended that the interface request method be placed directly in the same level directory of the component, and an api.ts file should be created. Many people are accustomed to putting interface requests into a folder of services, but how many interfaces are reused? It is really bad to switch folders back and forth across a long distance in the editor when maintaining code. development experience.
 // #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

The above code is generated by the tool. The next part will talk about the tools to improve development efficiency and experience.


若邪
1.5k 声望64 粉丝

划水摸鱼糊屎