35
头图

foreword

Recently, the team members took over an "old" front-end project that was originally written by the back-end bosses. The reuse of components at the business level depends entirely on copying the same code. Let's not talk about it. After the maintenance of different bosses, the code style is even more Weird. The front-end project is still iterating and updating normally, and it is impossible to rewrite it. Faced with the same code, the two small front-ends who took over were shivering with tears in their arms. Seeing this, I can only comfort him, temporarily play the spirit of Q, standardize your new code, and then reconstruct one or two old components for each iterative development task. After this process lasts for 2-3 months, go to eslint and prettier to automatically detect syntax and formatting code.
In line with the warning of "non-standard code, two lines of tears for newcomers", the following code style cases related to JavaScript, ES6 and Vue single-file components are summarized for your reference.

Javascript code style

Use meaningful variable names

Variable names should be descriptive and meaningful, and JavaScript variables should be named in camelCase.

// bad ❌
const foo = 'JDoe@example.com'
const bar = 'John'
const age = 23
const qux = true

// good ✅
const email = 'John@example.com'
const firstName = 'John'
const age = 23
const isActive = true

Boolean variables are often required to answer specific questions, such as:

isActive
didSubscribe
hasLinkedAccount

Avoid adding unnecessary context

Do not add redundant context to variable names when an object or class already contains context names.

// bad ❌
const user = {
  userId: '296e2589-7b33-400a-b762-007b730c8e6d',
  userEmail: 'JDoe@example.com',
  userFirstName: 'John',
  userLastName: 'Doe',
  userAge: 23
}

user.userId

//good ✅
const user = {
  id: '296e2589-7b33-400a-b762-007b730c8e6d',
  email: 'JDoe@example.com',
  firstName: 'John',
  lastName: 'Doe',
  age: 23
}

user.id

Avoid hardcoding values

// bad ❌
setTimeout(clearSessionData, 900000)

//good ✅
const SESSION_DURATION_MS = 15 * 60 * 1000

setTimeout(clearSessionData, SESSION_DURATION_MS)

Use meaningful function names

The function name needs to describe what the function actually does, even if it is long. Function names usually use verbs, with the possible exception of functions returning booleans—it can take the form of a yes-or-no question, and function names should also be camelCase.

// bad ❌
function toggle() {
  // ...
}

function agreed(user) {
  // ...
}

//good ✅
function toggleThemeSwitcher() {
  // ...
}

function didAgreeToAllTerms(user) {
  // ...
}

limit the number of parameters

Although this rule may be controversial, it is best for functions to have less than 3 parameters. If there are many parameters, it may be one of the following two situations:

  • The function is doing too much and should be split.
  • The data passed to the function is related in some way and can be passed as a dedicated data structure.
// bad ❌
function sendPushNotification(title, message, image, isSilent, delayMs) {
  // ...
}

sendPushNotification('New Message', '...', 'http://...', false, 1000)

//good ✅
function sendPushNotification({ title, message, image, isSilent, delayMs }) {
  // ...
}

const notificationConfig = {
  title: 'New Message',
  message: '...',
  image: 'http://...',
  isSilent: false,
  delayMs: 1000
}

sendPushNotification(notificationConfig)

Avoid doing too much in one function

A function should do one thing at a time, which helps reduce the size and complexity of the function and makes testing, debugging, and refactoring easier.

// bad ❌
function pingUsers(users) {
  users.forEach((user) => {
    const userRecord = database.lookup(user)
    if (!userRecord.isActive()) {
      ping(user)
    }
  })
}

//good ✅
function pingInactiveUsers(users) {
  users.filter(!isUserActive).forEach(ping)
}

function isUserActive(user) {
  const userRecord = database.lookup(user)
  return userRecord.isActive()
}

Avoid using boolean flags as parameters

A function that has a boolean flag as an argument means that the function can be simplified.

// bad ❌
function createFile(name, isPublic) {
  if (isPublic) {
    fs.create(`./public/${name}`)
  } else {
    fs.create(name)
  }
}

//good ✅
function createFile(name) {
  fs.create(name)
}

function createPublicFile(name) {
  createFile(`./public/${name}`)
}

Avoid writing duplicate code

If you write repetitive code, every time the logic changes, you need to change multiple places.

// bad ❌
function renderCarsList(cars) {
  cars.forEach((car) => {
    const price = car.getPrice()
    const make = car.getMake()
    const brand = car.getBrand()
    const nbOfDoors = car.getNbOfDoors()

    render({ price, make, brand, nbOfDoors })
  })
}

function renderMotorcyclesList(motorcycles) {
  motorcycles.forEach((motorcycle) => {
    const price = motorcycle.getPrice()
    const make = motorcycle.getMake()
    const brand = motorcycle.getBrand()
    const seatHeight = motorcycle.getSeatHeight()

    render({ price, make, brand, nbOfDoors })
  })
}

//good ✅
function renderVehiclesList(vehicles) {
  vehicles.forEach((vehicle) => {
    const price = vehicle.getPrice()
    const make = vehicle.getMake()
    const brand = vehicle.getBrand()

    const data = { price, make, brand }

    switch (vehicle.type) {
      case 'car':
        data.nbOfDoors = vehicle.getNbOfDoors()
        break
      case 'motorcycle':
        data.seatHeight = vehicle.getSeatHeight()
        break
    }

    render(data)
  })
}

avoid side effects

In JavaScript , you should prefer functional mode to imperative mode. In other words, we should keep functions pure in most cases. Side effects can modify shared state and resources, causing some weird problems. All side effects should be managed centrally, for example you need to change global variables or modify files, you can write a special util to do this.

// bad ❌
let date = '21-8-2021'

function splitIntoDayMonthYear() {
  date = date.split('-')
}

splitIntoDayMonthYear()

// Another function could be expecting date as a string
console.log(date) // ['21', '8', '2021'];

//good ✅
function splitIntoDayMonthYear(date) {
  return date.split('-')
}

const date = '21-8-2021'
const newDate = splitIntoDayMonthYear(date)

// Original vlaue is intact
console.log(date) // '21-8-2021';
console.log(newDate) // ['21', '8', '2021'];

Also, if you pass a mutable value to a function, you should clone a new value and return it instead of mutating it directly.

// bad ❌
function enrollStudentInCourse(course, student) {
  course.push({ student, enrollmentDate: Date.now() })
}

//good ✅
function enrollStudentInCourse(course, student) {
  return [...course, { student, enrollmentDate: Date.now() }]
}

Use non-negative conditions

// bad ❌
function isUserNotVerified(user) {
  // ...
}

if (!isUserNotVerified(user)) {
  // ...
}

//good ✅
function isUserVerified(user) {
  // ...
}

if (isUserVerified(user)) {
  // ...
}

Use shorthand whenever possible

// bad ❌
if (isActive === true) {
  // ...
}

if (firstName !== '' && firstName !== null && firstName !== undefined) {
  // ...
}

const isUserEligible = user.isVerified() && user.didSubscribe() ? true : false

//good ✅
if (isActive) {
  // ...
}

if (!!firstName) {
  // ...
}

const isUserEligible = user.isVerified() && user.didSubscribe()

Avoid too many branches

return early will make your code linear, more readable, and less complex.

// bad ❌
function addUserService(db, user) {
  if (!db) {
    if (!db.isConnected()) {
      if (!user) {
        return db.insert('users', user)
      } else {
        throw new Error('No user')
      }
    } else {
      throw new Error('No database connection')
    }
  } else {
    throw new Error('No database')
  }
}

//good ✅
function addUserService(db, user) {
  if (!db) throw new Error('No database')
  if (!db.isConnected()) throw new Error('No database connection')
  if (!user) throw new Error('No user')

  return db.insert('users', user)
}

Prefer map over switch statement

This reduces complexity and improves performance.

// bad ❌
const getColorByStatus = (status) => {
  switch (status) {
    case 'success':
      return 'green'
    case 'failure':
      return 'red'
    case 'warning':
      return 'yellow'
    case 'loading':
    default:
      return 'blue'
  }
}

//good ✅
const statusColors = {
  success: 'green',
  failure: 'red',
  warning: 'yellow',
  loading: 'blue'
}

const getColorByStatus = (status) => statusColors[status] || 'blue'

Use optional links

const user = {
  email: 'JDoe@example.com',
  billing: {
    iban: '...',
    swift: '...',
    address: {
      street: 'Some Street Name',
      state: 'CA'
    }
  }
}

// bad ❌
const email = (user && user.email) || 'N/A'
const street = (user && user.billing && user.billing.address && user.billing.address.street) || 'N/A'
const state = (user && user.billing && user.billing.address && user.billing.address.state) || 'N/A'

//good ✅
const email = user?.email ?? 'N/A'
const street = user?.billing?.address?.street ?? 'N/A'
const street = user?.billing?.address?.state ?? 'N/A'

avoid callbacks

Callbacks are messy and can lead to deeply nested code, use Promises instead of callbacks.

// bad ❌
getUser(function (err, user) {
  getProfile(user, function (err, profile) {
    getAccount(profile, function (err, account) {
      getReports(account, function (err, reports) {
        sendStatistics(reports, function (err) {
          console.error(err)
        })
      })
    })
  })
})

//good ✅
getUser()
  .then(getProfile)
  .then(getAccount)
  .then(getReports)
  .then(sendStatistics)
  .catch((err) => console.error(err))

// or using Async/Await ✅✅

async function sendUserStatistics() {
  try {
    const user = await getUser()
    const profile = await getProfile(user)
    const account = await getAccount(profile)
    const reports = await getReports(account)
    return sendStatistics(reports)
  } catch (e) {
    console.error(err)
  }
}

Handle thrown errors and reject promises

// bad ❌
try {
  // Possible erronous code
} catch (e) {
  console.log(e)
}

//good ✅
try {
  // Possible erronous code
} catch (e) {
  // Follow the most applicable (or all):
  // 1- More suitable than console.log
  console.error(e)

  // 2- Notify user if applicable
  alertUserOfError(e)

  // 3- Report to server
  reportErrorToServer(e)

  // 4- Use a custom error handler
  throw new CustomError(e)
}

Only annotate business logic

// bad ❌
function generateHash(str) {
  // Hash variable
  let hash = 0

  // Get the length of the string
  let length = str.length

  // If the string is empty return
  if (!length) {
    return hash
  }

  // Loop through every character in the string
  for (let i = 0; i < length; i++) {
    // Get character code.
    const char = str.charCodeAt(i)

    // Make the hash
    hash = (hash << 5) - hash + char

    // Convert to 32-bit integer
    hash &= hash
  }
}

// good ✅
function generateHash(str) {
  let hash = 0
  let length = str.length
  if (!length) {
    return hash
  }

  for (let i = 0; i < length; i++) {
    const char = str.charCodeAt(i)
    hash = (hash << 5) - hash + char
    hash = hash & hash // Convert to 32bit integer
  }
  return hash
}

ES6 optimized native JS (ES5) code style

Use default parameters

// bad ❌
function printAllFilesInDirectory(dir) {
  const directory = dir || './'
  //   ...
}

// good ✅
function printAllFilesInDirectory(dir = './') {
  // ...
}

object structure value

const obj = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  e: 5
}

// bad ❌
const f = obj.a + obj.d
const g = obj.c + obj.e

// good ✅
const { a, b, c, d, e } = obj
const f = a + d
const g = c + e

The destructuring assignment of ES6 is easy to use. But note that the destructed object cannot be undefined , null . Otherwise, an error will be reported, so the deconstructed object must be given a default value.

const { a, b, c, d, e } = obj || {}

Spread operator to combine data

Merging arrays or objects is somewhat redundant with ES5 writing

const a = [1, 2, 3]
const b = [1, 5, 6]
const obj1 = {
  a: 1
}
const obj2 = {
  b: 1
}

// bad ❌
const c = a.concat(b) //[1,2,3,1,5,6]
const obj = Object.assign({}, obj1, obj2) // {a:1, b:1}

// good ✅
const c = [...new Set([...a, ...b])] //[1,2,3,5,6]
const obj = { ...obj1, ...obj2 } // {a:1, b:1}

splice characters

const name = '小明'
const score = 59

// bad ❌
let result = ''
if (score > 60) {
  result = `${name}的考试成绩及格`
} else {
  result = `${name}的考试成绩不及格`
}

// good ✅
const result = `${name}${score > 60 ? '的考试成绩及格' : '的考试成绩不及格'}`

includes replaces multiple conditional judgments

// bad ❌
f(
    type == 1 ||
    type == 2 ||
    type == 3 ||
    type == 4 ||
){
   //...
}

// good ✅
const condition = [1,2,3,4];

if( condition.includes(type) ){
   //...
}

Find an item in a list

const a = [1, 2, 3, 4, 5]

// bad ❌
const result = a.filter((item) => {
  return item === 3
})

// good ✅
const result = a.find((item) => {
  return item === 3
})

Array flattening

// bad ❌
const deps = {
  采购部: [1, 2, 3],
  人事部: [5, 8, 12],
  行政部: [5, 14, 79],
  运输部: [3, 64, 105]
}
let member = []
for (let item in deps) {
  const value = deps[item]
  if (Array.isArray(value)) {
    member = [...member, ...value]
  }
}
member = [...new Set(member)]

// good ✅
const member = Object.values(deps).flat(Infinity)

Optional chaining operator to get object property value

// bad ❌
const name = obj && obj.name

// good ✅
const name = obj?.name

dynamic object property name

// bad ❌
let obj = {}
let index = 1
let key = `topic${index}`
obj[key] = '话题内容'

// good ✅
obj[`topic${index}`] = '话题内容'

Judging not empty

// bad ❌
if (value !== null && value !== undefined && value !== '') {
  //...
}

// good ✅
if ((value ?? '') !== '') {
  //...
}

Vue component style

Vue Single File Components style guide content is excerpted from Vue Official Style Guide .

component data

The component's data must be a function.

// bad
export default {
  data: {
    foo: 'bar'
  }
};

// good
export default {
  data() {
    return {
      foo: 'bar'
    };
  }
};

Single file component file name

Filenames for single-file components should either always start with a word capitalized (PascalCase) or always be hyphenated (kebab-case).

// bad
mycomponent.vue
myComponent.vue

// good
my - component.vue
MyComponent.vue

tightly coupled component name

Subcomponents that are tightly coupled to their parent should be named with the parent's name as a prefix.

// bad
components/
|- TodoList.vue
|- TodoItem.vue
└─ TodoButton.vue

// good
components/
|- TodoList.vue
|- TodoListItem.vue
└─ TodoListItemButton.vue

self-closing components

Components with no content in a single-file component should be self-closing.

<!-- bad -->
<my-component></my-component>

<!-- good -->
<my-component />

Prop name case

When declaring a prop, its naming should always use camelCase, and in templates it should always use kebab-case.

// bad
export default {
  props: {
    'greeting-text': String
  }
};

// good
export default {
  props: {
    greetingText: String
  }
};
<!-- bad -->
<welcome-message greetingText="hi" />

<!-- good -->
<welcome-message greeting-text="hi" />

instruction abbreviation

Instruction abbreviation, use : for v-bind: and @ for v-on:

<!-- bad -->
<input v-bind:value="value" v-on:input="onInput" />

<!-- good -->
<input :value="value" @input="onInput" />

Props order

The props of the label should have a uniform order, followed by instructions, properties and events.

<my-component
  v-if="if"
  v-show="show"
  v-model="value"
  ref="ref"
  :key="key"
  :text="text"
  @input="onInput"
  @change="onChange"
/>

Order of Component Options

Component options should have a uniform order.

export default {
  name: '',

  components: {},

  props: {},

  emits: [],

  setup() {},

  data() {},

  computed: {},

  watch: {},

  created() {},

  mounted() {},

  unmounted() {},

  methods: {}
}

Blank line in component options

When there are many component options, it is recommended to add blank lines between properties.

export default {
  computed: {
    formattedValue() {
      // ...
    },

    styles() {
      // ...
    }
  },

  methods: {
    onInput() {
      // ...
    },

    onChange() {
      // ...
    }
  }
}

Order of top-level tags for single-file components

Single-file components should always keep top-level tags in the same order, with blank lines between tags.

<template> ... </template>

<script>
  /* ... */
</script>

<style>
  /* ... */
</style>

wuwhs
6k 声望2.5k 粉丝

Code for work, write for progress!