takeaway

I divide code specifications into hard and soft.

The so-called hard specification is a specification that can be enforced by tools, such as the number of characters indented, the number of letters per line, the semicolon at the end of a sentence, etc. Lint can be used to guarantee the landing effect;

Soft specifications refer to rules such as method naming, file naming, and routine code that cannot be enforced by tools. This kind of rule can only be regarded as a kind of agreement, more depends on the consciousness, in addition to CR, there is no other effective method to ensure its implementation effect.

Obviously, good code specifications can improve project maintainability and reduce team communication and switching costs; For individuals, good coding habits can improve their professionalism and even architectural design (literally). This article will list some general soft coding specifications, a summary of the various team specifications I have experienced over the years, for your reference

“Code is written for people, not machines, but by the way computers can execute it.” — Construction and Interpretation of Computer Programs (SICP for short)

Code specification

Directory file

The file name

  • All the file names are lowercase and are connected with hyphens (-). Only component files (which actually inherit from class files) are named with a large hump
// GOOD
import MyComponent from './MyComponent';


// BAD
import MyComponent from './myComponent';
import MyComponent from './my-component';
Copy the code
  • Common file names (also used as folder names, usually plural)
    • Index.js – entry file;
    • Constants. Js – Constant;
    • Services.js – Network request;
    • Utils.js – utility functions;
    • Routes.js – routes;
    • Style.css – style;
    • Types. Ts-ts declaration;

The directory structure

. ├ ─ ─ the config# Project profile│ ├ ─ ─ webpack. Local. Js │ ├ ─ ─ webpack. Dev. Js │ ├ ─ ─ webpack. Prod. Js │ └ ─ ─ webpack. Config. Js ├ ─ ─ the public# Files that do not need to be compiled│ ├─ ├─ favicon. Ico │ ├─ manifest.json │ ├─ exercises# Project script file│ ├ ─ ─ build. Js │ └ ─ ─ the deploy. Js ├ ─ ─ the SRC │ ├ ─ ─ assets# static file│ ├─ ├─ ├─ ├─ ├─ exercises# components│ │ ├ ─ ─ MyComponent1 │ │ │ ├ ─ ─ index. The TSX │ │ │ └ ─ ─ style.css. CSS │ │ └ ─ ─ MyComponent2. The TSX │ ├ ─ ─ locales# international│ │ ├ ─ ─ en - US. Json │ │ └ ─ ─ useful - CN. Json │ ├ ─ ─ pages# page│ │ └ ─ ─ Home │ │ ├ ─ ─ components │ │ ├ ─ ─ index. The TSX │ │ ├ ─ ─ services. The ts# API request│ │ ├ ─ ─ style.css. CSS │ │ └ ─ ─ utils. Ts │ ├ ─ ─ the ts# constants│ ├─ ├─ routes# routing│ ├─ ├─ ├─ ├─ exercises# public method├ ─ ─. Gitignore ├ ─ ─. NPMRCNPM source control├ ─ ─ yarnrc# YARN source control├ ─ ─ yarn. The lockPackage-lock. json is usually used as a binary├ ─ ─ package. Json └ ─ ─ package - lock. JsonDo not ignore the lock file to ensure dependency stability
Copy the code

The code

variable

  • Constant names are all uppercase and underlined
// GOOD
const DEFAULT_PAGE_SIZE = 10;


// BAD
const defaultPageSize = 10;
Copy the code
  • Boolean variable name suggestions: isXXX, shouldXXX, canXXX, hasXXX, XXXable
// GOOD
const { isModalShow, shouldShowModal, canRun, hasReaded, closeable } = { true.false.true.false.false }


// BAD
const { showModal, modalClose } = { true.false }
Copy the code

function

  • Function names always start with a verb;
// GOOD
function showModal() {}
function changeStatus() {}


// BAD
function modalShow() {}
function statusChange() {}
Copy the code
  • The parameters are controlled within 3 (inclusive)
// GOOD
function createMenu({ title, body, buttonText, cancellable }) {
  // ...
}
 
createMenu({
  title: "Foo".body: "Bar".buttonText: "Baz".cancellable: true
});
 
 
// BAD
function createMenu(title, body, buttonText, cancellable) {
  // ...
}
 
createMenu("Foo"."Bar"."Baz".true);
Copy the code
  • Use pure functions to avoid side effects
// GOOD
function splitIntoFirstAndLastName(name) {
  return name.split("");
}
 
const name = "Ryan McDermott";
const newName = splitIntoFirstAndLastName(name);
 
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
 
 
// BAD
let name = "Ryan McDermott";
 
function splitIntoFirstAndLastName() {
  name = name.split("");
}
 
splitIntoFirstAndLastName();
 
console.log(name); // ['Ryan', 'McDermott'];
Copy the code
  • Note the handling of reference type data, which typically returns new data from cloneDeep
// GOOD
const addItemToCart = (cart, item) = > {
  // return [...cart, { item, date: Date.now() }]; OR below:
  const copyCart = _.cloneDeep(cart);
  copyCart.push({ item, date: Date.now() });
  return copyCart;
};
 
 
// BAD
const addItemToCart = (cart, item) = > {
  cart.push({ item, date: Date.now() });
};
Copy the code
  • API request functions are recommended to be prefixed by request Method to avoid confusion with outer functions. (Note: It is recommended to prefix FETCH in get requests to avoid other common conflicts.)
// GOOD
function submitForm() {
  postSubmitForm(values); / / API request
}

function getUserInfo() {
  fetchUser(); / / API request
}


// BAD
function submit() {
  submitForm(values); / / API request
}
function getUserInfo() {
  getUser(); / / API request
}
Copy the code
  • It is recommended to name event callback functions declared within a component in the handleXXX format rather than onXXX. Avoid confusion with the callback function passed to props
// GOOD
function handleSubmit() {
  props.onSubmit();
}
<button onClick={handleSubmit}>Submit</button>


// BAD
function onSubmit() {
  props.onSubmit();
}
<button onClick={onSubmit}>Submit</button>
Copy the code

readability

  • Display declarations are recommended for type conversions
// GOOD
Boolean(a); Number(b); String(c);


// BAD!!!!! a; +b;`${c}`; ' ' + c;
Copy the code
  • Code table to ensure semantic, prohibit Magic Number
// GOOD
enum YN { no, yes } // ts
const STATUS = { disabled: 0; active: 1; running: 2; }
if (status === STATUS.active) // code


// BAD
if (status === 2) // code
Copy the code
  • If the judgment condition is too long, encapsulate it
// GOOD
function shouldShowSpinner(fsm, listNode) {
  return fsm.state === "fetching" && isEmpty(listNode);
}
 
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}
 
 
// BAD
if (fsm.state === "fetching" && isEmpty(listNode)) {
  // ...
}
Copy the code
  • Use async/await, Promise instead of callback
// GOOD
import { get } from "request-promise";
import { writeFile } from "fs-extra";
 
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
  .then(body= > {
    return writeFile("article.html", body);
  })
  .then(() = > {
    console.log("File written");
  })
  .catch(err= > {
    console.error(err);
  });
 
 
// BAD
import { get } from "request";
import { writeFile } from "fs";
 
get(
  "https://en.wikipedia.org/wiki/Robert_Cecil_Martin".(requestErr, response, body) = > {
    if (requestErr) {
      console.error(requestErr);
    } else {
      writeFile("article.html", body, writeErr= > {
        if (writeErr) {
          console.error(writeErr);
        } else {
          console.log("File written"); }}); }});Copy the code
  • If possible, use switch instead of if else
  • Remove unnecessary console, debugger, comments, and deprecated code. To view code history, use GIT

Common business logic

The form

  • Character class input items should have length verification, and it should be clear whether the trim operation is required
  • Textarea input content, “display” to preserve the format, such as indentation, line feed, etc
  • To control the submission operation, use loading or Disabled to control the second click of the button

form

  • When updating data, ensure that the loading effect is enabled to prevent misoperations
  • Page number, page display number, query conditions and other conditions to obtain data are essentially the same, so unified convergence management is required
  • If the URL needs to be shared, the data of the above condition items should be maintained in the URL query. Otherwise, it is generally maintained in the sessionStorage. Use localStorage with caution
  • When the number of page displays and query conditions change, the page must be initialized to 1
  • When adding, deleting, or modifying data, it is recommended to retrieve paged list data directly to avoid a lot of trouble
  • Single-select, multi-select when triggering paging, query criteria changes, remember to clear

The interface specification

  • ID class, recommended to use string type, not recommended number, more than 17 integer js precision will be lost;
  • String (‘2021-01-01 08:00:00’) is not recommended to avoid the trouble caused by the international time zone.
  • With the API URL/api/xxxAt the beginning, other addresses are left to the front page, also convenient to set up proxy;
  • The outermost layer of Response format should be unified to facilitate encapsulation and maintenance. For example:
// TS
type Response<T = any> = PromiseThe < {code: number;
  msg: string; data: T; } >Copy the code
  • The format of parameters and return values of List requests should also be unified to facilitate encapsulation and maintenance. For example:
// TS
interface ListParams<U = any> {
  pageNo: number;
  pageSize: number; filter? : U; }type ListResponse<T = any> = PromiseThe < {code: number;
  msg: string;
  data: {
    records: T[];
    total: number; }; } >Copy the code

reference

  1. clean-code-javascript