Vue開發(fā)規(guī)范經(jīng)驗總結(jié)(部分)
規(guī)范與每個團隊和個人都是息息相關的,因為其影響的不只是只是代碼的維護和理解成本,嚴重的時候是會影響成員開發(fā)的心情
一個團隊的編碼規(guī)范、git規(guī)范等,并沒有絕對的最優(yōu)解,心里要清楚明白沒有銀彈,規(guī)范是為了讓團隊統(tǒng)一,提高代碼閱讀性、降低代碼維護成本等,本文是記錄一些在項目code review中常見的規(guī)范,僅供參考
JS部分
和渲染無關的數(shù)據(jù)
vue中data的數(shù)據(jù)默認便會進行雙向數(shù)據(jù)綁定,若是將大量的和渲染無關的數(shù)據(jù)直接放置在data中,將會浪費雙向數(shù)據(jù)綁定時所消耗的性能,將這些和渲染無關的數(shù)據(jù)進行抽離并配合Object.freeze進行處理。
table中columns數(shù)據(jù)可以單獨提取一個外部js文件作為配置文件,也可以在當前.vue文件中定義一個常量定義columns數(shù)據(jù),因為無論如何都是固定且不會修改的數(shù)據(jù),應該使用Object.freeze進行包裹,既可以提高性能還可以將固定的數(shù)據(jù)抽離,一些下拉框前端固定的數(shù)據(jù)也建議此操作
const?columnList?=?Object.freeze([
??{?title:?'姓名',?key:?'name',?align:?'center'?},
??{?title:?'性別',?key:?'gender',?align:?'center'?}
])
需要注意的是
Object.freeze()凍結(jié)的是值,這時仍然可以將變量的引用替換掉,還有確保數(shù)據(jù)不會變才可以使用這個語法,如果要對數(shù)據(jù)進行修改和交互,就不適合使用凍結(jié)了。
Modal框的控制
一個頁面種通常會存在很多個不同功能的彈框,若是每一個彈框都設置一個對應的變量來控制其顯示,則會導致變量數(shù)量比較冗余和命名困難,可以使用一個變量來控制同一頁面中的所有Modal彈框的展示
比如某個頁面中存在三個Modal彈框
//?bad
//?每一個數(shù)據(jù)控制對應的Modal展示與隱藏
new?Vue({
????data()?{
????????return?{
????????????modal1:?false,
????????????modal2:?false,
????????????modal3:?false,
????????}
????}
})
//?good
//?當modalType為對應的值時?展示其對應的彈框
new?Vue({
????data()?{
????????return?{
????????????modalType:?''?//?modalType值為?modal1,modal2,modal3
????????}
????}
})
debounce使用
例如遠程搜索時需要通過接口動態(tài)的獲取數(shù)據(jù),若是每次用戶輸入都接口請求,是浪費帶寬和性能的
當一個按鈕多次點擊時會導致多次觸發(fā)事件,可以結(jié)合場景是否立即執(zhí)行immediate
<Select?:remote-method="remoteMethod">
????<Option?v-for="item?in?temoteList"?:value="item.value"?:key="item.id">{{item.label}}Option>
Select>
import?{debounce}?from?'lodash'
methods:{
??? remoteMethod:debounce(function?(query)?{
????????//?to?do?...
???????//?this?的指向沒有問題
????},?200),
}
圖片
功能的開發(fā)過程中,圖片的處理往往是比較容易被忽略的環(huán)節(jié),也會在一定程度影響開發(fā)的效率和頁面的性能
圖片壓縮問題,除非特別要求圖片必須高質(zhì)量的顯示,否則都應該進行對應的壓縮處理
不同業(yè)務場景進行圖片格式的選型
JPG 適用于呈現(xiàn)色彩豐富的圖片,JPG 圖片經(jīng)常作為大的背景圖、輪播圖或 Banner 圖出現(xiàn)等 Logo、顏色簡單且對比強烈的圖片或背景、需要透明度等 將常用且變動頻率很低的小圖片進行合并成雪碧圖,對于變動比較頻繁和小于6KB的圖片進行base64 處理根據(jù)項目圖片數(shù)量和項目的用戶機型分布等,考慮采取webp進行圖片的處理
路由組件傳參
在組件中使用 $route 會使之與其對應路由形成高度耦合,從而使組件只能在某些特定的 URL 上使用,限制了其靈活性。
使用 props 將組件和路由解耦:
取代與 $route的耦合
const?User?=?{
??template:?'User?{{?$route.params.id?}}'
}
const?router?=?new?VueRouter({
??routes:?[
????{?path:?'/user/:id',?component:?User?}
??]
})
通過 props 解耦
這樣你便可以在任何地方使用該組件,使得該組件更易于重用和測試。
const?User?=?{
??props:?['id'],
??template:?'User?{{?id?}}'
}
const?router?=?new?VueRouter({
??routes:?[
????{?path:?'/user/:id',?component:?User,?props:?true?},
????//?對于包含命名視圖的路由,你必須分別為每個命名視圖添加?`props`?選項:
????{
??????path:?'/user/:id',
??????components:?{?default:?User,?sidebar:?Sidebar?},
??????props:?{?default:?true,?sidebar:?false?}
????}
??]
})
Vue生命周期
在父子組件中,掌握父子組件對應的生命周期鉤子加載順序可以讓開發(fā)者在更合適的時候做適合的事情,這個問題在最近的面試中起碼被問到了三次,還是需要注意的
父組件
<template>
??<div>
????<h3>homeh3>
????<list?@hook:mounted="listMounted"?/>
??div>
template>
<script>
import?List?from?'./list'
export?default?{
name:?"home",
components:?{
List
},
methods:?{
listMounted(){
console.log('------------?listMounted');
}
},
beforeCreate()?{
console.log("home?beforeCreate");
},
created()?{
console.log("home?created");
},
beforeMount()?{
console.log("home?beforeMount");
},
mounted()?{
console.log("home?mounted");
},
beforeDestroy()?{
console.log("home?beforeDestroy");
},
destroyed()?{
console.log("home?destroyed");
}
}
script>
子組件
<template>
??<div>
????list
??div>
template>
<script>
export?default?{
naem:?"list",
beforeCreate()?{
console.log("list?beforeCreate");
},
created()?{
console.log("list?created");
},
beforeMount()?{
console.log("list?beforeMount");
},
mounted()?{
console.log("list?mounted");
},
beforeDestroy()?{
console.log("list?beforeDestroy");
},
destroyed()?{
console.log("list?destroyed");
}
}
script>
加載時父子組件的加載順序
home?beforeCreate?-->?home?created?-->?home?beforeMount?-->?list?created?-->?list?beforeMount?-->?list?mounted
銷毀時父子組件的銷毀順序
home?beforeDestroy?-->?list?beforeDestroy?-->?list?destroyed?-->?home?destroyed
實際開發(fā)過程中會遇到當子組件某個生命周期完成之后通知父組件,然后在父組件做對應的處理
emit up
//?子組件在對應的鉤子中發(fā)布事件
created(){
??this.$emit('done')
}
//?父組件訂閱其方發(fā)
"childDone">
hook
通過@hook監(jiān)聽子組件的生命周期
"listMounted"?/>
Select優(yōu)化
下拉框遍歷時,需要注意options標簽保持同一行,若是存在換行,會導致選中時的值存在多余的空白
<Select?:remote-method="remoteMethod">
????<Option?v-for="item?in?temoteList"?:value="item.value"?:key="item.id">
????????{{item.label}}
????Option>
Select>
需要將Options和下拉框的值保持在同一行。
<Select?:remote-method="remoteMethod">
????<Option?v-for="item?in?temoteList"?:value="item.value"?:key="item.id">{{item.label}}Option>
Select>
data數(shù)據(jù)層級
data數(shù)據(jù)具有數(shù)據(jù)層級結(jié)構(gòu),切勿過度扁平化或者嵌套層級過深,若是過度扁平化會導致數(shù)據(jù)命名空間沖突,參數(shù)傳遞和處理,若是層級嵌套過深也會導致vue數(shù)據(jù)劫持的時候遞歸層級過深,若是嵌套層級喪心病狂那種的,小心遞歸爆棧的問題。而且層級過深會導致數(shù)據(jù)操作和處理不便,獲取數(shù)據(jù)做容錯處理也比較繁瑣。一般層級保持2-3層最好。
若是只有一層數(shù)據(jù),過于扁平
{
????name:?'',
????age:?'',
????gender:?''
}
導致處理不方便
//?作為接口參數(shù)傳遞
ajax({
?this.name,?this.age,?this.gender
})
//?接口獲取數(shù)據(jù),批量處理
ajax().then(res?=>?{
?const?{name,?age,?gender}?=?res.data
????this.name?=?name
????this.age?=?age
????this.gender?=?gender
})
適當?shù)膶蛹壗Y(jié)構(gòu)不僅增加代碼的維護和閱讀性,還可以增加操作和處理的便捷性
{
????person:?{?//?個人信息
????????name:?'',
????????age:?'',
????????gender:?''
????}
}
可以針對person進行操作
//?作為接口參數(shù)傳遞
ajax(this.person)
//?接口獲取數(shù)據(jù),批量處理
ajax().then(res?=>?{
?const?{name,?age,?gender}?=?res.data
????this.$set(this,?'person',?{name,?age,?gender})
})
策略模式
策略模式的使用,避免過多的if else判斷,也可以替代簡單邏輯的switch。
const?formatDemandItemType?=?(value)?=>?{
????switch?(value)?{
????????case?1:
????????????return?'基礎'
????????case?2:
????????????return?'高級'
????????case?3:
????????????return?'VIP'
????}
}
//?策略模式
const?formatDemandItemType2?=?(value)?=>?{
????const?obj?=?{
????????1:?'基礎',
????????2:?'高級',
????????3:?'VIP',
????}
????
????return?obj[value]
}
解構(gòu)
解構(gòu)賦值以及默認值,當解構(gòu)的數(shù)量小于多少時適合直接解構(gòu)并賦值默認值,數(shù)據(jù)是否進行相關的聚合處理
const?{
??naem?=?'',
??age?=?10,
??gender?=?'man'
}?=?res.data
//?bad
this.name?=?name
this.age?=?age
this.gender?=?gender
//?good
this.person?=?{
??naem,
??age,
??gender
}
職責單一
任何時候盡量是的一個函數(shù)就做一件事情,而不是將各種邏輯全部耦合在一起,提高單個函數(shù)的復用性和可讀性
每個頁面都會在加載完成時進行數(shù)據(jù)的請求并展示到頁面
created()?{
??this.init();
},
methods:?{
??//?將全部的請求行為聚合在init函數(shù)中
??//?將每個請求單獨拆分
??init()?{
????this.getList1()
????this.getList2()
??},
??getList1()?{
????//?to?do?...
??},
??getList2()?{
????//?to?do?...
??}
}
v-bind
在日常的開發(fā)過程中, 提取和封裝組件是一件很常規(guī)的操作,但是當組件需要的參數(shù)非常多時,會導致傳遞一堆的prop,不僅書寫上面比較繁瑣,對代碼的維護和閱讀不是一件有利的事情
例如組件test-demo需要一堆props傳遞
使用時
??<test-demo?
????:data1="data1"
????:data2="data2"
????:data3="data3"
????...假設還有一堆
??/>
test-demo中需要接收處理
{
?props:?['data1',?'data2',?'data3',?...]
}
//?or
props:?{
??modalVisible:?{
????//?控制展示modal
????type:?Boolean,
????default:?false
??},
??data1:?{
????type:?String,
????default:?'1'
??},
??data2:?{
????type:?String,
????default:?'2'
??},
??data3:?{
????type:?String,
????default:?'3'
??}
}
建議將子組件需要的數(shù)據(jù)收集起來,集中在一個對象中,使用v-bind傳遞將這個對象傳遞,子組件的使用和普通的props一樣
<template>
?<test-demo?
????v-bind="obj"
??/>
?template>
<script>
export?default?{
data?()?{
return?{
obj:?{?//?將需要傳遞給子組件的數(shù)據(jù)收集
data1:?'1',
data2:?'2',
data3:?'3'
}
}
}
}
script>
HTML部分
html書寫
編寫template模板時,屬性過多時,是否換行
<template>
??
??<VueButton?class="icon-button?go-up"?icon-left="keyboard_arrow_up"?v-tooltip="$t('org.vue.components.folder-explorer.toolbar.tooltips.parent-folder')"?@click="openParentFolder"?/>
<VueButton
class="icon-button?go-up"
icon-left="keyboard_arrow_up"
v-tooltip="$t('org.vue.components.folder-explorer.toolbar.tooltips.parent-folder')"
@click="openParentFolder"
/>
template>
實體使用
html中展示一些如<,>,&等字符時,使用字符實體代替
<div>
??>?1?&?12
div>
??
<div>
??>?1?&?<?12
div>
CSS部分
樣式穿透
在開發(fā)中修改第三方組件樣式是很常見,但由于 scoped 屬性的樣式隔離,可能需要去除 scoped 或是另起一個 style 。這些做法都會帶來副作用(組件樣式污染、不夠優(yōu)雅),樣式穿透在css預處理器中使用才生效。
less使用 /deep/
<style?scoped?lang="less">
.content?/deep/?.el-button?{
??height:?60px;
}
style>
scss使用 ::v-deep
<style?scoped?lang="scss">
.content?::v-deep?.el-button?{
??height:?60px;
}
style>
stylus使用 >>>
<style?scoped?ang="stylus">
外層?>>>?.custon-components{
??height:?60px;
}
style>
空格
適當?shù)目崭窨梢蕴嵘a的閱讀體驗,顯得更為優(yōu)雅和美觀
選擇器后、屬性值
.custom-style?{?//?選擇器和{?之間空格
??margin:?0;?//?屬性值前
??transform:?scale(1.5,?2.2);?//?逗號之后增加空格
}
換行
和html類型,當某行的屬性很多,適當?shù)膿Q行可以提高閱讀和美觀
.custom-style{
??//?可以在一次聲明中定義一個或多個屬性
??background:?background-clip
????background-color
????background-image
????background-origin
????background-position
????background-repeat
????background-size;
}
當一個規(guī)則包含多個選擇器時,每個選擇器聲明必須獨占一行,過長導致需要橫向滾動閱讀剩余的內(nèi)容,應該盡量使得閱讀順序縱向化
.custom?.header?.title,
.other?.header?.title?{
??color:?#f0f;
}
嵌套層級
瀏覽器在解析css時,是按照從右到左遞歸匹配的,過深的層級嵌套不僅影響性能,而且還會導致樣式閱讀性和代碼維護性降低,一般層架控制在5層之內(nèi)
雙引號
屬性選擇器中的值必須用雙引號包圍,不允許使用單引號,也不允許不使用引號,html的屬性值也是推薦使用雙引號,js中使用單引號
.custom-style{
?font-family:?"PingFang?SC",?"STHeitiSC-Light";??
}
屬性順序
同一 規(guī)則下的屬性在書寫時,應按功能進行分組。并以 Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相關) > Visual(視覺效果) 的順序書寫,以提高代碼的可讀性。
解釋:
Formatting Model 相關屬性包括:position / top / right / bottom / left / float / display / overflow 等 Box Model 相關屬性包括:border / margin / padding / width / height 等 Typographic 相關屬性包括:font / line-height / text-align / word-wrap 等 Visual 相關屬性包括:background / color / transition / list-style 等
另外,為增加可讀性,如果包含 content 屬性,應放在屬性的最前面。
參考
三年 Vue 前端開發(fā)的血與淚總結(jié)
https://gitbook.cn/books/5dd7de04c023a6766a83cfc4/index.html#-1
編碼規(guī)范作用
https://www.zhihu.com/search?type=content&q=%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%E4%BD%9C%E7%94%A8
關于作者
作者:Vam的金豆之路。曾獲得2019年CSDN年度博客之星稱號,CSDN博客、掘金技術社區(qū)訪問量累計已超過二百萬。公眾號:前端歷劫之路專注于前端技術分享與學習,謝謝你關注我。學習前端技術就如同經(jīng)歷一場場劫難,只有堅持、努力才會成為自己心中的大神!共勉~
