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
- Priority A: Necessary
- Priority B: Highly recommended
- Priority C: Recommended
- 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-for
Set 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-if
和 v-for
Used 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 privateproperty
The 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
-
Global awareness (requires awareness outside the component)
name
-
Template compilation options (change how templates are compiled)
compilerOptions
-
Template dependencies (resources used within templates)
components
directives
-
Combine (merge property into options)
extends
mixins
provide
/inject
-
Interface (component interface)
inheritAttrs
props
emits
-
Composite API (entry point to use composite API)
setup
-
Local state (local responsive property)
data
computed
-
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
-
-
Non-responsive Property (instance property that does not depend on a responsive system)
methods
-
Render (declarative description of component output)
Elements of 2.attribute
The order of
There should be a consistent order of attributes for elements, including components
-
Define (provide options for components)
is
-
List rendering (creating multiple variants of the same element)
v-for
-
Condition (whether the element is rendered/displayed)
v-if
v-else-if
v-else
v-show
v-cloak
-
Render modifiers (change how elements are rendered)
v-pre
v-once
-
Global awareness (requires awareness outside the component)
id
-
Unique Attribute (Attribute that requires a unique value)
ref
key
-
Bidirectional binding (combines binding and events)
v-model
-
Other attributes (all normal, bound, or unbound attributes)
-
Events (Component event listeners)
v-on
-
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. scope
Element 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. TheFlux
Global 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