preface

No style guide will be perfect for every team or project. It is therefore advisable to make conscious adjustments based on past experience, surrounding technology stacks, or personal values.

The following is a personal finish based on the Vue Style guide

Rule categories

  1. Priority A: Necessary
  2. Priority B: Highly recommended
  3. Priority C: Recommended
  4. Priority D: Use caution

Necessary (error avoidance)

1. Component names are multiple words

The component name should consist of multiple words, except for the component App

/* bad */
export default {
  name: 'Todo'.// ...
}

/* good */
export default {
  name: 'TodoItem'.// ...
}
Copy the code

2. Prop definitions shall be as detailed as possible

Prop definitions should be as detailed as possible, specifying at least its type

/* bad */
props: ['status']

/* good */
props: {
    status: String
}
/* better */
props: {
    status: {
        type: String.required: true. }}Copy the code

3. Tov-forSet up thekey

Add unique keys, optimize diff algorithms, and maintain the state of internal components and their subtrees

/* bad */
<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>

/* good */
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>
Copy the code

4. Avoidv-ifv-forUsed together

V-if has a higher priority than V-for, which may cause problems

/* bad */
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

/* good */
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>
Copy the code

5. Set the scope for the component style

Setting scope for styles, or CSS Modules, or a dedicated prefix (a beM-like strategy) minimizes style conflicts

/* bad */
<button class="btn btn-close">x</button>
<style>
.btn-close {
  background-color: red;
}
</style>

/* good */
// scope
<button class="btn btn-close">x</button>
<style scoped>
.btn-close {
  background-color: red;
}
</style>

// CSS modules
<button :class="[$style.buttonClose]">x</button>
<style module>
.buttonClose {
  background-color: red;
}
</style>/ / BEM conventions<button class="c-Button--close">x</button>
<style>
.c-Button--close {
  background-color: red;
}
</style>
Copy the code

6. The privatepropertyThe name of the

Use module scope to ensure that private functions cannot be accessed externally. If this is not possible, add the prefix $_ with a namespace ($_yourPluginName) to avoid conflicts

/* bad */
const myGreatMixin = {
  // ...
  methods: {
    // update() {
    // _update() {
    // $update() {
    $_update() {
      // ...}}}/* good */
const myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update() {
      // ...}}}/* better */
const myGreatMixin = {
  // ...
  methods: {
    publicMethod() {
      // ...
      myPrivateFunction()
    }
  }
}
function myPrivateFunction() {
  // ...
}
export default myGreatMixin
Copy the code

Highly recommended (for code readability)

1. Component files

Try to separate each component into a file to be more specific

/* bad */
app.component('TodoList', {
  // ...
})
app.component('TodoItem', {
  // ...
})

/* good */
components/
|- TodoList.vue
|- TodoItem.vue
Copy the code

2. The file case of a single file component

Either always uppercase words (PascalCase) or always horizontal links (kebab-case)

/* bad */
components/
|- mycomponent.vue
|- myComponent.vue

/* good */
components/
|- MyComponent.vue
|- my-component.vue
Copy the code

3. Name of the basic component

Specific styles and underlying components (presentation classes, non-logical or stateless components) should all start with a specific prefix, such as Base, App, or V

/* bad */
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue

/* good */
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
Copy the code

4. Singleton component name

Components that should only have a single active instance should be named with The prefix to indicate their uniqueness. You can only use prop once per page

/* bad */
components/
|- Heading.vue
|- MySidebar.vue

/* good */
components/
|- TheHeading.vue
|- TheSidebar.vue
Copy the code

5. Tightly coupled component names

Child components that are tightly coupled to the parent component should be named prefixed with the parent component name

/* bad */
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue

/* good */
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
Copy the code

6. Order of words in component names

Component names should start with a high-level (usually generically described) word and end with a descriptive modifier

/* 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
Copy the code

7. Self-closing components

In single-file components, string templates, and JSX, components with content should be self-closing, but never in DOM templates

/* bad */
<! -- In single-file components, string templates, and JSX
<MyComponent></MyComponent>
<! -- In the DOM template -->
<my-component/>

/* good */
<! -- In single-file components, string templates, and JSX
<MyComponent/>
<! -- In the DOM template -->
<my-component></my-component>
Copy the code

8. Component names in the template are case-sensitive

For most projects, component names in single-file components and string templates should always be PascalCase, but kebab-case in DOM templates

/* bad */
<! -- In single-file components and string templates -->
<mycomponent/>
<myComponent/>
<! -- In the DOM template -->
<MyComponent></MyComponent>

/* good */
<! -- In single-file components and string templates -->
<MyComponent/>
<! -- In the DOM template -->
<my-component></my-component>/ * or * /<! -- In all places -->
<my-component></my-component>
Copy the code

9. Component name used in JS/JSX

Component names used in JS/JSX should always be PascalCase. In simpler project, using global components registered app.com ponent, can use the kebab – case

/* bad */
app.component('myComponent', {
  // ...
})
import myComponent from './MyComponent.vue'
export default {
    // name: 'myComponent',
    name: 'my-component',}/* good */
app.component('MyComponent', {
  // ...
})
app.component('my-component', {
  // ...
})
import MyComponent from './MyComponent.vue'
export default {
    name: 'MyComponent',}Copy the code

10. Full word component names

Component names should tend to be full words, not abbreviations

/* bad */
components/
| - SdSettings.vue
| - UProfOpts.vue

/* good */
components/
| - StudentDashboardSettings.vue
| - UserProfileOptions.vue
Copy the code

11. Prop

CamelCase should always be used when declaring prop, and kebab-case should always be used in templates and JSX

/* bad */
props: {
  'greeting-text': String
}
<WelcomeMessage greetingText="hi"/>

/* good */
props: {
  greetingText: String
}
<WelcomeMessage greeting-text="hi"/>
Copy the code

12. Elements with multiple attributes

Elements with multiple attributes should be written in multiple lines, one for each attribute

/* bad */
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>

/* good */
<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
<MyComponent
  foo="a"
  bar="b"
  baz="c"
/>
Copy the code

13. Simple expressions in templates

Component templates should contain only simple expressions, and complex expressions should be refactored to evaluate properties or methods

/* bad */
{{
  fullName.split(' ').map((word) = > {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')}}/* good */
// In the template
{{ normalizedFullName }}
// The complex expression has been converted to a calculated property
computed: {
  normalizedFullName() {
    return this.fullName.split(' ')
      .map(word= > word[0].toUpperCase() + word.slice(1))
      .join(' ')}}Copy the code

14. Simple computed properties

Complex computational properties should be divided into as many simpler computational properties as possible

/* bad */
computed: {
  price() {
    const basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0))}}/* good */
computed: {
  basePrice() {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount() {
    return this.basePrice * (this.discountPercent || 0)},finalPrice() {
    return this.basePrice - this.discount
  }
}
Copy the code

15. Attribute values with quotation marks

Values of non-empty HTML attributes should always have quotes (single or double quotes, select the one not used in JS)

/* bad */
<input type=text>
<AppSidebar :style={width:sidebarWidth+'px'} >

/* good */
<input type="text">
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
Copy the code

16. Abbreviations of instructions

V-on => @, v-bind => :, v-slot => #, either always used or never used

/* bad */
<input
  v-bind:value="newTodoText"
  :placeholder="newTodoInstructions"
>
<input
  v-on:input="onInput"
  @focus="onFocus"
>
<template v-slot:header>
    <h1>Here might be a page title</h1>
</template>
<template #footer>
    <h1>Here's some contact info</h1>
</template>

/* good */
<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>
<input
  @input="onInput"
  @focus="onFocus"
>
<template v-slot:header>
    <h1>Here might be a page title</h1>
</template>
<template v-slot:footer>
    <h1>Here's some contact info</h1>
</template>
Copy the code

Recommendation (to minimize selection and cognitive costs)

1. Order of component/instance options

Component/instance options should have a uniform order

  1. Global awareness (requires awareness outside the component)

    • name
  2. Template compilation options (change how templates are compiled)

    • compilerOptions
  3. Template dependencies (resources used within templates)

    • components
    • directives
  4. Combine (merge property into options)

    • extends
    • mixins
    • provide/inject
  5. Interface (component interface)

    • inheritAttrs
    • props
    • emits
  6. Composite API (entry point to use composite API)

    • setup
  7. Local state (local responsive property)

    • data
    • computed
  8. Events (callbacks triggered by reactive events)

    • watch

    • Lifecycle events (in the order in which they are invoked)

      • beforeCreate
      • created
      • beforeMount
      • mounted
      • beforeUpdate
      • updated
      • activated
      • deactivated
      • beforeUnmount
      • unmounted
      • errorCaptured
      • renderTracked
      • renderTriggered
  9. Non-responsive Property (instance property that does not depend on a responsive system)

    • methods
  10. Render (declarative description of component output)

Elements of 2.attributeThe order of

There should be a consistent order of attributes for elements, including components

  1. Define (provide options for components)

    • is
  2. List rendering (creating multiple variants of the same element)

    • v-for
  3. Condition (whether the element is rendered/displayed)

    • v-if
    • v-else-if
    • v-else
    • v-show
    • v-cloak
  4. Render modifiers (change how elements are rendered)

    • v-pre
    • v-once
  5. Global awareness (requires awareness outside the component)

    • id
  6. Unique Attribute (Attribute that requires a unique value)

    • ref
    • key
  7. Bidirectional binding (combines binding and events)

    • v-model
  8. Other attributes (all normal, bound, or unbound attributes)

  9. Events (Component event listeners)

    • v-on
  10. Content (overrides the content of the element)

    • v-html
    • v-text

3. Blank lines in component/instance options

Add a blank line between properties, especially if there are many of these options

/* good */
props: {
  value: {
    type: String.required: true
  },

  focused: {
    type: Boolean.default: false
  },

  label: String.icon: String
},

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

  inputClasses() {
    // ...}}// While components can still be easily read and located,
// No blank lines are fine
props: {
  value: {
    type: String.required: true
  },
  focused: {
    type: Boolean.default: false
  },
  label: String.icon: String
},
computed: {
  formattedValue() {
    // ...
  },
  inputClasses() {
    // ...}}Copy the code

4. Order of top-level elements for single-file components

Single-file components should always have the same order of

/* bad */
<style>/ *... * /</style>
<script>/ *... * /</script>
<template>.</template>

/* good */
<template>.</template>
<script>/ *... * /</script>
<style>/ *... * /</style>// Template and script can also be reversedCopy the code

Use caution (potential risks)

1. scopeElement selector in

Element selectors should be avoided in scopes

/* bad */
<template>
  <button>x</button>
</template>

<style scoped>
button {
  background-color: red;
}
</style>

/* good */
<template>
  <button class="btn btn-close">x</button>
</template>

<style scoped>
.btn-close {
  background-color: red;
}
</style>
Copy the code

2. Implicit parent-child component communication

Parent component communication should be via prop and events in preference to this.$parent or changes to prop

/* bad */
app.component('TodoItem', {
  props: {
    todo: {
      type: Object.required: true}},template: '<input v-model="todo.text">'
})

app.component('TodoItem', {
  props: {
    todo: {
      type: Object.required: true}},methods: {
    removeTodo() {
      this.$parent.todos = this.$parent.todos.filter(todo= >todo.id ! == vm.todo.id) } },template:  {{todo.text}} < button@click ="removeTodo"> × 
         '
})

/* good */
app.component('TodoItem', {
  props: {
    todo: {
      type: Object.required: true}},emits: ['input'].template: `  `
})

app.component('TodoItem', {
  props: {
    todo: {
      type: Object.required: true}},emits: ['delete'].template: ` < span > {{todo. Text}} < button @ click = "$emit (' delete ')" > x < / button > < / span > `
})
Copy the code

3. TheFluxGlobal state management

Global state should be managed through Vuex in preference to this.$root or a global event bus

/* bad */
// main.js
import { createApp } from 'vue'
import mitt from 'mitt'
const app = createApp({
  data() {
    return {
      todos: [].emitter: mitt()
    }
  },

  created() {
    this.emitter.on('remove-todo'.this.removeTodo)
  },

  methods: {
    removeTodo(todo) {
      const todoIdToRemove = todo.id
      this.todos = this.todos.filter(todo= >todo.id ! == todoIdToRemove) } } })/* good */
// store/modules/todos.js
export default {
  state: {
    list: []},mutations: {
    REMOVE_TODO (state, todoId) {
      state.list = state.list.filter(todo= >todo.id ! == todoId) } },actions: {
    removeTodo ({ commit, state }, todo) {
      commit('REMOVE_TODO', todo.id) } } } <! -- TodoItem.vue --><template>
  <span>
    {{ todo.text }}
    <button @click="removeTodo(todo)">
      X
    </button>
  </span>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  props: {
    todo: {
      type: Object.required: true}},methods: mapActions(['removeTodo'])}</script>
Copy the code