vue大部分规范需参照HTML、CSS、JavaScript规范,在此不再强调。
-
组件名应该始终是多个单词的
根组件
App
以及
<transition>
、
<component>
之类的 Vue 内置组件除外。
这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。
– Example –
-js // 不推荐 Vue.component('todo', { // ... }) export default { name: 'Todo', // ... } // 推荐 Vue.component('todo-item', { // ... }) export default { name: 'TodoItem', // ... }
-
组件的
data
必须是一个函数
当在组件中使用 data property 的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。
– Example –
-js // 不推荐 Vue.component('some-comp', { data: { foo: 'bar' } }) export default { data: { foo: 'bar' } } // 推荐 Vue.component('some-comp', { data: function () { return { foo: 'bar' } } }) // In a .vue file export default { data () { return { foo: 'bar' } } } // 在一个 Vue 的根实例上直接使用对象是可以的, // 因为只存在一个这样的实例。 new Vue({ data: { foo: 'bar' } })
-
Prop
定义应该尽量详细
至少需要指定其类型(type)。
– Example –
-js // 不推荐 // 这样做只有开发原型系统时可以接受 props: ['status'] // 推荐 props: { status: String } // 更好的做法! props: { status: { type: String, required: true, validator: function (value) { return [ 'syncing', 'synced', 'version-conflict', 'error' ].indexOf(value) !== -1 } } }
-
为
v-for
设置键值
总是用
key
配合
v-for
。
– Example –
-html <!-- 不推荐 --> <ul> <li v-for="todo in todos"> {{ todo.text }} </li> </ul> <!-- 推荐 --> <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} </li> </ul>
-
避免
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)。
– Example –
-html <!-- 不推荐 --> <ul> <li v-for="user in users" v-if="user.isActive" :key="user.id"> {{ user.name }} </li> </ul> <ul> <li v-for="user in users" v-if="shouldShowUsers" :key="user.id"> {{ user.name }} </li> </ul> <!-- 推荐 --> <ul> <li v-for="user in activeUsers" :key="user.id"> {{ user.name }} </li> </ul> <ul v-if="shouldShowUsers"> <li v-for="user in users" :key="user.id"> {{ user.name }} </li> </ul>
-
为组件样式设置作用域
对于应用来说,顶级
App
组件和布局组件中的样式可以是全局的,但是其它所有组件都应该是有作用域的。
设置样式作用域方法:- scoped attribute;
-
CSS Modules
(基于 class 的类似 BEM 的策略)
– Example –
-html <!-- 不推荐 --> <template> <button class="btn btn-close">X</button> </template> <style> .btn-close { background-color: red; } </style> <!-- 推荐 --> <!-- 使用 `scoped` attribute --> <template> <button class="button button-close">X</button> </template> <style scoped> .button { border: none; border-radius: 2px; } .button-close { background-color: red; } </style> <!-- 使用 CSS Modules --> <template> <button :class="[$style.button, $style.buttonClose]">X</button> </template> <style module> .button { border: none; border-radius: 2px; } .buttonClose { background-color: red; } </style> <!-- 使用 BEM 约定 --> <template> <button class="c-Button c-Button--close">X</button> </template> <style> .c-Button { border: none; border-radius: 2px; } .c-Button--close { background-color: red; } </style>
-
私有
property
名
使用模块作用域保持不允许外部访问的函数的私有性。如果无法做到这一点,就始终为插件、混入等不考虑作为对外公共 API 的自定义私有
property
使用
$_
前缀。并附带一个命名空间以回避和其它作者的冲突 (比如
$_yourPluginName_
)。
– Example –
-js // 不推荐 var myGreatMixin = { // ... methods: { update: function () { // ... } } } var myGreatMixin = { // ... methods: { _update: function () { // ... } } } var myGreatMixin = { // ... methods: { $update: function () { // ... } } } var myGreatMixin = { // ... methods: { $_update: function () { // ... } } } // 推荐 var myGreatMixin = { // ... methods: { $_myGreatMixin_update: function () { // ... } } } // 甚至更好! var myGreatMixin = { // ... methods: { publicMethod() { // ... myPrivateFunction() } } } function myPrivateFunction() { // ... } export default myGreatMixin
-
组件文件
只要有能够拼接文件的构建系统,就把每个组件单独分成文件。
– Example –
-js // 不推荐 Vue.component('TodoList', { // ... }) Vue.component('TodoItem', { // ... })
- // 推荐 components/ |- TodoList.js |- TodoItem.js components/ |- TodoList.vue |- TodoItem.vue
-
单文件组件文件的大小写
单文件组件的文件名应该:
- 始终是单词大写开头 (PascalCase);
-
始终是横线连接 (kebab-case)
。
– Example –
- // 不推荐 components/ |- mycomponent.vue components/ |- myComponent.vue // 推荐 components/ |- MyComponent.vue components/ |- my-component.vue
-
基础组件名
应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如
Base
、
App
或
V
。
这些组件为你的应用奠定了一致的基础样式和行为。它们可能只包括:- HTML 元素;
- 其它基础组件;
- 第三方 UI 组件库.
好处:
- 基础组件会全部列在一起,这样更容易识别;
- 可以避免你在包裹简单组件时随意选择前缀 (比如 MyButton、VueButton);
- 因为这些组件会被频繁使用,所以你可能想把它们放到全局而不是在各处分别导入它们。
– Example –
- // 不推荐 components/ |- MyButton.vue |- VueTable.vue |- Icon.vue // 推荐 components/ |- BaseButton.vue |- BaseTable.vue |- BaseIcon.vue components/ |- AppButton.vue |- AppTable.vue |- AppIcon.vue components/ |- VButton.vue |- VTable.vue |- VIcon.vue
-
单例组件名
只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。
这不意味着组件只可用于一个单页面,而是每个页面只使用一次。
– Example –
- // 不推荐 components/ |- Heading.vue |- MySidebar.vue // 推荐 components/ |- TheHeading.vue |- TheSidebar.vue
-
紧密耦合的组件名
和父组件紧密耦合的子组件应该以父组件名作为前缀命名。
不推荐 在其父组件命名的目录中嵌套子组件。
– Example –
- // 不推荐 components/ |- TodoList.vue |- TodoItem.vue |- TodoButton.vue components/ |- SearchSidebar.vue |- NavigationForSearchSidebar.vue // 推荐 components/ |- TodoList.vue |- TodoListItem.vue |- TodoListItemButton.vue components/ |- SearchSidebar.vue |- SearchSidebarNavigation.vue
-
组件名中的单词顺序
组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。
如果是一组组件,建议以 高级别的 (通常是一般化描述的) 单词 为名创建一个文件夹,统一放置相关组件文件。
– Example –
- // 不推荐 components/ |- ClearSearchButton.vue |- ExcludeFromSearchInput.vue |- LaunchOnStartupCheckbox.vue |- RunSearchButton.vue |- SearchInput.vue |- TermsCheckbox.vue // 推荐 components/ |- SearchButtonClear.vue |- SearchButtonRun.vue |- SearchInputQuery.vue |- SearchInputExcludeGlob.vue |- SettingsCheckboxTerms.vue |- SettingsCheckboxLaunchOnStartup.vue components/ |- search/ |- ButtonClear.vue |- ButtonRun.vue |- InputQuery.vue |- InputExcludeGlob.vue |-setting/ |- CheckboxTerms.vue |- CheckboxLaunchOnStartup.vue
-
自闭合组件
在单文件组件、字符串模板和 JSX 中没有内容的组件应该是自闭合的——但在 DOM 模板里永远不要这样做。
– Example –
-html <!-- 不推荐 --> <!-- 在单文件组件、字符串模板和 JSX 中 --> <MyComponent></MyComponent> <!-- 在 DOM 模板中 --> <my-component/> <!-- 推荐 --> <!-- 在单文件组件、字符串模板和 JSX 中 --> <MyComponent/> <!-- 在 DOM 模板中 --> <my-component></my-component>
-
自模板中的组件名大小写
对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。
在所有地方使用 kebab-case
– Example –
-html <!-- 不推荐 --> <!-- 在单文件组件和字符串模板中 --> <mycomponent/> <!-- 在单文件组件和字符串模板中 --> <myComponent/> <!-- 在 DOM 模板中 --> <MyComponent></MyComponent> <!-- 推荐 --> <!-- 在单文件组件和字符串模板中 --> <MyComponent/> <!-- 在 DOM 模板中 --> <my-component></my-component> <!-- 在所有地方 --> <my-component/>
-
JS/JSX 中的组件名大小写
JS/JSX
中的组件名应该始终是 PascalCase 的,尽管在较为简单的应用中只使用
Vue.component
进行全局组件注册时,可以使用 kebab-case 字符串。
– Example –
-js // 不推荐 Vue.component('myComponent', { // ... }) import myComponent from './MyComponent.vue' export default { name: 'myComponent', // ... } export default { name: 'my-component', // ... } // 推荐 Vue.component('MyComponent', { // ... }) Vue.component('my-component', { // ... }) import MyComponent from './MyComponent.vue' export default { name: 'MyComponent', // ... }
-
完整单词的组件名
组件名应该倾向于完整单词而不是缩写。
– Example –
- // 不推荐 components/ |- SdSettings.vue |- UProfOpts.vue // 推荐 components/ |- StudentDashboardSettings.vue |- UserProfileOptions.vue
-
Prop
名大小写
在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板和 JSX 中应该始终使用 kebab-case。
– Example –
不推荐:-js props: { 'greeting-text': String }
-html <welcome-message greetingText="hi"/>
推荐:
-js props: { greetingText: String }
-html <welcome-message greeting-text="hi"/>
-
多个
attribute
的元素
多个
attribute
的元素应该一行撰写,超出 80 字符按照
attribute
元素进行换行。
– Example –
-html <!-- 不推荐 --> <img src="https://vuejs.org/images/logo.png" alt="Vue Logo" > <my-component foo="a" bar="b" baz="c" /> <!-- 推荐 --> <img src="https://vuejs.org/images/logo.png" alt="Vue Logo"> <my-component foo="a" bar="b" baz="c"/>
-
模板中简单的表达式
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。
{
{
后要空一格,
}}
前要空一格
– Example –
-html <!-- 不推荐 --> <div> {{ fullName.split(' ').map(function (word) { return word[0].toUpperCase() + word.slice(1) }).join(' ') }} </div> <!-- 推荐 --> <div>{{ normalizedFullName }}</div>
-js // 复杂表达式已经移入一个计算属性 computed: { normalizedFullName: function () { return this.fullName.split(' ').map(function (word) { return word[0].toUpperCase() + word.slice(1) }).join(' ') } }
-
简单的计算属性
应该把复杂计算属性分割为尽可能多的更简单的 property。
– Example –
-js // 不推荐 computed: { price: function () { var basePrice = this.manufactureCost / (1 - this.profitMargin) return ( basePrice - basePrice * (this.discountPercent || 0) ) } } // 推荐 computed: { basePrice: function () { return this.manufactureCost / (1 - this.profitMargin) }, discount: function () { return this.basePrice * (this.discountPercent || 0) }, finalPrice: function () { return this.basePrice - this.discount } }
-
带引号的
attribute
值
非空 HTML attribute 值应该始终带双引号 。
– Example –
-html <!-- 不推荐 --> <input type=text> <app-sidebar :style={width:sidebarWidth+'px'}> <!-- 推荐 --> <input type="text"> <app-sidebar :style="{ width: sidebarWidth + 'px'}">
-
指令缩写
指令缩写 (用 : 表示 v-bind:、用 @ 表示 v-on: 和用 # 表示 v-slot:)
– Example –
-html <!-- 不推荐 --> <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> <!-- 推荐 --> <input :value="newTodoText" :placeholder="newTodoInstructions"> <input @input="onInput" @focus="onFocus"> <template #header> <h1>Here might be a page title</h1> </template>
-
组件/实例的选项的统一顺序
- 副作用 (触发组件外的影响);(el)
- 全局感知 (要求组件以外的知识);(name/parent)
- 组件类型 (更改组件的类型);(functional)
- 模板修改器 (改变模板的编译方式);(delimiters/comments)
- 模板依赖 (模板内使用的资源);(components/directives/filters)
- 组合 (向选项里合并 property);(extends/mixins)
- 接口 (组件的接口);(inheritAttrs/model/props/propsData)
- 本地状态 (本地的响应式 property);(data/computed)
-
事件 (通过响应式事件触发的回调);(watch/生命周期钩子)
- 生命周期钩子顺序:beforeCreate / created / beforeMount / mounted / beforeUpdate / updated / activate d /deactivated / beforeDestroy / destroyed
- 非响应式的 property (不依赖响应系统的实例 property);(methods)
- 渲染 (组件输出的声明式描述)。(template/render/renderError)
-
最常用组件/实例顺序
- 全局感知 (要求组件以外的知识);(name/parent)
- 模板修改器 (改变模板的编译方式);(delimiters/comments)
- 模板依赖 (模板内使用的资源);(components/directives/filters)
- 接口 (组件的接口);(inheritAttrs/model/props/propsData)
- 本地状态 (本地的响应式 property);(data/computed)
-
事件 (通过响应式事件触发的回调);(watch/生命周期钩子)
- 生命周期钩子顺序:beforeCreate / created / beforeMount / mounted / beforeUpdate / updated / activate d /deactivated / beforeDestroy / destroyed
- 非响应式的 property (不依赖响应系统的实例 property)。(methods)
-
元素
attribute
的顺序
- 定义 (提供组件的选项);(is)
- 列表渲染 (创建多个变化的相同元素);(v-for)
- 条件渲染 (元素是否渲染/显示);(v-if/v-else-if/v-else/v-show/v-cloak)
- 渲染方式 (改变元素的渲染方式);(v-pre/v-once)
- 全局感知 (需要超越组件的知识);(id)
-
唯一的
attribute
(需要唯一值的
attribute
);(ref/key) - 双向绑定 (把绑定和事件结合起来);(v-model)
- 事件 (组件事件监听器);(v-on)
- 内容 (覆写元素的内容);(v-html/v-text)
-
其它
attribute
(所有普通的绑定或未绑定的
attribute
)。
-
组件/实例选项中的空行
一组
property
之间,空行隔开
– Example –
-js // 推荐 props: { value: { type: String, required: true }, focused: { type: Boolean, default: false }, label: String, icon: String }, data() { return { advertInfo:[], advertConfig:{}, other1:'', other2:'', } }, computed: { formattedValue: function () { // ... }, inputClasses: function () { // ... } }
-
单文件组件的顶级元素的顺序
– Example –
-html <!-- 推荐 --> <template>...</template> <script>/* ... */</script> <style>/* ... */</style>
-
避免在
scoped
中使用元素选择器
– Example –
-html <!-- 不推荐 --> <template> <button>X</button> </template> <style scoped> button { background-color: red; } </style> <!-- 推荐 --> <template> <button class="btn btn-close">X</button> </template> <style scoped> .btn-close { background-color: red; } </style>
-
隐性的父子组件通信
应该优先通过
prop
和事件进行父子组件之间的通信,而不是
this.$parent
或变更
prop
。 -
非 Flux 的全局状态管理
应该优先通过 Vuex 管理全局状态,而不是通过 this.$root 或一个全局事件总线。
-
Vue Router 规范
-
页面跳转数据传递使用路由参数。
– Example –
-js let id = '123'; this.$router.push({ name: 'userCenter', query: { id: id } });
-
router 中的命名规范。
- path、childrenPoints 命名规范采用kebab-case命名规范;
-
path必须以
/
开头,
children
里的path也要以
/
开头; - name 命名规范采用KebabCase命名规范且和component组件名保持一致。
-