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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。