看了老外的一篇關於組件開發的建議(強烈建議閱讀英文原版),感覺不錯翻譯一下加深理解。
這篇文章制定一個統一的規則來開發你的vue程序,以至於達到一下目的。
1.讓開發者和開發團隊更容易發現一些事情。
2.讓你更好的利用你的IDE.
3.讓你更加容易的使用打包工具
4.讓你的代碼更容易碎片化以達到復用的目的。
基於模塊開發
用一些功能單一的小模塊來組織你的應用
Why?
對於你自己和你團隊的人來說較小的模塊更容易看懂 維護 復用和調試。
How?
每個組件應該保持單一 獨立 可復用 可測試
把你很大的組件拆分成功能單一的小組件,盡量不讓一個組件的代碼超過100行,保持組件獨立。最好是寫個組件應用的小demo
組件命名
組件命名應該遵從以下幾點原則
- 有意義: 名字不要太詳細,也不要太抽象。
- 短: 名字最好是2-3個單詞。
- 可讀的:容易讓人能讀出來以便我們可以更容易的討論它。
vue組件也應該遵循以下原則
- 遵從元素命名規范 包括連字符,不要使用保留字
- 為了在其他項目中復用,應該以某個模塊名字作為命名空間
Why?
- 為了讓我們更好地通過名字來交流這個組件,這個組件必須 短 有意義 可讀
How?
<!-- 建議這樣 -->
<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。當着這些js很有效,但是也很復雜。因此你應該保持行內表達式簡潔
Why?
- 復雜的行內表達式可讀性差
- 這些行內表達式不能任意地方復用,這樣會導致代碼冗余
- IDE不支持行內式js的語法校驗
How?
把復雜的語法移動到methods或者計算屬性中
<!-- recommended -->
<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>
<!-- avoid -->
<template>
<h1>
{{ `${(new Date()).getUTCFullYear()}-${('0' + ((new Date()).getUTCMonth()+1)).slice(-2)}` }}
</h1>
</template>
保證組件的props簡單
盡管vue支持通過props傳遞復雜的object,但是你要盡量保持props傳遞的數據簡單,盡量只傳遞基本數據類型(strings, numbers, booleans)
Why?
- 簡潔的props讓你的接口api比較簡單
- props只傳遞簡單類型數據和函數,讓我們組件的api看起來更像原生html的屬性。
- props只傳遞簡單類型數據,讓其他開發者容易明白傳什么參數。
- props傳遞復雜數據類型,讓你的組件很難重構,也會造成代碼冗余。
How?
vue component 只傳遞簡單數據類型或者函數如下
<!-- recommended -->
<range-slider
:values="[10, 20]"
min="0"
max="100"
step="5"
:on-slide="updateInputs"
:on-end="updateResults">
</range-slider>
<!-- avoid -->
<range-slider :config="complexConfigObject"></range-slider>
對你組件的props做一些限制
vue 組件中props就是api,健壯且可預測的api讓別人更容易使用你的組件
組件的props通過html屬性來編寫,這些值可以是vue的簡答字符串(:attr="value" or v-bind:attr="value")也可以不寫。你應該對props做一些限制
Why?
對props做一些限制保證你的組件正常工作,即使別人沒有按照你預想的方式調用你的組件。
How?
- 屬性設置默認值
- 屬性設置數據類型校驗
- 使用組件之前檢查props是否存在
<template>
<input type="range" v-model="value" :max="max" :min="min">
</template>
<script type="text/javascript">
export default {
props: {
max: {
type: Number, // [1*] This will validate the 'max' prop to be a Number.
default() { return 10; },
},
min: {
type: Number,
default() { return 0; },
},
value: {
type: Number,
default() { return 4; },
},
},
};
</script>
將組件設定為this
在組件內部上下文中,this指的是vue組件實例,因此在其他上下文中使用它的時候保證'this'在組件中可以使用
換句話說,不要這樣寫 const self = this;
Why?
- 通過把
this
分配給會改變名字的組件,告訴開發者this是一個組件實例。
How?
組件結構
讓你的組件代碼按照一定的順序編寫
Why?
- 用export導出一個清晰的對象,提高代碼可讀性,同時讓開發着統一代碼結構
- 按一下順序排列,讓代碼容易被找到 (name; extends; props, data and computed; components; watch and methods; lifecycle methods, etc.);
- 增加
name
屬性,這樣再使用vue devtools時便於開發測試。 - 按照一定的規則寫css
- 按照如下順序組織代碼template-script-style
How?
<template lang="html">
<div class="Ranger__Wrapper">
<!-- ... -->
</div>
</template>
<script type="text/javascript">
export default {
// Do not forget this little guy
name: 'RangeSlider',
// compose new components
extends: {},
// component properties/variables
props: {
bar: {}, // Alphabetized
foo: {},
fooBar: {},
},
// variables
data() {},
computed: {},
// when component uses other components
components: {},
// methods
watch: {},
methods: {},
// component Lifecycle hooks
beforeCreate() {},
mounted() {},
};
</script>
<style scoped>
.Ranger__Wrapper { /* ... */ }
</style>
組件事件命名
vue提供的vue處理函數和表達式是嚴格綁定在vm上的。每個組件事件應該遵循一個良好的命名規范從而避免開發中出現的問題。
Why?
- 開發者任意使用事件名稱會導致混亂,例如用了原生的事件名。
- 隨意命名事件會導致dom模板不協調。
How?
- 事件命名按照kebab-cased(不用駝峰法)規范(例如download-success)
- 一個事件名稱對應唯一的事件
- 事件名應該已動詞(例如client-api-load)或名詞(例如 drive-upload-success)結尾
避免使用this.$patent
vue支持組件嵌套,子組件獲得父組件上下文,但是獲得外部上下文違反了組件獨立的規定,所有不要使用this.$patent
Why?
- 就像其他組件一樣vue組件也應該獨立工作
- 如果組件依賴他的父組件那么他將難以復用。
How?
- 通過attribute/properties將數據從父組件傳遞給子組件
- 在屬性表達式中把在父組件中定義的會掉函數傳遞到子組件
- 從子組件emit事件到父組件
謹慎使用this.$refs
vue 支持組件通過 this.$refs
來獲得組件或者dom元素的上下文,大部分情況下這中用法應該被禁止。當你用他的時候也應該謹慎防止錯誤的組件api。
Why?
- 就像其他組件一樣,vue的組件應該是獨立的,不能適應所有應用場景的組件是一個不好的組件
- 大部分情況下屬性和事件已經足夠用了
How?
- 設計好的組件api
- 多考慮一些組件在其余業務場景下的重用
- 不要寫一些特殊的代碼,如果你需要些說明你需要設計一個新的組件
- 檢查是否有props缺失,如果是的話補全這些缺陷。
- 檢查所有的事件(event)大多數時候開發者只記得通過props實現父子組件通信嗎,而忘記通過自定義事件。
- 以設計好的api和組件獨立性為目的來更新你的組件
- 當props和自定義事件實在達不到目的再用
this.$refs
- 當元素不能用數據綁定或者指令操作時,用
this.$refs
是比jquery和document.getElement*
好一些的選擇
<!-- good, no need for ref -->
<range :max="max"
:min="min"
@current-value="currentValue"
:step="1"></range>
<!-- good example of when to use this.$refs -->
<modal ref="basicModal">
<h4>Basic Modal</h4>
<button class="primary" @click="$refs.basicModal.close()">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>
<!-- avoid accessing something that could be emitted -->
<template>
<range :max="max"
:min="min"
ref="range"
:step="1"></range>
</template>
<script>
export default {
// ...
methods: {
getRangeCurrentValue() {
return this.$refs.range.currentValue;
},
},
// ...
};
</script>
使用組件名稱作為css作用域
vue 組件的名字作為css根作用域類名是極好的。
Why?
- css在style標簽加上scoped能有效的防止組件內css污染外部組件的css
- css根作用域類名和組件名相同,讓開發者容易理解他們是一個組件中的。
How?
把組件名作為css命名空間依賴BEM和OOCSS(面向對象css)。在style標簽上加scoped。加了scoped告訴vue在編譯時給每個類名都加一個后綴,從而避免污染其余組件或者全局樣式。
<style scoped>
/* recommended */
.MyExample { }
.MyExample li { }
.MyExample__item { }
/* avoid */
.My-Example { } /* not scoped to component or module name, not BEM compliant */
</style>
為你的組件寫api文檔
一個vue實例通過實例化應用中的組件而來。這個實例通過組件屬性配置而來。如果組件要提供給其他開發者使用,這些定制的屬性也就是組件的api應該寫在readme.md中。
Why?
- 文檔提供給開發者一個關於組件的概要,使開發者不需要看組件的源碼。這樣組件比較容易讓人接受和使用。
- 組件的api是使用組件需要配置項的指導。特別是對於那些只使用這個組件的開發者。
- 組件正式的文檔告訴開發者當組件代碼變化了怎么去做兼容
README.md
是一個文檔應該先被閱讀的。github等代碼倉庫通過README.md 來展示代碼內容
How?
給一個組件增加README.md
range-slider/
├── range-slider.vue
├── range-slider.less
└── README.md
其余還包括給你的組件寫小demo,對組件做eslint代碼審查。。