Skip to content

Instantly share code, notes, and snippets.

@ywwwtseng
Last active June 15, 2021 03:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ywwwtseng/529556a3ca6d2cb8adde140a060a6ceb to your computer and use it in GitHub Desktop.
Save ywwwtseng/529556a3ca6d2cb8adde140a060a6ceb to your computer and use it in GitHub Desktop.
clean code for vue

Clean Code For Vue

✌️ Vue

聲明式渲染

善用 computed 屬性去描述一些條件式或可讀性不高的程式碼

<template>
  <div v-if="isLoggedInAndDataLoaded">
    Logging !
  </div>
</template>

<script>
export default {
  name: 'Home',
  computed: {
    menu() {
      return this.$store.state.app.menu;
    },
    isLoggedInAndDataLoaded() {
      return this.$store.state.auth.isLoggedIn && this.menu;
    },
  },
}
</script>

命名規則

文件式組件 (*.vue) 使用 PascalCase 命名方式.

components/
|- MyComponent.vue

基礎組件 (無邏輯或無條件的組件) 命名方式, 以前綴 Base、App 或 V 命名.

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue

單例組件 (單個實例組件) 以前綴 The 命名表示唯一性.

components/
|- TheHeading.vue
|- TheSidebar.vue

緊密耦合的組件應以父組件名為前綴命名.

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue

組件應以一般描述的單詞為開頭, 描述性的修飾詞結尾

Bad:

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue

Good:

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue

排序 Vue 組件選項


1. Side Effects (觸發組件外的影響)

el

2. Global Awareness (暴露資訊給組件外)

name parent

3. Component Type (組件類型)

functional

4. Template Modifiers (改變組件編譯方式)

delimiters comments

5. Template Dependencies (組件外部資源)

components directives filters

6. Composition (擴展組件功能)

extends mixins

7. Interface

inheritAttrs model props / propsData

8. Local State

data computed

9. Events (通過響應式事件觸發的 Callback)

watch

Lifecycle Events

beforeCreate created beforeMount mounted beforeUpdate updated activated deactivated beforeDestroy destroyed

10. Non-Reactive Properties

methods

11. Rendering

template / render renderError


☕ Javascript

變數

使用有意義且可拼音的命名變數

const currentDate = moment().format('YYYY/MM/DD')

給變量賦予意義提高代碼可讀性及可搜尋性

// Declare them as capitalized named constants.
const MILLISECONDS_IN_A_DAY = 86400000

setTimeout(blastOff, MILLISECONDS_IN_A_DAY)

給變量賦予意義提高代碼可讀性及可搜尋性

// Declare them as capitalized named constants.
const MILLISECONDS_IN_A_DAY = 86400000

setTimeout(blastOff, MILLISECONDS_IN_A_DAY)

說明變數名稱提高代碼可讀性

Bad:

const address = 'One Infinite Loop, Cupertino 95014'
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/
saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2])

Good:

const address = 'One Infinite Loop, Cupertino 95014'
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/
const [, city, zipCode] = address.match(cityZipCodeRegex) || []
saveCityZipCode(city, zipCode)

函式

函式變數數量不要超過2個,當超過2個表示這個函式正在做複雜的事

  • 一個函式不要做太多事情,拆分功能提高復用性及可測試性
  • 函數名稱明確告訴我們它正在做什麼事
  • 利用函式減少重複的代碼,提高維護性

一個函式專注做一件事

Bad:

function emailClients(clients) {
  clients.forEach((client) => {
    const clientRecord = database.lookup(client)
    if (clientRecord.isActive()) {
      email(client)
    }
  });
}

Good:

function emailActiveClients(clients) {
  clients
    .filter(isActiveClient)
    .forEach(email)
}

function isActiveClient(client) {
  const clientRecord = database.lookup(client)
  return clientRecord.isActive()
}

避免 Side Effects (Part 1)

函式做了"接受一個值並返回一個結果"以外的事,可能會產生副作用,像是對原本的值進行操作。比如說寫入文件、更動全域變數或影響到其他部分。

Bad:

let name = 'Ryan McDermott'

function splitInfoFirstAndLastName() {
  name = name.split(' ')
}

splitInfoFirstAndLastName()

console.log(name)  // ['Ryan', 'McDermott']

Good:

function splitInfoFirstAndLastName(name) {
   return name.split(' ')
}

const name = 'Ryan McDermott'
const newName = splitInfoFirstAndLastName(name)

console.log(name) // 'Ryan McDermott'
console.log(newName) // ['Ryan', 'McDermott']

避免 Side Effects (Part 2)

In JavaScript, primitives are passed by value and objects/arrays are passed by reference.

Bad:

const addItemToCart = (cart, item) => {
  cart.push({item, date: Date.now()})
}

Good:

const addItemToCart = (cart, item) => {
  return [...cart, {item, date: Date.now()}]
}

複製巨大物件是一件成本很重的事情,好在有一些很棒的工具,像是 Immutable.js,降低記憶體的消耗,有效配制記憶體。

開發

熱愛函數式編程而非命令式編程

JavaScript isn't a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages can be cleaner and easier to test. Favor this style of programming when you can.

Bad:

const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
]

let totalOutput = 0

for (let i = 0; i < programmerOutput.length; i++) {
  totalOutput += programmerOutput[i].linesOfCode
}

Good:

const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
];

const totalOutput = programmerOutput
  .map(output => output.linesOfCode)
  .reduce((totalLines, lines) => totalLines + lines)

條件語句

  • 封裝條件語句
  • 避免否定條件語句
  • 避免使用條件判斷

使用 ES6 的 classes 而不是 ES5 的 Function

function 在繼承及建構方面可讀性較差,當需要使用繼承時優先選擇 classes,但建立的對象過於複雜時,效能考量最好選擇更小的 fumction 而非 classes

參考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment