# vue編碼規范
## 目標
本規范提供了一種統一的編碼規范來編寫 [Vue.js](http://vuejs.org/) 代碼。這使得代碼具有如下的特性:
* 其它開發者或是團隊成員更容易閱讀和理解。
* IDE更容易理解代碼,從而提供高亮、格式化等輔助功能
* 更容易使用現有的工具
* 更容易實現緩存以及代碼包的分拆
## 準則
* [基于模塊開發](##基于模塊開發)
* [vue 組件命名](##vue-組件命名)
* [組件表達式簡單化](##組件表達式簡單化)
* [組件 props 原子化](##組件-props-原子化)
* [驗證組件的 props](##驗證組件的-props)
* [將 `this` 賦值給 `component` 變量](#將-this-賦值給-component-變量)
* [組件結構化](##組件結構化)
* [組件事件命名](##組件事件命名)
* [避免 `this.$parent`](##避免-this.$parent)
* [謹慎使用 `this.$refs`](##謹慎使用-this.$refs)
* [使用組件名作為樣式作用域空間](##使用組件名作為樣式作用域空間)
* [提供組件 API 文檔](##提供組件-api-文檔)
* [提供組件 demo](##提供組件-demo)
* [對組件文件進行代碼校驗](##對組件文件進行代碼校驗)
* [只在需要時創建組件](##只在需要時創建組件)
<!-- * [使用 `*.vue` 擴展名](##使用-vue-后綴作為擴展名) -->
<!-- * [為你的項目添加徽章標識](##為你的項目添加徽章標識) -->
## 基于模塊開發
始終基于模塊的方式來構建你的 app,每一個子模塊只做一件事情。
Vue.js 的設計初衷就是幫助開發者更好的開發界面模塊。一個模塊是應用程序中獨立的一個部分。
### How?
每一個 Vue 組件(等同于模塊)[首先]((https://addyosmani.com/first/))必須專注于解決一個[單一的問題](http://en.wikipedia.org/wiki/Single_responsibility_principle),*獨立的*、*可復用的*、*微小的* 和 *可測試的*。
如果你的組件做了太多的事或是變得臃腫,請將其拆分成更小的組件并保持單一的原則。一般來說,盡量保證每一個文件的代碼行數不要超過 100 行。也請保證組件可獨立的運行。比較好的做法是增加一個單獨的 demo 示例。
[↑ 回到目錄](#目錄)
## Vue 組件命名
組件的命名需遵從以下原則:
* **有意義的**: 不過于具體,也不過于抽象
* **簡短**: 2 到 3 個單詞
* **具有可讀性**: 以便于溝通交流
同時還需要注意:
* 必須符合**自定義元素規范**: [使用連字符](https://www.w3.org/TR/custom-elements/#concepts)分隔單詞,切勿使用保留字。
* **`app-` 前綴作為命名空間**: 如果非常通用的話可使用一個單詞來命名,這樣可以方便于其它項目里復用。
### Why?
* 組件是通過組件名來調用的。所以組件名必須簡短、富有含義并且具有可讀性。
### 如何做?
```html
<!-- 推薦 -->
<app-header></app-header>
<user-list></user-list>
<range-slider></range-slider>
<!-- 避免 -->
<btn-group></btn-group> <!-- 雖然簡短但是可讀性差. 使用 `button-group` 替代 -->
<ui-slider></ui-slider> <!-- ui 前綴太過于寬泛,在這里意義不明確 -->
<slider></slider> <!-- 與自定義元素規范不兼容 -->
```
[↑ 回到目錄](#目錄)
## 組件表達式簡單化
Vue.js 的表達式是 100% 的 Javascript 表達式。這使得其功能性很強大,但也帶來潛在的復雜性。因此,你應該盡量**保持表達式的簡單化**。
### Why?
* 復雜的行內表達式難以閱讀。
* 行內表達式是不能夠通用的,這可能會導致重復編碼的問題。
* IDE 基本上不能識別行內表達式語法,所以使用行內表達式 IDE 不能提供自動補全和語法校驗功能。
### How?
如果你發現寫了太多復雜并難以閱讀的行內表達式,那么可以使用 method 或是 computed 屬性來替代其功能。
```html
<!-- 推薦 -->
<template>
<h1>
{{ `${year}-${month}` }}
</h1>
</template>
<script type="text/javascript">
export default {
computed: {
month() {
return this.twoDigits((new Date()).getUTCMonth() + 1);
},
year() {
return (new Date()).getUTCFullYear();
}
},
methods: {
twoDigits(num) {
return ('0' + num).slice(-2);
}
},
};
</script>
<!-- 避免 -->
<template>
<h1>
{{ `${(new Date()).getUTCFullYear()}-${('0' + ((new Date()).getUTCMonth()+1)).slice(-2)}` }}
</h1>
</template>
```
[↑ 回到目錄](#目錄)
### 組件 props 原子化
雖然 Vue.js 支持傳遞復雜的 JavaScript 對象通過 props 屬性,但是你應該盡可能的使用原始類型的數據。盡量只使用 [JavaScript 原始類型](https://developer.mozilla.org/en-US/docs/Glossary/Primitive)(字符串、數字、布爾值)和函數。盡量避免復雜的對象。
### Why?
* 使得組件 API 清晰直觀。
* 只使用原始類型和函數作為 props 使得組件的 API 更接近于 HTML(5) 原生元素。
* 其它開發者更好的理解每一個 prop 的含義、作用。
* 傳遞過于復雜的對象使得我們不能夠清楚的知道哪些屬性或方法被自定義組件使用,這使得代碼難以重構和維護。
### How?
組件的每一個屬性單獨使用一個 props,并且使用函數或是原始類型的值。
```html
<!-- 推薦 -->
<range-slider
:values="[10, 20]"
min="0"
max="100"
step="5"
:on-slide="updateInputs"
:on-end="updateResults">
</range-slider>
<!-- 避免 -->
<range-slider :config="complexConfigObject"></range-slider>
```
[↑ 回到目錄](#目錄)
## 驗證組件的 props
在 Vue.js 中,組件的 props 即 API,一個穩定并可預測的 API 會使得你的組件更容易被其他開發者使用。
組件 props 通過自定義標簽的屬性來傳遞。屬性的值可以是 Vue.js 字符串(`:attr="value"` 或 `v-bind:attr="value"`)或是不傳。你需要保證組件的 props 能應對不同的情況。
### Why?
驗證組件 props 可以保證你的組件永遠是可用的(防御性編程)。即使其他開發者并未按照你預想的方法使用時也不會出錯。
### How?
* 提供默認值。
* 使用 `type` 屬性[校驗類型](http://vuejs.org/v2/guide/components.html#Prop-Validation)。
* 使用 props 之前先檢查該 prop 是否存在。
```html
<template>
<input type="range" v-model="value" :max="max" :min="min">
</template>
<script type="text/javascript">
export default {
props: {
max: {
type: Number, // 這里添加了數字類型的校驗
default() { return 10; },
},
min: {
type: Number,
default() { return 0; },
},
value: {
type: Number,
default() { return 4; },
},
},
};
</script>
```
[↑ 回到目錄](#目錄)
## 將 `this` 賦值給 `component` 變量
在 Vue.js 組件上下文中,`this`指向了組件實例。因此當你切換到了不同的上下文時,要確保 `this` 指向一個可用的 `component` 變量。
換句話說,如果你正在使用 **ES6** 的話,就不要再編寫 `var self = this;` 這樣的代碼了,您可以安全地使用 Vue 組件。
### Why?
* 使用 **ES6**,就不再需要將 `this` 保存到一個變量中了。
* 一般來說,當你使用箭頭函數時,會保留 `this` 的作用域。(譯者注:箭頭函數沒有它自己的 this 值,箭頭函數內的 this 值繼承自外圍作用域。)
* 如果你沒有使用 **ES6**,當然也就不會使用 `箭頭函數` 啦,那你必須將 “this” 保存到到某個變量中。這是唯一的例外。
### How?
```html
<script type="text/javascript">
export default {
methods: {
hello() {
return 'hello';
},
printHello() {
console.log(this.hello());
},
},
};
</script>
<!-- 避免 -->
<script type="text/javascript">
export default {
methods: {
hello() {
return 'hello';
},
printHello() {
const self = this; // 沒有必要
console.log(self.hello());
},
},
};
</script>
```
[↑ 回到目錄](#目錄)
## 組件結構化
按照一定的結構組織,使得組件便于理解。
### Why?
* 導出一個清晰、組織有序的組件,使得代碼易于閱讀和理解。同時也便于標準化。
* 按首字母排序 properties、data、computed、watches 和 methods 使得這些對象內的屬性便于查找。
* 合理組織,使得組件易于閱讀。(name; extends; props, data 和 computed; components; watch 和 methods; lifecycle methods 等)。
* 使用 `name` 屬性。借助于 [vue devtools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en) 可以讓你更方便的測試。
* 合理的 CSS 結構,如 [BEM](https://medium.com/tldr-tech/bem-blocks-elements-and-modifiers-6b3b0af9e3ea#.bhnomd7gw) 或 [rscss](https://github.com/rstacruz/rscss) - [詳情?](#使用組件名作為樣式作用域空間)。
* 使用單文件 .vue 文件格式來組件代碼。
### How?
組件結構化
```html
<template lang="html">
<div class="Ranger__Wrapper">
<!-- ... -->
</div>
</template>
<script type="text/javascript">
export default {
// 不要忘記了 name 屬性
name: 'RangeSlider',
// 組合其它組件
extends: {},
// 組件屬性、變量
props: {
bar: {}, // 按字母順序
foo: {},
fooBar: {},
},
// 變量
data() {},
computed: {},
// 使用其它組件
components: {},
// 方法
watch: {},
methods: {},
// 生命周期函數
beforeCreate() {},
mounted() {},
};
</script>
<style scoped>
.Ranger__Wrapper { /* ... */ }
</style>
```
[↑ 回到目錄](#目錄)
## 組件事件命名
Vue.js 提供的處理函數和表達式都是綁定在 ViewModel 上的,組件的每一個事件都應該按照一個好的命名規范來,這樣可以避免不少的開發問題,具體可見如下 **為什么**。
### Why?
* 開發者可以隨意給事件命名,即使是原生事件的名字,這樣會帶來迷惑性。
* 過于寬松的事件命名可能與 [DOM 模板不兼容](https://vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats)。
### How?
* 事件名也使用連字符命名。
* 一個事件的名字對應組件外的一組意義操作,如:upload-success、upload-error 以及 dropzone-upload-success、dropzone-upload-error (如果需要前綴的話)。
* 事件命名應該以動詞(如 client-api-load) 或是 形容詞(如 drive-upload-success)結尾。([出處](https://github.com/GoogleWebComponents/style-guide#events))
[↑ 回到目錄](#目錄)
## 避免 this.$parent
Vue.js 支持組件嵌套,并且子組件可訪問父組件的上下文。訪問組件之外的上下文違反了[基于模塊開發](#基于模塊開發)的[第一原則](https://addyosmani.com/first/)。因此你應該盡量避免使用 **`this.$parent`**。
### Why?
* 組件必須相互保持獨立,Vue 組件也是。如果組件需要訪問其父層的上下文就違反了該原則。
* 如果一個組件需要訪問其父組件的上下文,那么該組件將不能在其它上下文中復用。
### How?
* 通過 props 將值傳遞給子組件。
* 通過 props 傳遞回調函數給子組件來達到調用父組件方法的目的。
* 通過在子組件觸發事件來通知父組件。
[↑ 回到目錄](#目錄)
## 謹慎使用 this.$refs
Vue.js 支持通過 `ref` 屬性來訪問其它組件和 HTML 元素。并通過 `this.$refs` 可以得到組件或 HTML 元素的上下文。在大多數情況下,通過 `this.$refs`來訪問其它組件的上下文是可以避免的。在使用的的時候你需要注意避免調用了不恰當的組件 API,所以應該盡量避免使用 `this.$refs`。
### Why?
* 組件必須是保持獨立的,如果一個組件的 API 不能夠提供所需的功能,那么這個組件在設計、實現上是有問題的。
* 組件的屬性和事件必須足夠的給大多數的組件使用。
### How?
* 提供良好的組件 API。
* 總是關注于組件本身的目的。
* 拒絕定制代碼。如果你在一個通用的組件內部編寫特定需求的代碼,那么代表這個組件的 API 不夠通用,或者你可能需要一個新的組件來應對該需求。
* 檢查所有的 props 是否有缺失的,如果有提一個 issue 或是完善這個組件。
* 檢查所有的事件。子組件向父組件通信一般是通過事件來實現的,但是大多數的開發者更多的關注于 props 從忽視了這點。
* **Props向下傳遞,事件向上傳遞!**。以此為目標升級你的組件,提供良好的 API 和 獨立性。
* 當遇到 props 和 events 難以實現的功能時,通過 `this.$refs`來實現。
* 當需要操作 DOM 無法通過指令來做的時候可使用 `this.$ref` 而不是 `JQuery`、`document.getElement*`、`document.queryElement`。
```html
<!-- 推薦,并未使用 this.$refs -->
<range :max="max"
:min="min"
@current-value="currentValue"
:step="1"></range>
```
```html
<!-- 使用 this.$refs 的適用情況-->
<modal ref="basicModal">
<h4>Basic Modal</h4>
<button class="primary" @click="$refs.basicModal.hide()">Close</button>
</modal>
<button @click="$refs.basicModal.open()">Open modal</button>
<!-- Modal component -->
<template>
<div v-show="active">
<!-- ... -->
</div>
</template>
<script>
export default {
// ...
data() {
return {
active: false,
};
},
methods: {
open() {
this.active = true;
},
hide() {
this.active = false;
},
},
// ...
};
</script>
```
```html
<!-- 如果可通過 emited 來做則避免通過 this.$refs 直接訪問 -->
<template>
<range :max="max"
:min="min"
ref="range"
:step="1"></range>
</template>
<script>
export default {
// ...
methods: {
getRangeCurrentValue() {
return this.$refs.range.currentValue;
},
},
// ...
};
</script>
```
[↑ 回到目錄](#目錄)
## 使用組件名作為樣式作用域空間
Vue.js 的組件是自定義元素,這非常適合用來作為樣式的根作用域空間。可以將組件名作為 CSS 類的命名空間。
### Why?
* 給樣式加上作用域空間可以避免組件樣式影響外部的樣式。
* 保持模塊名、目錄名、樣式根作用域名一樣,可以很好的將其關聯起來,便于開發者理解。
### How?
使用組件名作為樣式命名的前綴,可基于 BEM 或 OOCSS 范式。同時給 style 標簽加上 scoped 屬性。加上 scoped 屬性編譯后會給組件的 class 自動加上唯一的前綴從而避免樣式的沖突。
```html
<style scoped>
/* 推薦 */
.MyExample { }
.MyExample li { }
.MyExample__item { }
/* 避免 */
.My-Example { } /* 沒有用組件名或模塊名限制作用域, 不符合 BEM 規范 */
</style>
```
[↑ 回到目錄](#目錄)
## 提供組件 API 文檔
使用 Vue.js 組件的過程中會創建 Vue 組件實例,這個實例是通過自定義屬性配置的。為了便于其他開發者使用該組件,對于這些自定義屬性即組件API應該在 `README.md` 文件中進行說明。
## Why?
* 良好的文檔可以讓開發者比較容易的對組件有一個整體的認識,而不用去閱讀組件的源碼,也更方便開發者使用。
* 組件配置屬性即組件的 API,對于組件的用戶來說他們更感興趣的是 API 而不是實現原理。
* 正式的文檔會告訴開發者組件 API 變更以及向后的兼容性情況。
* `README.md` 是標準的我們應該首先閱讀的文檔文件。代碼托管網站(GitHub、Bitbucket、Gitlab 等)會默認在倉庫中展示該文件作為倉庫的介紹。
### How?
在模塊目錄中添加 `README.md` 文件:
```
range-slider/
├── range-slider.vue
├── range-slider.less
└── README.md
```
在 README 文件中說明模塊的功能以及使用場景。對于 vue 組件來說,比較有用的描述是組件的自定義屬性即 API 的描述介紹。
# Range slider
## 功能
range slider 組件可通過拖動的方式來設置一個給定范圍內的數值。
該模塊使用 [noUiSlider](http://refreshless.com/nouislider/) 來實現跨瀏覽器和 touch 功能的支持。
## 如何使用
`<range-slider>` 支持如下的自定義屬性:
| attribute | type | description
| --- | --- | ---
| `min` | Number | 可拖動的最小值.
| `max` | Number | 可拖動的最大值.
| `values` | Number[] *optional* | 包含最大值和最小值的數組. 如. `values="[10, 20]"`. Defaults to `[opts.min, opts.max]`.
| `step` | Number *optional* | 增加減小的數值單位,默認為 1.
| `on-slide` | Function *optional* | 用戶拖動開始按鈕或者結束按鈕時的回調函數,函數接受 `(values, HANDLE)` 格式的參數。 如: `on-slide={ updateInputs }`, `component.updateInputs = (values, HANDLE) => { const value = values[HANDLE]; }`.
| `on-end` | Function *optional* | 當用戶停止拖動時觸發的回調函數,函數接受 `(values, HANDLE)` 格式的參數。
如需要自定義 slider 的樣式可參考 [noUiSlider 文檔]((http://refreshless.com/nouislider/more/#section-styling))
[↑ 回到目錄](#目錄)
## 提供組件 demo
添加 `index.html` 文件作為組件的 demo 示例,并提供不同配置情況的效果,說明組件是如何使用的。
### Why?
* demo 可以說明組件是獨立可使用的。
* demo 可以讓開發者預覽組件的功能效果。
* demo 可以展示組件各種配置參數下的功能。
[↑ 回到目錄](#目錄)
## 對組件文件進行代碼校驗
代碼校驗可以保持代碼的統一性以及追蹤語法錯誤。.vue 文件可以通過使用 `eslint-plugin-html`插件來校驗代碼。你可以通過 `vue-cli` 來開始你的項目,`vue-cli` 默認會開啟代碼校驗功能。
### Why?
* 保證所有的開發者使用同樣的編碼規范。
* 更早的感知到語法錯誤。
### How?
為了校驗工具能夠校驗 `*.vue`文件,你需要將代碼編寫在 `<script>`標簽中,并使[組件表達式簡單化](#保持組件表達式簡單化),因為校驗工具無法理解行內表達式,配置校驗工具可以訪問全局變量 `vue` 和組件的 `props`。
#### ESLint
[ESLint](http://eslint.org/) 需要通過 [ESLint HTML 插件](https://github.com/BenoitZugmeyer/eslint-plugin-html#eslint-plugin-html)來抽取組件中的代碼。
通過 `.eslintrc` 文件來配置 ESlint,這樣 IDE 可以更好的理解校驗配置項:
```json
{
"extends": "eslint:recommended",
"plugins": ["html"],
"env": {
"browser": true
},
"globals": {
"opts": true,
"vue": true
}
}
```
運行 ESLint
```bash
eslint src/**/*.vue
```
#### JSHint
[JSHint](http://jshint.com/) 可以解析 HTML(使用 `--extra-ext`命令參數)和抽取代碼(使用 `--extract=auto`命令參數)。
通過 `.jshintrc` 文件來配置 ESlint,這樣 IDE 可以更好的理解校驗配置項。
```json
{
"browser": true,
"predef": ["opts", "vue"]
}
```
運行 JSHint
```bash
jshint --config modules/.jshintrc --extra-ext=html --extract=auto modules/
```
注:JSHint 不接受 `vue` 擴展名的文件,只支持 `html`。
## 只在需要時創建組件
### Why?
Vue.js 是一個基于組件的框架。如果你不知道何時創建組件可能會導致以下問題:
* 如果組件太大, 可能很難重用和維護;
* 如果組件太小,你的項目就會(因為深層次的嵌套而)被淹沒,也更難使組件間通信;
### How?
* 始終記住為你的項目需求構建你的組件,但是你也應該嘗試想到它們能夠從中脫穎而出(獨立于項目之外)。如果它們能夠在你項目之外工作,就像一個庫那樣,就會使得它們更加健壯和一致。
* 盡可能早地構建你的組件總是更好的,因為這樣使得你可以在一個已經存在和穩定的組件上構建你的組件間通信(props & events)。
### 規則
* 首先,盡可能早地嘗試構建出諸如模態框、提示框、工具條、菜單、頭部等這些明顯的(通用型)組件。總之,你知道的這些組件以后一定會在當前頁面或者是全局范圍內需要。
* 第二,在每一個新的開發項目中,對于一整個頁面或者其中的一部分,在進行開發前先嘗試思考一下。如果你認為它有一部分應該是一個組件,那么就創建它吧。
* 最后,如果你不確定,那就不要。避免那些“以后可能會有用”的組件污染你的項目。它們可能會永遠的只是(靜靜地)待在那里,這一點也不聰明。注意,一旦你意識到應該這么做,最好是就把它打破,以避免與項目的其他部分構成兼容性和復雜性。
[↑ 回到目錄](#目錄)
---
[官網規范參見](https://cn.vuejs.org/v2/style-guide/)
- 概要
- 技術介紹
- 框架與環境
- vue開發
- 開發規范
- 前端開發規范
- 總體原則
- HTML規范
- HTML&css規范
- vue編碼規范
- Javascript規范
- 后端開發規范
- cap4
- 自定義控件
- 前端2.0(PC+移動)
- PC前端
- 后端
- 移動端
- 移動端接口
- 低版本協同升級到V5 8.0適配說明
- 自定義按鈕
- 自定義按鈕(無流程)
- 自定義控件(列表插槽)
- 自定義按鈕(篩選條件)
- 低版本協同升級到V5 8.0適配說明
- 門戶空間
- 門戶與欄目掛載
- 欄目開發及流程說明
- 頁面模板
- 客開通路及插件體系
- 表單設計器擴展配置
- 使用步驟
- 配置說明
- 事件API
- Demo示例
- 運行態客開通路
- 插件使用步驟
- 插件接口
- 事件接口
- 鉤子相關接口
- 表單操作接口
- Demo示例
- 插件機制
- 表單運行態接口(舊)
- 白名單插件
- 版本記錄
- vue組件庫
- 開發指南
- 開發文檔規范
- 業務組件介紹
- 業務組件
- table組件
- 分頁組件
- title組件
- 統計排隊組件
- code組件
- 條件篩選
- 批量導入
- 上傳Excel
- 批量更新
- 批量刷新
- UI組件
- 按鈕組件
- 復選組件
- 取色器組件
- 示例組件
- 水平選擇組件
- 選圖標組件
- 提示組件
- 單選組件
- 搜索組件
- 選擇組件
- 穿梭框組件
- 標簽組件
- 文本組件
- 樹組件
- 驗證組件
- 菜單組件
- iframe組件
- toolbar
- 統計組件
- 餅圖
- 柱狀圖
- 圖標
- 業務關系開發指南
- 自定義觸發
- 自定義關聯
- 后端API
- 更新表單數據緩存
- 發起表單流程
- 取得指定表單PDF或截圖
- 無流程批量添加
- 無流程批量刪除
- 無流程批量更新
- 無流程批量導出
- 客開培訓文檔
- Vue基礎培訓
- Vue實戰培訓
- Vue進階培訓
- VueCLI3培訓
- cap3
- 自定義控件
- 后端
- 移動端
- 前端編譯
- 表單運行態接口
- 協同云