
基于 Vue 官方风格指南整理
使用 eslint standard 规范
正例:
export default {
name: 'TodoItem'
// ...
}
反例:
export default {
name: 'Todo'
// ...
}
正例:
// In a .vue file
export default {
data() {
return {
foo: 'bar'
}
}
}
// 在一个 Vue 的根实例上直接使用对象是可以的,
// 因为只存在一个这样的实例。
new Vue({
data: {
foo: 'bar'
}
})
反例:
export default {
data: {
foo: 'bar'
}
}
正例:
props: {
status: String
}
// 更好的做法!
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}
反例:
// 这样做只有开发原型系统时可以接受
props: ['status']
正例:
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
反例:
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>
5.避免 v-if 和 v-for 用在一起永远不要把 v-if 和 v-for 同时用在同一个元素上。一般我们在两种常见的情况下会倾向于这样做:
为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ul, ol)。正例:
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
反例:
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
不管怎样,对于组件库,我们应该更倾向于选用基于 class 的策略而不是 scoped 特性。这让覆写内部样式更容易:使用了常人可理解的 class 名称且没有太高的选择器优先级,而且不太会导致冲突。正例:
<template>
<button class="c-Button c-Button--close">X</button>
</template>
<!-- 使用 BEM 约定 -->
<style>
.c-Button {
border: none;
border-radius: 2px;
}
.c-Button--close {
background-color: red;
}
</style>
反例:
<template>
<button class="btn btn-close">X</button>
</template>
<style>
.btn-close {
background-color: red;
}
</style>
<template>
<button class="button button-close">X</button>
</template>
<!-- 使用 `scoped` 特性 -->
<style scoped>
.button {
border: none;
border-radius: 2px;
}
.button-close {
background-color: red;
}
</style>
正例:
components/
|- TodoList.vue
|- TodoItem.vue
反例:
Vue.component('TodoList', {
// ...
})
Vue.component('TodoItem', {
// ...
})
正例:
components/
|- MyComponent.vue
反例:
components/
|- myComponent.vue
|- mycomponent.vue
正例:
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
反例:
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
正例:
components/
|- TheHeading.vue
|- TheSidebar.vue
反例:
components/
|- Heading.vue
|- MySidebar.vue
正例:
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
反例:
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
正例:
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
反例:
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
正例:
<!-- 在单文件组件和字符串模板中 -->
<MyComponent/>
反例:
<!-- 在单文件组件和字符串模板中 -->
<mycomponent/>
<!-- 在单文件组件和字符串模板中 -->
<myComponent/>
正例:
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
反例:
components/
|- SdSettings.vue
|- UProfOpts.vue
正例:
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
<MyComponent
foo="a"
bar="b"
baz="c"
/>
反例:
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>
正例:
<!-- 在模板中 -->
{{ normalizedFullName }}
// 复杂表达式已经移入一个计算属性
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
反例:
{
{
fullName
.split(' ')
.map(function(word) {
return word[0].toUpperCase() + word.slice(1)
})
.join(' ')
}
}
computed: {
basePrice: function () {
return this.manufactureCost / (1 - this.profitMargin)
},
discount: function () {
return this.basePrice \* (this.discountPercent || 0)
},
finalPrice: function () {
return this.basePrice - this.discount
}
}
反例:
computed: {
price: function () {
var basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice \* (this.discountPercent || 0)
)
}
}
正例:
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
反例:
<AppSidebar :style={width:sidebarWidth+'px'}>
正例:
<input
@input="onInput"
@focus="onFocus"
反例:
<input
v-bind:value="newTodoText"
:placeholder="newTodoInstructions"
<script>、<template> 和 <style> 标签的顺序保持一致。且<style> 要放在最后,因为另外两个标签至少要有一个。正例:
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
四、谨慎使用 (有潜在危险的模式)
<div> 元素)。正例:
<div
v-if="error"
key="search-status"
>
错误:{{ error }}
</div>
<div
v-else
key="search-results"
>
{{ results }}
</div>
反例:
<div v-if="error">
错误:{{ error }}
</div>
<div v-else>
{{ results }}
</div>
正例:
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
反例:
<template>
<button>X</button>
</template>
<style scoped>
button {
background-color: red;
}
</style>
正例:
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: `<input :value="todo.text" @input="$emit('input', $event.target.value)" >`
})
反例:
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
methods: {
removeTodo() {
var vm = this
vm.$parent.todos = vm.$parent.todos.filter(function(todo) {
return todo.id !== vm.todo.id
})
}
},
template: `<span> {{ todo.text }} <button @click="removeTodo"> X </button> </span>`
})
正例:
// 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>
反例:
// main.js
new Vue({
data: {
todos: []
},
created: function() {
this.$on('remove-todo', this.removeTodo)
},
methods: {
removeTodo: function(todo) {
var todoIdToRemove = todo.id
this.todos = this.todos.filter(function(todo) {
return todo.id !== todoIdToRemove
})
}
}
})
5 方便阅读代码 代码的钩子函数顺序 首先是名字和依赖在最上,方便添加删除 然后是数据优先按照数据处理顺序依次 然后是方法
name:'',
components:{},
mixins:[],
props:{},
data(){},
watch:{},
computed:{},
methods:{},
created(){},
activated(){},
mounted(){}
提示
Auto Close Tag 自动闭合标签
Path Intellisense 路劲感知
Prettier 代码格式化
Vetur vue 插件
blubloco-dark 一款主题
barcket pair colorizer 括号高亮
code outline 方便读源码
material icon theme 图标主题
sublime text keymap sublime 快捷键
todo highlight 待办高亮
{
"workbench.startupEditor": "newUntitledFile",
"workbench.iconTheme": "material-icon-theme",
"terminal.integrated.shell.windows":
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
// 以下解决格式化js自动添加分号
"prettier.singleQuote": true,
"prettier.semi": false,
// 以下解决stylus格式化问题
"stylusSupremacy.insertColons": true,
// 是否插入冒号
"stylusSupremacy.insertSemicolons": true,
// 是否插入分好
"stylusSupremacy.insertBraces": true,
// 是否插入大括号
"stylusSupremacy.insertNewLineAroundImports": false, // import之后是否换行
"stylusSupremacy.insertNewLineAroundBlocks": false,
// 两个选择器中是否换行
// 解决js单引号自动转换成双引号:
"prettier.semi": false,
"prettier.singleQuote": true,
// 窗口失去焦点自动保存
"files.autoSave": "onFocusChange",
// 编辑粘贴自动格式化
//"editor.formatOnPaste": true,
// 通过使用鼠标滚轮同时按住 Ctrl 可缩放编辑器的字体
"editor.mouseWheelZoom": false,
// 行太长自动换行
"editor.wordWrap": "on",
// eslint设置
"eslint.validate": [
"javascript",
"javascriptreact",
"html",
"vue",
{
"language": "vue",
"autoFix": true
}
],
// tab锁紧
"editor.tabSize": 2,
// 保存自动化
"editor.formatOnSave": true,
"editor.formatOnSave": true,
"vetur.format.defaultFormatter.html": "js-beautify-html",
"javascript.format.insertSpaceBeforeFunctionParenthesis": true,
"todohighlight.keywords": [
"DEBUG:",
"REVIEW:",
{
"text": "NOTE:",
"color": "#ff0000",
"backgroundColor": "yellow",
"overviewRulerColor": "grey"
},
{
"text": "BUG:",
"color": "#fff",
"backgroundColor": "red",
"isWholeLine": false
},
{
"text": "HACK:",
"color": "#000",
"isWholeLine": false
},
{
"text": "TIP:",
"color": "#fff",
"backgroundColor": "#1690ff",
"isWholeLine": false
}
],
"workbench.colorTheme": "Bluloco Dark",
"sublimeTextKeymap.promptV3Features": true
}