Vue屎山代碼大盤點(diǎn)

前言

相比其他的框架來說,Vue中更容易產(chǎn)出屎山代碼;因?yàn)閂ue中的options就是一個(gè)大對(duì)象,導(dǎo)致js本身的很多檢測(cè)都失效了,比如一個(gè)函數(shù)沒有用到的話會(huì)“變灰”,template中代碼提示比較少,較多的mixins等等;遇到屎山代碼,大多數(shù)人第一反應(yīng)就是這誰寫的代碼這么差,其實(shí)大多數(shù)公司大多數(shù)人至少曾經(jīng)都寫過一些屎山代碼,有屎山代碼很正常,問題在于怎么快速梳理出業(yè)務(wù)邏輯,防止在迭代新需求時(shí)引發(fā)bug,在富有余力的情況下可以進(jìn)行局部重構(gòu),漸進(jìn)式優(yōu)化屎山代碼;

今天重點(diǎn)就看一看Vue2中的那些屎山;

通用屎山

一號(hào)屎山--目錄雜亂

危害程度:??

file

復(fù)制代碼src/

├── App.vue

├── api

├── components

├── constants

├── main.js

├── pages

├── router

├── services

├── utils

│? └── hash.js

└── views

看一下上面的目錄,views和pages是類似的含義,都是指的路由對(duì)應(yīng)的頁面,而api和services也是類似的都是存放后端接口的封裝,同時(shí)存在這幾種文件夾說明項(xiàng)目初期沒有規(guī)范,每個(gè)人按照自己的規(guī)范去開發(fā),導(dǎo)致有的人頁面寫在views里面,有的寫在pages里面,建議這幾個(gè)相似含義的目錄只保留一個(gè);

一號(hào)屎山的危害在于讓后續(xù)接手的人要頻繁切換文件夾去看不同頁面的邏輯,并且不知道后續(xù)自己應(yīng)該在哪個(gè)文件夾開發(fā)自己的頁面,導(dǎo)致惡性循環(huán);

二號(hào)屎山--奇葩命名法

危害程度:??????????

奇葩命名法有以下幾種情況:

全拼音命名法

”畢竟都是中國(guó)人嘛,全拼音命名大家應(yīng)該都看得懂吧“,舉個(gè)例子:dazhe.vue。但是同一個(gè)拼音可以翻譯出不同的意思出來,他們之間是一對(duì)多的關(guān)系,因此不適合作為組件名;當(dāng)然,全拼音命名還算是手下留情的,有的時(shí)候全拼音命名可能會(huì)很長(zhǎng),那就直接取首字母吧!

拼音首字母命名法

于是dazhe.vue變成了dz.vue,這個(gè)時(shí)候就成了猜謎語,有一首歌詞寫得好:女孩的心思男孩你別猜別猜別猜你猜來猜去也猜不明白,到了這里就是代碼的心思你別猜,直接放棄吧!

中西合璧命名法

有些同學(xué)覺得光中文那不太高大上啊,要把英語也加進(jìn)來才能顯示自己的水平,所以這樣命名:dzList.vue,照樣還是讓人看不懂

英文首字母命名法

我想這種方式命名的同學(xué)應(yīng)該不多吧,畢竟已經(jīng)拿起翻譯工具翻譯了,直接cv就可以了,為什么還要摘出首字母來呢?

上面舉了文件名作為例子,其實(shí)命名規(guī)范充斥在所有程序員的每一項(xiàng)工作中,比如:變量命名、函數(shù)命名、類命名、接口命名,以我之見,嚴(yán)格遵循命名規(guī)范是編程的第一步,必須使用翻譯的英文來命名,英文就是一個(gè)字典,至少大部分的英文通過翻譯之后還是能夠準(zhǔn)確地知曉其含義的,這里面錯(cuò)誤的概率遠(yuǎn)遠(yuǎn)低于以上幾種方式;

三號(hào)屎山--組件不拆分

危害程度:??????????

Vue將template、script、style組合在一個(gè).vue文件中,這天然就會(huì)使得每一個(gè).vue文件的行數(shù)會(huì)非常多,難以維護(hù),Vue2中一個(gè)最明顯的屎山就是幾千行、甚至上萬行的代碼,用專業(yè)的術(shù)語來講就是不符合單一職責(zé)原則,一個(gè)組件應(yīng)該只干一件事情,一個(gè)函數(shù)應(yīng)該只處理一個(gè)邏輯,剩下的邏輯交給其他函數(shù)或者組件來做;時(shí)刻牢記“SOLID”原則是遠(yuǎn)離屎山的第一心法;

前面通用屎山已經(jīng)堆積到一定高度,接下來再加大馬力,看看template屎山、script屎山和style屎山。

template屎山

四號(hào)屎山--復(fù)雜的表達(dá)式

危害程度:??????

js

復(fù)制代碼?

class="files"

:class="{?disabled:?!isAllowRead?&&?hasNotPassed?&&?aaa?&&?(bbb?||?ccc)?}"

@click="toDetail()"

>

看這一段代碼,為了判斷一個(gè)禁用狀態(tài),使用了大量的運(yùn)算符,導(dǎo)致邏輯不清晰,并且遇到相似的邏輯在下面b組件上不得不ctrl cv,妥妥地變成了cv工程師,這里正確的做法是應(yīng)該放到計(jì)算屬性里面去進(jìn)行判斷,并且根據(jù)后面所使用到的邏輯進(jìn)行計(jì)算屬性的拆分:

diff

復(fù)制代碼?????

class="files"

:class="{?disabled:?isFileDisabled?}"

@click="toDetail()"

>

export?default?{

//?此處省略...

computed:?{

+??????????????isFileDisabled(){

+?????????????????return?!isAllowRead?&&?hasNotPassed?&&?aaa?&&?(bbb?||?ccc)

+??????????????}

}

}

當(dāng)然isFileDisabled這個(gè)計(jì)算屬性也可以拆分成多個(gè),這個(gè)主要看后續(xù)的復(fù)用情況;所以二號(hào)屎山的優(yōu)化方案就是利用計(jì)算屬性或者拆分計(jì)算屬性

五號(hào)屎山--大量重復(fù)節(jié)點(diǎn)

危害程度:??????

html

復(fù)制代碼

姓名:{{?name?}}

年齡:{{?age?}}

性別:{{?gender?}}

身高:{{?height?}}

體重:{{?weight?}}

愛好{{?habit?}}

優(yōu)化后的代碼:

diff

復(fù)制代碼??

+????<span v-for="item?in?textConfigs"?:key="item.valueKey">{{

+??????response[item.valueKey]

+????}}</span>

data()?{

return?{

+??????textConfigs:?[

+????????{?label:?"性別",?valueKey:?"name"?},

+????????{?label:?"年齡",?valueKey:?"age"?},

+????????{?label:?"性別",?valueKey:?"gender"?},

+????????{?label:?"身高",?valueKey:?"height"?},

+????????{?label:?"體重",?valueKey:?"weight"?},

+????????{?label:?"愛好",?valueKey:?"habit"?}

+??????]

};

},

可能有些同學(xué)認(rèn)為這個(gè)不算是屎山代碼,但是當(dāng)這個(gè)span變得復(fù)雜起來之后甚至這個(gè)span里面包含了幾十行代碼的時(shí)候,就會(huì)發(fā)現(xiàn)這里面的重復(fù)元素太多了,進(jìn)而無法維護(hù);

script屎山

六號(hào)屎山--if else switch

危害程度:????

js

復(fù)制代碼if(!values.username){

this.$message.error("用戶名不能為空")

}elseif(!values.password){

this.$message.error("密碼不能為空")

}elseif(!values.phoneNumber){

this.$message.error("手機(jī)號(hào)不能為空")

}else{

this.submit();

}

可能有人會(huì)說,上面的代碼語義明確,寫得還不夠好嗎?但是如果需要增加更多的校驗(yàn)條件時(shí),開發(fā)者不得不侵入到具體方法去修改代碼,使用策略模式優(yōu)化之后能夠讓校驗(yàn)條件與具體判斷邏輯解耦,當(dāng)需要增加校驗(yàn)條件時(shí)直接修改數(shù)組即可:

js

復(fù)制代碼constvalidators?=?[

{message:"用戶名不能為空",required:true,key:"username"},

{message:"密碼不能為空",required:true,key:"password"},

{message:"手機(jī)號(hào)不能為空",required:true,key:"phoneNumber"}

];

exportdefault{

methods:?{

validator(values)?{

constresult?=?validators.some(el=>{

if(el.required?&&?!values[el.key])?{

this.$message.error(el.message);

returntrue;

}

});

returnresult;

},

submit(values)?{

if(this.validator(values))?{

return;

}

//?...?調(diào)用接口

}

}

};

七號(hào)屎山--后端參數(shù)處理

危害程度:??????

js

復(fù)制代碼????handleParams()?{

constparams?=?{};

params.id?=this.formItem.id;

params.startDate?=this.formItem.startDate.format("YYYY-MM-DD");

params.endDate?=this.formItem.endDate.format("YYYY-MM-DD");

params.price?=this.formItem.price.toString();

params.type?=this.formItem.type;

params.total?=this.formItem.total;

params.name?=this.formItem.name;

params.comment?=this.formItem.comment;

//?...?此處省略一萬行代碼

}

看到這樣的代碼內(nèi)心是崩潰的,明顯只有幾個(gè)字段需要處理一下卻把所有字段都賦值了一遍,可以這樣簡(jiǎn)化:

js

復(fù)制代碼????handleParams()?{

const{?startDate,?endDate,?price,?...params?}?=this.formItem;

params.startDate?=?startDate.format("YYYY-MM-DD");

params.endDate?=?endDate.format("YYYY-MM-DD");

params.price?=?price.toString();

//?...?此處省掉一萬行代碼

}

八號(hào)屎山--硬編碼

危害程度:????????

vue

復(fù)制代碼? computed: {

isGood() {

return this.type === 1;

},

isBad() {

return this.type === 0;

}

}

看上面的例子,這種硬編碼基本隨處可見,作者在寫這段代碼的時(shí)候肯定是覺得這個(gè)type只會(huì)在這里用到,沒有必要單獨(dú)定義一個(gè)常量來管理,后面接收的同學(xué)來了他也不會(huì)去關(guān)注之前的邏輯,他只要用到了type又會(huì)去重新判斷一下是good還是bad,就這樣最后代碼中充斥著0,1,2,3這樣的數(shù)字,后來新人接到一個(gè)需求并且涉及到這些數(shù)字背后的含義這個(gè)時(shí)候就不得不去一個(gè)一個(gè)地詢問原作者了,好的做法就是寫成常量配置文件,單獨(dú)寫一個(gè)文件config.js,然后組件去引用這個(gè)常量:

js

復(fù)制代碼//?貨物的品質(zhì)枚舉值

exportconstGOODS_TYPE?=?{

good:1,//?質(zhì)量好

bad:0//?質(zhì)量差

};

九號(hào)屎山--Mixins屎山

危害程度:??????

我不生產(chǎn)代碼,我只是Mixins的搬運(yùn)工:

js

復(fù)制代碼//?a.mixin.js

exportdefault{

data()?{

return{

username:"",

password:"",

age:18

};

},

created()?{

this.fetchUserInfo();

},

methods:?{

fetchUserInfo()?{}

}

};

//?b.mixin.js

exportdefault{

data(){

return{

height:'',

weight:''

}

},

created(){

this.fetchBodyFat();

},

methods:{

fetchBodyFat(){

}

}

}

//?c.vue

constDEGREEMAP?=?{

doctor:'博士'

}

exportdefault{

mixins:[a,b],

data(){

return{

degree:DEGREEMAP.doctor

}

},

created(){

this.log()

},

methods:{

log(){

if(this.age?<30&&this.height>180&&this.degree===DEGREEMAP.doctor){

alert("真牛!")

}

}

}

}

這里a、b提供了一些數(shù)據(jù),最后統(tǒng)一在c.vue中使用,這樣的話容易造成變量覆蓋以及來路不明等問題,如果必須使用vue2的話這種情況是避免不了的,只能盡量減少組件對(duì)mixins中data的耦合度,但是最近看到一篇文章打開了新的思路,有興趣的可以讀一讀:我可能發(fā)現(xiàn)了Vue Mixin的正確用法——?jiǎng)討B(tài)Mixin

十號(hào)屎山--無用代碼不刪除

危害程度:????

大段注釋不刪除,沒用的methods也不注釋,本著多一事不如少一事的原則,因?yàn)閂ue中父組件調(diào)用子組件方法比較常見,因此有些方法不使用了,自己也不去注釋或者刪除,害怕引發(fā)其他bug,或者干脆就不管,直接寫一個(gè)其他名稱的方法

無用的文件不刪除,比方說開始定義了一個(gè)list.vue,后面這個(gè)文件重構(gòu)了,直接增加了一個(gè)List.vue,目錄中同時(shí)存在這兩個(gè)文件,讓人摸不著頭腦,無形增加了項(xiàng)目復(fù)雜度,試想一下如果一個(gè)超大項(xiàng)目,一半的文件都是沒有引用到的,那是多么的可怕!

style屎山

十一號(hào)屎山--類名無規(guī)范

危害程度:??

將id,駝峰、橫線、下劃線結(jié)合使用:

css

復(fù)制代碼#id{}

#App{}

.AppBuy{}

.app-buy{}

.app_buy{}

.App_Buy{}

好的css是有一定的規(guī)范的,禁止使用id選擇器、!important;類名用橫線分割,或者參考BEM規(guī)范

十二號(hào)屎山--樣式大量重復(fù)

危害程度:??

css

復(fù)制代碼.a{

display:flex;

align-items:center;

justify-content:center;

}

.b{

display:flex;

align-items:center;

justify-content:center;

font-size:16px;

color:red;

}

css樣式大量重復(fù)導(dǎo)致css文件體積劇增,特別是在樣式基本固定的后臺(tái)系統(tǒng)中,寫樣式其實(shí)是一個(gè)痛苦的事情,因此最好是做到原子化公共樣式與業(yè)務(wù)具體樣式的分離

最后一個(gè)屎山--不寫注釋

危害程度:??????????

寫個(gè)注釋是舉手之勞,花不了多少時(shí)間,而且前面所有的屎山堆起來,如果有注釋的話還是可以快速理解其含義的,但是如果再加上不寫注釋,那就是天坑了,誰也救不了這個(gè)屎山;

羅馬的道路不是一日鋪成的,屎山的代碼也不是一天寫成的,而是在每個(gè)開發(fā)者無所謂的心態(tài)下堆成的,如果平時(shí)多注意注意至少也能保證自己寫的代碼”留有余香“。

建議讀完本文之后再讀一讀參考文章,最后是嚴(yán)格地執(zhí)行!如果以時(shí)間不夠?yàn)榻杩诙粓?zhí)行那么看再多的文章也沒有用!

堆屎山?jīng)]有終點(diǎn),持續(xù)更新中......

更新于2023.06.23

十四號(hào)屎山--組件不寫name

危害程度:??

js

復(fù)制代碼//?temp.vue

temp

export?default?{};

</

script>

//?App.vue

importMyComponentfrom"@/components/temp";

exportdefault{

components:?{

MyComponent

},

};

temp組件不寫name,然后導(dǎo)入的時(shí)候隨便設(shè)置一個(gè)其他的名字,在父組件中使用,這樣在vue-devtools中查看的話,組件名為MyComponent,如果只有這樣一個(gè)組件問題不大;想一下所有組件都不設(shè)置name,然后從根組件開始每一層組件都有一個(gè)叫Button的組件,一個(gè)新人接手這個(gè)項(xiàng)目了,他用devtool打開一看全是Button組件,看起來貌似是一樣但是其實(shí)不一樣,而且要搞清楚到底對(duì)應(yīng)的代碼在哪里還很費(fèi)時(shí)間;如果定義了name,那么即使改變注冊(cè)的key,組件名也是固定的,另外推薦組件名與文件名一致,這樣大大地降低了組件搜索成本;

最后

感謝閱讀,歡迎分享給身邊的朋友,

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近寫了太多技術(shù)文章,今天想寫一點(diǎn)簡(jiǎn)單的東西。當(dāng)一個(gè)新人問:如何開始學(xué)前端?很多知乎人都會(huì)發(fā)這樣的腦圖: 新人表示...
    寫代碼的海怪閱讀 1,415評(píng)論 2 7
  • 1路由,其實(shí)就是指向的意思,當(dāng)我點(diǎn)擊頁面上的home按鈕時(shí),頁面中就要顯示home的內(nèi)容,如果點(diǎn)擊頁面上的abou...
    你好陌生人丶閱讀 1,793評(píng)論 0 6
  • JavaScript 作用域和作用域 執(zhí)行上下文 范圍: 一段 或一個(gè)函數(shù) 全局: 變量定義,函數(shù)聲明 一段 函數(shù)...
    羅恬閱讀 411評(píng)論 0 0
  • 前言 回顧2017年,總是希望來盤點(diǎn)一下這一年中前端的發(fā)展。這一年,有一些新技術(shù)和新東西產(chǎn)生,同時(shí),前端技術(shù)也慢慢...
    MapleLeafFall閱讀 393評(píng)論 0 1
  • 前言: vue3項(xiàng)目是在兩年前開始的,正式版3.0于2020年9月發(fā)布;目前vue生態(tài)支持情況還不完善,如vuex...
    eks閱讀 1,723評(píng)論 0 0

友情鏈接更多精彩內(nèi)容