摘要:對團隊而言,如果有統(tǒng)一的規(guī)范命名,交接時可以減少大量的學習和溝通成本。當時一直不知道是什么玩意,后來向那個人打聽才知道,是執(zhí)行登錄的操作,是中文拼音,又是英文,這樣的命名。
1.前言
對于前端開發(fā)而言,肯定會和API打交道,大家也都會想過怎么設計自己的API。優(yōu)秀的 API 之于代碼,就如良好內(nèi)涵對于每個人。好的 API 不但利于使用者理解,開發(fā)時也會事半功倍,后期維護更是順風順水。至于怎么設計API,今天就提下我自己的一些建議。如果大家有什么好的想法,歡迎指點。
2.命名良好的一個命名習慣,就是效率開發(fā)的第一步。如果命名規(guī)范,對自己而言,文件整理有很大的幫助,后期修改文件、可以快速的定位文件,命名規(guī)范,也顯得自己專業(yè)。對團隊而言,如果有統(tǒng)一的規(guī)范命名,交接時可以減少大量的學習和溝通成本。
關(guān)于命名,下面提點幾個小建議
2-1.正確拼寫這個應該說是命名的一個底線了,經(jīng)常性出現(xiàn),單詞拼寫錯誤,搞得自己或者團隊的人都一頭霧水的情況不再少數(shù)。我遇到情況比較深刻的有
中文大意 | 期望 | 實際 |
---|---|---|
表單 | form | from |
報名 | sign-up | sign-in |
采納 | adopt | adept |
內(nèi)容 | content | contend |
測試 | test | text |
聯(lián)系 | contact | contract |
高度 | height | heigth |
寬度 | width | widht |
移動 | mobile | moblie |
標簽 | tab | tap |
這些單詞,如果是拼寫錯誤還好,至少編輯器都會提醒。但是如果寫錯了,但是單詞又是正確的單詞就可大可小了(表單,報名,采納,內(nèi)容這些例子,單詞寫錯了,意思變了,但是單詞是正確的,編輯器都不會提醒)。
試過挖坑比較深的一次就是:一個活動,有報名,有簽到的功能!處理方法如下
獲取已報名用戶信息:getSignUpUserList,
重置報名的數(shù)據(jù):resetSignUpUser,
提交報名操作:signInDo
獲取已簽到用戶信息:getSignInUserList,
重置簽到的表單數(shù)據(jù):resetSignInUser,
提交簽到的操作:signUpDo
修改bug的時候,完全懵逼了,原因大家懂的。
2-2.注意單復數(shù)所有涉及遍歷,操作對象,數(shù)組,集合的函數(shù),建議都采用復數(shù)。
對于展現(xiàn)復數(shù),不同公司有不同的習慣,但是得統(tǒng)一,比如產(chǎn)品列表-productList。這里用了list表示復數(shù),再其它地方,就不建議使用products這種方式表示復數(shù)了2-3.用詞準確
這個主要的兩方面的內(nèi)容
2-3-1.單詞意思搞錯比如彈窗上面的信息,有些時候見到,使用包含notice的字樣,但是實際上,notice的中文意思,準確的應該是‘公告,告示,聲明’之類。
一個彈窗這樣的會話消息,建議使用message這個字樣。notice應該像‘公告,告示,聲明’之類的情況使用。
比如關(guān)閉彈窗的方法,的方法是closeDialog,然后顯示彈窗用的又是showDialog。show的意思是‘顯示’,反義詞應該是hide‘隱藏’。而close意思是關(guān)閉,反義詞應該是open。
附常用反義詞組(有些帶縮寫)
in | out |
on | off |
prev | next |
show | hide |
close | open |
success | fail |
before | after |
begin | end |
這一塊,本來打算放在2-2里面講的,因為命名如果有意義也是一個底線。但是最后放在這里,是因為這個情況在函數(shù)里面出現(xiàn)得不多,更多應該出現(xiàn)在普通變量里面(相信很多人會遇到過這樣的命名:var n1,n2,n3;)。關(guān)于命名,還是建議大家要起有意義名稱,不使用沒意義的命名。
遇到最多的情況,就是圖標的命名方面。
比如下面的圖標(選自某平臺的底部導航欄),點擊不同的圖標出發(fā)不同的方法。
很多人喜歡下面的命名
//版本1 function handle1(){ } function handle2(){ } //版本2 function handleA(){ } function handleB(){ } //版本3 function handleOne(){ } function handleTwo(){ }
這樣的命名,別人函數(shù)了,就算是元素的 class 。這樣的命名在后期維護絕對增加了難度。甚至可能導致重構(gòu)。
建議的姿勢
function handleHome(){ } function handleCollect(){ }2-5.命名格式
文章說的API,主要針對的是函數(shù),但是在這一小塊里面,也列舉一下其它的目標的建議命名方式。
待命名對象 | 推薦名稱 |
---|---|
圖片 | ‘-’ ‘_’ 分割 |
class,id | ‘-’ 分割 |
文件,變量 | 駝峰命名 |
臨時變量 | ‘_’ 開頭,駝峰命名 |
對于中文拼音,應該說只有一種情況,被中國人創(chuàng)造出來,沒有英文翻譯的。
命名 | 含義 |
---|---|
taobao | 淘寶 |
微博 | |
zongzi | 粽子 |
pinyin | 拼音 |
在一年多以前,遇到一個中二的命名-dengluDo。當時一直不知道是什么玩意,后來向那個人打聽才知道,是執(zhí)行登錄的操作,denglu是中文拼音,do又是英文,這樣的命名。后期如果維護,他不哭,算我輸。2-7.命名潛規(guī)則
有些情況,給特定的對象命名,還要用特定的名字,可以說是潛規(guī)則吧。印象最清楚的就是給按鈕命名要么全拼,要么寫btn。很清楚的記得我一個老師說過:寫but,bto的程序也能正常運行,也沒人說你錯,但是我做面試官,就是不錄用你,就說你不專業(yè)。
待命名對象 | 推薦名稱 | 錯誤示范 |
---|---|---|
按鈕 | btn | but bto |
背景 | bg | back background |
模板 | tpl | tem |
提示信息 | msg | mes |
標簽欄 | tab | tit |
網(wǎng)站大圖(廣告宣傳圖) | banner | ban |
注冊 | register | sign-in |
對于函數(shù)而言,參數(shù)是用戶設置最頻繁,也是最關(guān)心的部分,合理設計函數(shù)參數(shù),這一步很重要,直接影響函數(shù)的使用。
3-1.const入?yún)?/b>這個應該說是一個習慣吧,不要直接改變?nèi)雲(yún)⒌闹怠_@個規(guī)則的初衷是解決函數(shù)副作用問題。如果參數(shù)是一個引用類型的數(shù)據(jù),如果在函數(shù)內(nèi)修改了參數(shù),到時候?qū)沟迷镜臄?shù)據(jù)發(fā)生改變,往往會發(fā)生難以追蹤的問題。
3-2.控制參數(shù)數(shù)量參數(shù)的數(shù)量,個人建議就是,超過3個,使用對象進行封裝。因為如果API參數(shù)越多,那么使用對于這個API的記憶成本就越大,易用性也很受影響。
比如下面的例子:
encryptStr: function (str, regArr, type, replacement) { var regtext = "", Reg = null, _type=type||0, replaceText = replacement || "*"; //ecDo.encryptStr("18819322663",[3,5,3],0) //result:188*****663 //repeatStr是在上面定義過的(字符串循環(huán)復制),大家注意哦 if (regArr.length === 3 && type === 0) { regtext = "(w{" + regArr[0] + "})w{" + regArr[1] + "}(w{" + regArr[2] + "})" Reg = new RegExp(regtext); var replaceCount = this.repeatStr(replaceText, regArr[1]); return str.replace(Reg, "$1" + replaceCount + "$2") } //ecDo.encryptStr("asdasdasdaa",[3,5,3],1) //result:***asdas*** else if (regArr.length === 3 && type === 1) { regtext = "w{" + regArr[0] + "}(w{" + regArr[1] + "})w{" + regArr[2] + "}" Reg = new RegExp(regtext); var replaceCount1 = this.repeatStr(replaceText, regArr[0]); var replaceCount2 = this.repeatStr(replaceText, regArr[2]); return str.replace(Reg, replaceCount1 + "$1" + replaceCount2) } //ecDo.encryptStr("1asd88465asdwqe3",[5],0) //result:*****8465asdwqe3 else if (regArr.length === 1 && type === 0) { regtext = "(^w{" + regArr[0] + "})" Reg = new RegExp(regtext); var replaceCount = this.repeatStr(replaceText, regArr[0]); return str.replace(Reg, replaceCount) } //ecDo.encryptStr("1asd88465asdwqe3",[5],1,"+") //result:"1asd88465as+++++" else if (regArr.length === 1 && type === 1) { regtext = "(w{" + regArr[0] + "}$)" Reg = new RegExp(regtext); var replaceCount = this.repeatStr(replaceText, regArr[0]); return str.replace(Reg, replaceCount) } }
大家可以看上面的注釋,就知道這段代碼的具體作用了,如果想想就找個參數(shù),我必須要除了記得4個參數(shù)的作用,還要記得參數(shù)的順序。
如果使用對象記錄參數(shù),用戶只需要記得4個參數(shù)的作用,不需要記參數(shù)的順序。
encryptStr: function (obj) { var _default={ type:0, replacement:"*" }; for(var key in obj){ _default[key]=obj[key]; } }, //調(diào)用方式 ecDo.encryptStr({str:"18819266335",regArr:[5],type:0,replacement:"-"});
這樣還有一個好處就是,比如像剛才的函數(shù),type這個參數(shù),我想保留默認值,偷懶不傳。原來的方案,就得這樣傳。
ecDo.encryptStr("1asd88465asdwqe3",[5],"","+");
這樣肯定是會激起不少有代碼潔癖的開發(fā)者,比如我。如果使用對象,就很好避免了。
ecDo.encryptStr({str:"18819266335",regArr:[5],replacement:"-"});3-3.前置相關(guān)性高的參數(shù)
這個應該沒什么可能,就一個意思:必填重要的參數(shù)前置,可省略的參數(shù)后置。
比如下面的例子
/格式化處理字符串 //ecDo.formatText("1234asda567asd890") //result:"12,34a,sda,567,asd,890" //ecDo.formatText("1234asda567asd890",4," ") //result:"1 234a sda5 67as d890" //ecDo.formatText("1234asda567asd890",4,"-") //result:"1-234a-sda5-67as-d890" formatText: function (str, size, delimiter) { var _size = size || 3, _delimiter = delimiter || ","; var regText = "B(?=(w{" + _size + "})+(?!w))"; var reg = new RegExp(regText, "g"); return str.replace(reg, _delimiter); },
調(diào)用大家都看得出來。如果API這樣設計
formatText: function (size, delimiter, str) { var _size = size || 3, _delimiter = delimiter || ","; var regText = "B(?=(w{" + _size + "})+(?!w))"; var reg = new RegExp(regText, "g"); return str.replace(reg, _delimiter); },
就得這樣調(diào)用,如果這樣寫API,被批斗的可能性很大!
ecDo.formatText("","","1234asda567asd890")4.作用 4-1.支持批量處理
比如這個例子,頁面有這樣的元素
有一個類似jQuery的css這個API的API。
css: function (dom, json) { for (var attr in json) { dom.style[attr] = json[attr]; } }
然后給這些div設置樣式的時候,代碼如下
var oDiv1 =document.querySelectorAll(".div1"); var oDiv2=document.querySelector("#div1"); ecDo.css(oDiv2,{"height":"100px","width":"100px","background":"#333"}); ecDo.css(oDiv1,{"height":"100px","width":"100px","background":"#09f"});
當運行到ecDo.css(oDiv1,{"height":"100px","width":"100px","background":"#09f"});會提示報錯,原因大家也知道。css這個API里面,只處理了單個元素,并沒有處理元素的集合。
建議的方式是把 css 這個API改成可批量處理元素集合的。
css: function (dom, json) { if (dom.length) { for (var i = 0; i < dom.length; i++) { for (var attr in json) { dom[i].style[attr] = json[attr]; } } } else { for (var attr in json) { dom.style[attr] = json[attr]; } } },4-2.多態(tài)處理
一個類似jQuery的html這個API的API-html
之前遇到一個開發(fā)者的處理方式是:獲取元素的innerHTML和設置元素innerHTML分開為兩個方法-getHtml,setHtml。這樣的問題又在于記憶的成本比原生的 innerHTML 還要高。建議的姿勢就是,獲取和設置用同一個API。
html: function (dom) { if (arguments.length === 1) { return dom.innerHTML; } else if (arguments.length === 2) { dom.innerHTML = arguments[1]; } } ecDo.html(oDiv);//獲取 ecDo.html(oDiv,"守候");//設置4-3.可擴展性
可擴展性,就是建議遵守開放-封閉原則。對擴展開放,對修改關(guān)閉。比如jQuery的$.fn和$.fn.extend()。
說一個簡單的例子-計算加薪額度
var addMoney = (function () { //定義策略類 var strategies = { A:function(money){ return money + 2000; }, B:function(money){ return money + 1000; } }; //暴露接口 return { //根據(jù)等級和現(xiàn)工資,輸入加薪后的工資 compute:function(lv,money){ return strategies[lv](money) } }; })(); //比如:等級為A,5000+2000 console.log(addMoney.compute("A",5000))//7000 //比如:等級為B,20000+1000 console.log(addMoney.compute("B",20000))//21000
代碼看著沒有問題,但是如果以后需求要增加C等級呢?這就不得不修改strategies。在里面增加方法。
如下
var strategies = { A:function(money){ return money + 2000; }, B:function(money){ return money + 1000; }, C:function(money){ return money + 500; } };
這樣實現(xiàn)也簡單,如果以后要增加S等級呢?又得改strategies。這里還有一個問題就是,如果增加的C等級只有在A模塊需要用到,在B模塊不會出現(xiàn),那么在B模塊引用addMoney的時候,又會把C等級的計算方式也引入進去,造成不必要的資源浪費。
建議的方式是,設置一個接口,擴展strategies。
var addMoney = (function () { //定義策略類 let strategies = { A:function(money){ return money + 2000; }, B:function(money){ return money + 1000; } }; //暴露接口 return { //根據(jù)等級和現(xiàn)工資,輸入加薪后的工資 compute:function(lv,money){ return strategies[lv](money) }, //擴展等級 addRule:function(lv,fn){ strategies[lv]=fn; } }; })(); //增加C等級的調(diào)用 addMoney.addRule("C",function(money){ return money + 500; }); console.log(addMoney.compute("C",20000))//205004-4.避免副作用
函數(shù)的副作用,相信很多人都會遇到過,比如在一個函數(shù)體內(nèi)修改一個外部作用域的變量,或者全局變量,在函數(shù)體內(nèi)修改引用類型的參數(shù),這些情況多少都會遇到過。
如何避免呢?主要是以下兩個寫代碼習慣。
1.函數(shù)體內(nèi)可以使用參數(shù),進行操作,但是不能修改。如果修改,用一個臨時變量記錄參數(shù)(如果是引用類型,需要用深拷貝記錄)。這樣可以避免直接修改參數(shù)。
2.對于函數(shù)外的變量,如全局變量。函數(shù)體內(nèi)可以訪問,但是不能修改。
3.如果需要給函數(shù)外的變量賦值,不能在函數(shù)體內(nèi)操作,把值返回到外部,在外部進行賦值。(感覺這里有點啰嗦,因為賦值了,就是修改了外部變量,就違反了第二點)。
//不好做法 var myName=""; function setName(firstName,lastName){ myName=firstName+lastName; } setName("守","侯"); //推薦做法 var myName=""; function setName(firstName,lastName){ return firstName+lastName; } myName=setName("守","侯");5.向下兼容
這個建議主要就是為了兼顧以前的寫法。還是拿上面的那個例子吧!
原本傳參方式是這樣
encryptStr: function (str, regArr, type, replacement) {};
后來升級改成這樣
encryptStr: function (obj){}
這樣問題就來了,一個項目里面,因為歷史的原因難免會使用這個API,并且使用了第一種方式傳參。現(xiàn)在API改了,解決的方案有兩個,要么把整個項目使用的這個API的方式,都改成第二種的傳參方式,要么就是對接口進行向下兼容,兼容以前的方案。
encryptStr: function (obj) { var _default={ type:0, replacement:"*" }; //如果還是以之前的方式調(diào)用函數(shù),兼容性判斷 if(arguments.length>1){ _default.str=arguments[0]; _default.regArr=arguments[1]; _default.type=arguments[2]||0; _default.replacement=arguments[3]||"*"; } else{ for(var key in obj){ _default[key]=obj[key]; } } //下面代碼略 },
如果API已經(jīng)準備來一個大版本的更新,(比如從1.0.0升級到2.0.0,不是1.0.0升級到1.0.1,或者1.0.0升級到1.1.0)。不打算兼容以前的版本了。可以忽略這一步,畢竟兼容性的代碼可能也很多。6.簡單
這一步可以說是API設計最高級的一步,也是最難開發(fā)的一步,這就是為什么這篇文章會帶有‘大道至簡’的字樣,即使API的實現(xiàn)很難,但使用起來簡單感覺就是高級的API。這一步也直接影響API的好用與否。簡單的API不但是用起來簡單,試試可以一看就懂的API。這樣的API更易理解、記憶、調(diào)試和變更使用方式。
原生的API,比如Date,some、map、find等所有數(shù)組遍歷操作函數(shù),es6提供的Object.assign,Object.keys,Object.values等。
曾經(jīng)的霸主jQuery,現(xiàn)在的王者react,黑馬vue。這些項目讓人拍手稱贊的原因雖然有很多,但也不可否認的,那便是它們的API設計非常的巧妙。如:jQuery的$,siblings,toogleClass,animate等,react的cloneElement,replaceProps等,vue的nextTick,set等。
jQuery對于現(xiàn)在而言,雖然是過時了,但里面的知識還是值得學習,比如使用的淋漓盡致的 js 寫作技巧,設計模式,以及 API 設計等。
自己寫的API,我也是把API寫得盡量的簡單,最高境界就是讓別人掃一眼文檔,就知道記牢了API的使用方式。這個是我追求的目標,只是現(xiàn)在距離還是有點遠。大家看我encryptStr這個API就知道(此處尷尬一天)。
7.小結(jié)在我的眼里,一個好的API,會有一個一看就懂的名字,一個強大的功能,一個簡單的調(diào)用方式。雖然只有三個條件,但是這三個條件結(jié)合起來,可不是那么容易做到的。一個好的API,無論是對自己,對團隊,對項目開發(fā)都是一個很好的幫助。
對于設計API的一些個人建議,就到這里了,如果以后有更好的想法,會第一時間分享,和大家交流意見。如果大家有什么想法,歡迎指點迷津。
---------------------------華麗的分割線--------------------------------
想了解更多,關(guān)注我的微信公眾號-守候書閣
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/93501.html
摘要:演示效果源碼介紹該源碼是一套簡潔大方的網(wǎng)站源碼,采用目前最流行的框架開發(fā),后臺布局采用。系統(tǒng)架構(gòu)清晰,功能強大,操作簡單,秉承了大道至簡的核心理念。 演示效果: http://www.erdangjiade.com/源碼介紹:該源碼是一套簡潔大方的網(wǎng)站源碼,采用目前最流行的php框架php5.6+Mysql5.6+Apache2.4開發(fā),后臺布局采用BootStrap。系統(tǒng)架構(gòu)清晰,功...
摘要:演示效果源碼介紹該源碼是一套簡潔大方的網(wǎng)站源碼,采用目前最流行的框架開發(fā),后臺布局采用。系統(tǒng)架構(gòu)清晰,功能強大,操作簡單,秉承了大道至簡的核心理念。 演示效果: http://www.erdangjiade.com/源碼介紹:該源碼是一套簡潔大方的網(wǎng)站源碼,采用目前最流行的php框架php5.6+Mysql5.6+Apache2.4開發(fā),后臺布局采用BootStrap。系統(tǒng)架構(gòu)清晰,功...
摘要:演示效果源碼介紹該源碼是一套簡潔大方的網(wǎng)站源碼,采用目前最流行的框架開發(fā),后臺布局采用。系統(tǒng)架構(gòu)清晰,功能強大,操作簡單,秉承了大道至簡的核心理念。 演示效果: http://www.erdangjiade.com/源碼介紹:該源碼是一套簡潔大方的網(wǎng)站源碼,采用目前最流行的php框架php5.6+Mysql5.6+Apache2.4開發(fā),后臺布局采用BootStrap。系統(tǒng)架構(gòu)清晰,功...
閱讀 2796·2023-04-26 01:47
閱讀 3602·2023-04-25 23:45
閱讀 2483·2021-10-13 09:39
閱讀 619·2021-10-09 09:44
閱讀 1811·2021-09-22 15:59
閱讀 2789·2021-09-13 10:33
閱讀 1735·2021-09-03 10:30
閱讀 668·2019-08-30 15:53