一、命名規(guī)范
1呢诬、文件命名
文件夾/文件的命名統(tǒng)一用小寫涌哲,使用短橫線命名 (kebab-case),包括js
尚镰、css
膛虫、html
文件。
案例
assets/
|-- css/
|--- reset.css
|-- image/
|--- icon-logo.png
api/
|-- ajax.js
components/
|- home-header/
|- home-main/
|- home-main/
pages/
|-- PageHome.vue
helpers/
|-- util.js
...
index.html
admin.html
2钓猬、組件命名
vue
組件命名統(tǒng)一大寫單詞開頭稍刀,使用駝峰式命名(PascalCase),組件名應(yīng)該始終是多個(gè)單詞敞曹。
案例
components/
|- home-header/
|-- HeaderLogo.vue
|-- HeaderSearch.vue
|- home-main/
|- home-footer/
pages/
|-- PageHome.vue
二账月、html 規(guī)范
html 標(biāo)簽語義化
-
<header>
:定義文檔或者文檔的部分區(qū)域的頁眉,應(yīng)作為介紹內(nèi)容或者導(dǎo)航鏈接欄的容器澳迫。 -
<nav>
:描述一個(gè)含有多個(gè)超鏈接的區(qū)域局齿,該區(qū)域包含跳轉(zhuǎn)到其他頁面或頁面內(nèi)部其他部分的鏈接列表。 -
<main>
:定義文檔的主要內(nèi)容橄登,該內(nèi)容在文檔中應(yīng)當(dāng)是獨(dú)一無二的抓歼,不包含任何在文檔中重復(fù)的內(nèi)容,比如側(cè)邊欄拢锹,導(dǎo)航欄鏈接谣妻,版權(quán)信息,網(wǎng)站 logo卒稳,搜索框(除非搜索框作為文檔的主要功能)蹋半。 -
<article>
:表示文檔、頁面充坑、應(yīng)用或網(wǎng)站中的獨(dú)立結(jié)構(gòu)减江,是可獨(dú)立分配的、可復(fù)用的結(jié)構(gòu)捻爷,如在發(fā)布中辈灼,它可能是論壇帖子、雜志或新聞文章也榄、博客巡莹、用戶提交的評(píng)論、交互式組件手蝎,或者其他獨(dú)立的內(nèi)容項(xiàng)目榕莺。 -
<aside>
:表示一個(gè)和其余頁面內(nèi)容幾乎無關(guān)的部分俐芯,被認(rèn)為是獨(dú)立于該內(nèi)容的一部分且可以被單獨(dú)的拆分出來而不會(huì)影響整體棵介。通常表現(xiàn)為側(cè)邊欄或嵌入內(nèi)容。 -
<footer>
:定義最近一個(gè)章節(jié)內(nèi)容或者根節(jié)點(diǎn)元素的頁腳吧史。一個(gè)頁腳通常包含該章節(jié)作者邮辽、版權(quán)數(shù)據(jù)或者與文檔相關(guān)的鏈接等信息,使用 footer 插入聯(lián)系信息時(shí),應(yīng)在footer
元素內(nèi)使用<address>
元素吨述。 -
<section>
:表示文檔中的一個(gè)區(qū)域(或節(jié))岩睁,比如,內(nèi)容中的一個(gè)專題組揣云。
如果元素內(nèi)容可以分為幾個(gè)部分的話捕儒,應(yīng)該使用 <article>
而不是 <section>
。
不要把 <section>
元素作為一個(gè)普通的容器來使用邓夕,特別是當(dāng)<section>
僅僅是為了美化樣式或方便腳本使用的時(shí)候刘莹,應(yīng)使用<div>
。
通俗來說就是<article>
比<section>
更具有獨(dú)立性焚刚、完整性点弯。可通過該段內(nèi)容脫離了所在的語境矿咕,是否完整抢肛、獨(dú)立來判斷。
頁面基本結(jié)構(gòu):
三碳柱、css 規(guī)范
使用
BEM
規(guī)范進(jìn)行css
命名
BEM 規(guī)范
BEM 代表塊(Block)捡絮,元素(Element),修飾符(Modifier)
編程方法論中一個(gè)最常見的例子就是面向?qū)ο缶幊蹋∣OP)莲镣。這一編程范例出現(xiàn)在許多語言中锦援。在某種程度上,BEM 和 OOP 是相似的剥悟。
塊(Block)
使用 vue 進(jìn)行開發(fā)灵寺,一個(gè)組件就是一個(gè)
Block
一個(gè)塊是一個(gè)獨(dú)立的實(shí)體,就像應(yīng)用的一塊“積木”区岗。一個(gè)塊既可以是簡單的也可以是復(fù)合的(包含其他塊)略板。
例如一個(gè)輸入域和一個(gè)按鈕是 Search 塊的中的元素。
元素(Element)
一個(gè)元素是塊的一部分慈缔,具有某種功能叮称。元素是依賴上下文的:它們只有處于他們應(yīng)該屬于的塊的上下文中時(shí)才是有意義的。
例如一個(gè)輸入域和一個(gè)按鈕是 Search 塊的中的元素藐鹤。
用塊與元素來描述頁面
頁面的內(nèi)容由塊和元素構(gòu)成瓤檐,每個(gè)塊都可以看成一個(gè)組件,部分公用性比較強(qiáng)的元素娱节,也可以看成一個(gè)組件挠蛉。
一個(gè)復(fù)雜塊里有可能再嵌套多個(gè)單一功能塊。
例如肄满,一個(gè) Head 塊會(huì)包含其他塊:
每一個(gè)塊和元素谴古,都應(yīng)該有對(duì)應(yīng)的關(guān)鍵字质涛。
用來標(biāo)識(shí)一個(gè)具體塊的關(guān)鍵字其實(shí)就是這個(gè)塊的名字(block name
)。
例如掰担,menu
可以作為Menu
塊的關(guān)鍵字汇陆,head
可以作為Head
塊的關(guān)鍵字。
例如带饱,菜單中的每個(gè)菜單項(xiàng)就是menu
塊的item
元素毡代。
一個(gè)塊范圍內(nèi)的一種元素的名字也必須是唯一的。一種元素可以重復(fù)出現(xiàn)多次勺疼。
例如上面的 head 塊月趟,可以這樣分解
<block:xxx>
表示一個(gè)塊,<element:column>
表示一個(gè)元素
<block:page>
<block:head>
<block:menu> … </block:menu>
<element:column>
<block:logo/>
</element:column>
<element:column>
<block:search>
<element:input/>
<element:button>Search</element:button>
</block:search>
</element:column>
<element:column>
<block:auth> … </block:auth>
<element:column>
</block:head>
</block:page>
這種結(jié)構(gòu)可以叫做 BEM 樹(和 DOM 樹類似)恢口。
塊的獨(dú)立性
一個(gè)獨(dú)立的塊等于一個(gè)獨(dú)立的組件孝宗,可以放置在頁面的任意位置 ,包括嵌套在其他塊里耕肩。
使用 BEM 規(guī)范來命名 CSS
獨(dú)立的 css
從 CSS 的角度來看:
- 一個(gè)塊(或者一個(gè)元素)必須有一個(gè)唯一的“名字”(一個(gè) CSS 類)這樣才能被 CSS 規(guī)則所作用因妇。
- HTML 元素不能用作 CSS 選擇器(如.menu td)因?yàn)檫@樣的選擇器并非是完全上下文無關(guān)的。
- 避免使用級(jí)聯(lián)(cascading)選擇器(注:如.menu .item)猿诸。
下面是一種可能的 CSS 類命名方案:
一個(gè)元素的 CSS 類名是一個(gè)塊名和一個(gè)元素名的組合婚被,它們中間用一些符號(hào)隔開。
- block: menu
- element: item
- modifier: active
<!-- block menu -->
<ul class="menu">
<li class="menu-item">…</li>
<li class="menu-item">…</li>
<li class="menu-item menu-item-active">…</li>
</ul>
<!-- block search-form -->
<form class="search-form search-form-theme-gray">
<div class="search-form-content">
<input class="search-form-input"/>
<button class="search-form-button search-form-button-disable"></button>
</div>
</form>
一個(gè)相對(duì)復(fù)雜的例子
下面是根據(jù)之前的 header 例子創(chuàng)建的一個(gè)對(duì)應(yīng)的 html 結(jié)構(gòu)
1梳虽、根據(jù)頁面結(jié)構(gòu)生成 bem 樹結(jié)構(gòu)
<block:page>
<block:head>
<block:menu> … </block:menu>
<element:column>
<block:logo/>
</element:column>
<element:column>
<block:search>
<element:input/>
<element:button>Search</element:button>
</block:search>
</element:column>
<element:column>
<block:auth> … </block:auth>
<element:column>
</block:head>
</block:page>
2址芯、轉(zhuǎn)換成對(duì)應(yīng)的 dom
樹以及對(duì)應(yīng)的 class
命名
<article class="home">
<!-- block header -->
<header class="header">
<!-- element header-menu -->
<!-- block menu -->
<ul class="menu header-menu">
<!-- element menu-item -->
<li class="menu-item"></li>
<li class="menu-item"></li>
<li class="menu-item menu-item-active"></li>
<li class="menu-item"></li>
</ul>
<!-- block logo -->
<section class="logo">
<!-- element logo-txt -->
<span class="logo-txt">logo</span>
<i class="logo-icon"></i>
</section>
<!-- block search -->
<section class="search">
<input class="search-ipt" type="text">
<button class="search-btn search-btn-disable"></button>
</section>
<!-- block auth -->
<section class="auth">
<input class="auth-username" type="text">
<input class="auth-password" type="password">
</section>
</header>
<!-- block main -->
<main class="main"></main>
<!-- block footer -->
<footer class="footer"></footer>
</article>
3、使用 vue 組件進(jìn)行分解
<!-- HeaderMenu.vue -->
<template>
<ul class="menu">
<li class="menu-item"></li>
<li class="menu-item"></li>
<li class="menu-item menu-item-active"></li>
<li class="menu-item"></li>
</ul>
</template>
<!-- HeaderLogo.vue -->
<template>
<section class="logo">
<span class="logo-txt">logo</span>
<i class="logo-icon"></i>
</section>
</template>
<!-- HeaderSearch.vue -->
<template>
<section class="search">
<input class="search-ipt" type="text">
<button class="search-btn search-btn-disable"></button>
</section>
</template>
<!-- HeaderAuth.vue -->
<template>
<section class="auth">
<input class="auth-username" type="text">
<input class="auth-password" type="password">
</section>
</template>
<!-- HomeHeader.vue -->
<template>
<header class="header">
<header-menu/>
<header-logo/>
<header-search/>
<header-auth/>
</header>
</template>
<!-- HomeMain.vue -->
<template>
<main class="main">...</main>
</template>
<!-- HomeFooter.vue -->
<template>
<footer class="footer">...</footer>
</template>
<!-- PageHome.vue -->
<template>
<home-header/>
<home-main/>
<home-footer/>
</template>
4窜觉、文件結(jié)構(gòu)
components/
|- home-header/
|-- HomeHeader.vue
|-- HeaderMenu.vue
|-- HeaderLogo.vue
|-- HeaderSearch.vue
|-- HeaderAuth.vue
|- HomeMain.vue
|- HomeFooter.vue
pages/
|- PageHome.vue
四谷炸、js 規(guī)范
1、使用 prettier
來規(guī)范 js
與 css
代碼格式
- vscode 的插件中搜索
prettier
禀挫,進(jìn)行安裝 - 在文件-首選項(xiàng)-設(shè)置中旬陡,將以下配置加到
User Settings
配置文件
"editor.formatOnSave": true
"prettier.singleQuote": true,
"prettier.semi": false
2、js 代碼規(guī)范
2.1 變量
- 命名方式:小駝峰
- 命名規(guī)范:前綴名詞
- 命名建議:語義化
變量聲明要根據(jù)上下文環(huán)境語義化聲明语婴,不能使用無任何語義的關(guān)鍵詞或者數(shù)字來表示變量
除了一些約定俗成的簡寫描孟,正常情況下盡量不使用簡寫聲明變量
需要做到看到變量名稱,就知道這個(gè)變量是用來做什么的
// bad
let setCount = 10
let input1 = document.querySelector('#username')
let isUserActive = true
// good
let maxCount = 10
let inputUser = document.querySelector('#username')
let userActive = true
2.2 常量
- 命名方式:全部大寫
- 命名規(guī)范:使用大寫字母和下劃線來組合命名砰左,下劃線用以分割單詞
- 命名建議:語義化
案例
// bad
async function fetchSomething(data) {
let result = await api.post('http://www.baidu.com', data)
return result
}
function isCurrentCount(count) {
return count < 10
}
// good
// config.js
const MAX_COUNT = 10
const API_ROOT = 'http://www.baidu.com'
// xxx.js
import { MAX_COUNT, API_ROOT } from './config.js'
async function fetchSomething(data) {
let result = await api.post(API_ROOT, data)
return result
}
function isCurrentCount(count) {
return count < MAX_COUNT
}
2.3 函數(shù)
命名規(guī)范
- 命名方式:小駝峰式命名法匿醒。
- 命名規(guī)范:前綴應(yīng)當(dāng)為動(dòng)詞。
- 命名建議:語義化缠导。
可以參考如下的動(dòng)作:
- has: 判斷是否含有某個(gè)值
- is: 判斷是否為某個(gè)值
- get: 獲取某個(gè)值
- set: 設(shè)置某個(gè)值
- update: 更新某個(gè)值
- fetch: ajax 請求(一般用在
vuex
里的actions
) - on: 觸發(fā)事件(
click/change
等dom
事件或者emit
派發(fā)事件) - render: 渲染頁面
- handle: 執(zhí)行某一個(gè)事件(如果不清楚用什么動(dòng)詞前綴廉羔,可以使用
handle
)
還有很多類似的動(dòng)作,例如:add/delete/put/select/change/move/remove/to
等
案例:
// 接口請求
async function fetchUserInfo(id) {
const result = await request.post('/api/userInfo', { id })
return result
}
// 判斷是否含有username
function hasUserName(user) {
// doSomething
if (user.name === xxx) {
return true
} else {
return false
}
}
// 獲取用戶信息
function getUserInfo(id) {
let userInfo = this.fetchUserInfo(id)
return {
name: userInfo.name,
role: userInfo.role
}
}
// 觸發(fā)某個(gè)dom事件
function onUserIptBlur(e) {
let username = e.target.value
this.changeUserName(username)
}
function changeUserName(username) {
this.username = username
}
// 渲染登錄浮層
function renderLoginModel() {
this.loginModelVisible = true
}
控制函數(shù)的副作用
set
和update
等動(dòng)詞前綴的方法酬核,一般用來修改某個(gè)全局變量蜜另,有一定副作用,除了修改 vuex
修改狀態(tài)使用嫡意,其它情況建議使用get
返回一個(gè)新的修改后的對(duì)象举瑰,然后在handle
方法中修改該全局變量
不要修改函數(shù)的入?yún)?/p>
案例:
// bad
function addRoleToUser(user) {
let role = await fetchUserRole()
user.role = role
}
// good
function getRoleUser(user) {
let role = await fetchUserRole()
return {role, ...user}
}
// vuex中,actions和mutaions命名可以使用set/add/update等動(dòng)詞前綴
mutations = {
updateUserInfo(state, userInfo) {
state.userInfo = userInfo
}
}
actions = {
setUserInfo({ commit }, data) {
apis.fetchUserInfo(data).then(res => {
let userInfo = res.result
commit('updateUserInfo', userInfo)
})
}
}
// 如果確實(shí)需要修改數(shù)據(jù),可以走vuex數(shù)據(jù)流
...mapMutations(['updateUserInfo'])
function changeUserRole(user) {
let roleUser = this.getRoleUser(user)
this.updateUserInfo(roleUser)
}
// 或者
function changeUserRole(user) {
this.roleUser = this.getRoleUser(user)
}
無副作用的函數(shù)蔬螟,是不依賴上下文此迅,也不改變上下文的函數(shù)
案例:
// bad
async function addFavoritesToUser(user) {
const result = await fetchUserFavorits(user.id)
user.favoriteBooks = result.books
user.favoriteSongs = result.songs
user.isMusicFan = result.songs.length > 100
}
// good
async function getUserDetail(user) {
const { books, songs, isMusicFan } = await getUserFavorites(id)
return Object.assign(user, { books, songs, isMusicFan })
}
async function getUserFavorites(id) {
const { books, songs } = await fetchUserFavorits(user.id)
return {
books,
songs,
isMusicFan: result.songs.length > 100
}
}
最小函數(shù)準(zhǔn)則
一個(gè)函數(shù)只做一件事情,提高代碼可維護(hù)性和模塊化
// bad
async function fetchUserInfo(id) {
const isSingle = typeof idList === 'string'
const idList = isSingle ? [id] : id
const result = await request.post('/api/userInfo', { idList })
return isSingle ? result[0] : result
}
const userList = await fetchUserInfo(['1011', '1013'])
const user = await fetchUserInfo('1017')
遵循一個(gè)函數(shù)只做一件事的原則旧巾,我們可以將上述功能拆成兩個(gè)函數(shù)fetchMultipleUser
和 fetchSingleUser
來實(shí)現(xiàn)耸序。在需要獲取用戶數(shù)據(jù)時(shí),只需要選擇調(diào)用其中的一個(gè)函數(shù)鲁猩。
async function fetchMultipleUser(idList) {
return await request.post('/api/users/', { idList })
}
async function fetchSingleUser(id) {
return await fetchMultipleUser([id])[0]
}
上述改良不僅改善了代碼的可讀性坎怪,也改善了可維護(hù)性。舉個(gè)例子廓握,如果后期需要去除單一查詢功能搅窿,按照未改良前的邏輯,需要在函數(shù)內(nèi)部進(jìn)行變動(dòng)隙券,而且很擔(dān)心會(huì)有其它問題男应。按照改良后的版本,直接去掉fetchSingleUser
方法就行
五娱仔、組件規(guī)范
每個(gè) Vue 組件的代碼建議不要超出 200 行沐飘,如果超出建議拆分組件。
組件一般情況下是可以拆成基礎(chǔ)/ui 部分和業(yè)務(wù)部分牲迫,基礎(chǔ)組件一般是承載呈現(xiàn)耐朴,基礎(chǔ)功能,不和業(yè)務(wù)耦合部分盹憎。
業(yè)務(wù)組件一般包含業(yè)務(wù)功能業(yè)務(wù)特殊數(shù)據(jù)等等隔箍。
組件規(guī)范
1、UI 組件/基礎(chǔ)組件
放在src/components
里
UI 組件可以是某個(gè)頁面的一塊block
脚乡,和業(yè)務(wù)關(guān)聯(lián)性較強(qiáng)蜒滩,數(shù)據(jù)由容器組件通過 props
傳給 ui 組件,容器組件由 ui 組件組成
基礎(chǔ)組件可以是公共組件奶稠,業(yè)務(wù)性較弱俯艰,通用性強(qiáng),可以包含一些公共 mixin
參考目錄結(jié)構(gòu):
components/
|- base/
|-- BaseDialog.vue
|-- BaseToast.vue
|- home-header/
|-- HomeHeader.vue
|-- HeaderMenu.vue
|- HomeMain.vue
|- HomeFooter.vue
2锌订、容器組件
放在src/pages
里
和當(dāng)前業(yè)務(wù)耦合性比較高竹握,由多個(gè)基礎(chǔ)組件組成,可承載當(dāng)前頁的業(yè)務(wù)接口請求和數(shù)據(jù)(vuex
)辆飘。
容器組件獲取vuex
相關(guān)狀態(tài)啦辐,通過props
傳遞數(shù)據(jù)給 ui 組件或者基礎(chǔ)組件谓传,通過$emit
來獲取子組件分發(fā)的數(shù)據(jù)
參考目錄結(jié)構(gòu):
pages/
|- PageHome.vue
組件開發(fā)風(fēng)格
組件名為多個(gè)單詞
組件名應(yīng)該始終是多個(gè)單詞的,根組件 App
除外芹关。
這樣做可以避免跟現(xiàn)有的以及未來的 HTML
元素相沖突续挟,因?yàn)樗械?HTML
元素名稱都是單個(gè)單詞的。
// bad
Vue.component('todo', {
// ...
})
export default {
name: 'Todo',
// ...
}
// good
Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem',
// ...
}
組件命名的大小寫
單文件組件的文件名應(yīng)該要么始終是單詞大寫開頭 (PascalCase)侥衬,要么始終是橫線連接 (kebab-case)诗祸。
組件文件命名建議統(tǒng)一
PascalCase
// bad
components/
|- mycomponent.vue
components/
|- myComponent.vue
// good
components/
|- MyComponent.vue
components/
|- my-component.vue
組件引用的大小寫
在 js/vue
文件內(nèi)部的組件名可以是PascalCase
或者kebab-cas
,在 dom
模板中始終是 kebab-case
的
建議在所有地方使用
kebab-case
引用
<!-- bad -->
<!-- 在單文件組件和字符串模板中 -->
<mycomponent/>
<!-- 在單文件組件和字符串模板中 -->
<myComponent/>
<!-- 在 DOM 模板中 -->
<MyComponent></MyComponent>
<!-- good -->
<!-- 在單文件組件和字符串模板中 -->
<MyComponent/>
<!-- 在 DOM 模板中 -->
<my-component></my-component>
<!-- 或者在所有地方 -->
<my-component></my-component>
基礎(chǔ)組件名用特定前綴開頭
應(yīng)用特定樣式和約定的基礎(chǔ)組件 (也就是展示類的、無邏輯的或無狀態(tài)的組件) 應(yīng)該全部以一個(gè)特定的前綴開頭轴总,比如 Base
直颅、App
或 V
。
// bad
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
// good
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
單例組件名用 The 前綴
只應(yīng)該擁有單個(gè)活躍實(shí)例的組件應(yīng)該以 The 前綴命名怀樟,以示其唯一性功偿。
這不意味著組件只可用于一個(gè)單頁面,而是每個(gè)頁面只使用一次往堡。這些組件永遠(yuǎn)不接受任何 prop脖含,因?yàn)樗鼈兪菫槟愕膽?yīng)用定制的,而不是它們在你的應(yīng)用中的上下文投蝉。如果你發(fā)現(xiàn)有必要添加 prop养葵,那就表明這實(shí)際上是一個(gè)可復(fù)用的組件,只是目前在每個(gè)頁面里只使用一次瘩缆。
// bad
components/
|- Heading.vue
|- MySidebar.vue
// good
components/
|- TheHeading.vue
|- TheSidebar.vue
緊密耦合的組件名
和父組件緊密耦合的子組件應(yīng)該以父組件名作為前綴命名关拒。
如果一個(gè)組件只在某個(gè)父組件的場景下有意義,這層關(guān)系應(yīng)該體現(xiàn)在其名字上庸娱。因?yàn)榫庉嬈魍ǔ?huì)按字母順序組織文件着绊,所以這樣做可以把相關(guān)聯(lián)的文件排在一起。
// bad
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
// good
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue