1.前言

对于前端开发而言,肯定会和API打交道,大家也都会想过怎么设计自己的API。优秀的 API 之于代码,就如良好内涵对于每个人。好的 API 不但利于使用者理解,开发时也会事半功倍,后期维护更是顺风顺水。至于怎么设计API,今天就提下我自己的一些建议。如果大家有什么好的想法,欢迎指点。

2.命名

良好的一个命名习惯,就是效率开发的第一步。如果命名规范,对自己而言,文件整理有很大的帮助,后期修改文件、可以快速的定位文件,命名规范,也显得自己专业。对团队而言,如果有统一的规范命名,交接时可以减少大量的学习和沟通成本。

关于命名,下面提点几个小建议

2-1.正确拼写

这个应该说是命名的一个底线了,经常性出现,单词拼写错误,搞得自己或者团队的人都一头雾水的情况不再少数。我遇到情况比较深刻的有

中文大意 期望 实际
表单 form from
报名 sign-up sign-in
采纳 adopt adept
内容 content contend
测试 test text
联系 contact contract
高度 height heigth
宽度 width widht
移动 mobile moblie
标签 tab tap

这些单词,如果是拼写错误还好,至少编辑器都会提醒。但是如果写错了,但是单词又是正确的单词就可大可小了(表单,报名,采纳,内容这些例子,单词写错了,意思变了,但是单词是正确的,编辑器都不会提醒)。

试过挖坑比较深的一次就是:一个活动,有报名,有签到的功能!处理方法如下

获取已报名用户信息:getSignUpUserList,
重置报名的数据:resetSignUpUser,
提交报名操作:signInDo

获取已签到用户信息:getSignInUserList,
重置签到的表单数据:resetSignInUser,
提交签到的操作:signUpDo

修改bug的时候,完全懵逼了,原因大家懂的。

2-2.注意单复数

所有涉及遍历,操作对象,数组,集合的函数,建议都采用复数。

对于展现复数,不同公司有不同的习惯,但是得统一,比如产品列表-productList。这里用了list表示复数,再其它地方,就不建议使用products这种方式表示复数了

2-3.用词准确

这个主要的两方面的内容

2-3-1.单词意思搞错

比如弹窗上面的信息,有些时候见到,使用包含notice的字样,但是实际上,notice的中文意思,准确的应该是‘公告,告示,声明’之类。
一个弹窗这样的会话消息,建议使用message这个字样。notice应该像‘公告,告示,声明’之类的情况使用。

2-3-2.正反词义单词错用

比如关闭弹窗的方法,的方法是closeDialog,然后显示弹窗用的又是showDialogshow的意思是‘显示’,反义词应该是hide‘隐藏’。而close意思是关闭,反义词应该是open

附常用反义词组(有些带缩写)

in out
on off
prev next
show hide
close open
success fail
before after
begin end

2-4.命名意义

这一块,本来打算放在2-2里面讲的,因为命名如果有意义也是一个底线。但是最后放在这里,是因为这个情况在函数里面出现得不多,更多应该出现在普通变量里面(相信很多人会遇到过这样的命名:var n1,n2,n3;)。关于命名,还是建议大家要起有意义名称,不使用没意义的命名。

遇到最多的情况,就是图标的命名方面。

比如下面的图标(选自某平台的底部导航栏),点击不同的图标出发不同的方法。

很多人喜欢下面的命名

//版本1
function handle1(){ }
function handle2(){ }
//版本2
function handleA(){ }
function handleB(){ }
//版本3
function handleOne(){ }
function handleTwo(){ }

这样的命名,别人函数了,就算是元素的 class 。这样的命名在后期维护绝对增加了难度。甚至可能导致重构。

建议的姿势

function handleHome(){

}
function handleCollect(){ }

2-5.命名格式

文章说的API,主要针对的是函数,但是在这一小块里面,也列举一下其它的目标的建议命名方式。

待命名对象 推荐名称
图片 ‘-’ ‘_’ 分割
class,id ‘-’ 分割
文件,变量 驼峰命名
临时变量 ‘_’ 开头,驼峰命名

2-6.处理中文拼音

对于中文拼音,应该说只有一种情况,被中国人创造出来,没有英文翻译的。

命名 含义
taobao 淘宝
weibo 微博
zongzi 粽子
pinyin 拼音

在一年多以前,遇到一个中二的命名-dengluDo。当时一直不知道是什么玩意,后来向那个人打听才知道,是执行登录的操作,denglu是中文拼音,do又是英文,这样的命名。后期如果维护,他不哭,算我输。

2-7.命名潜规则

有些情况,给特定的对象命名,还要用特定的名字,可以说是潜规则吧。印象最清楚的就是给按钮命名要么全拼,要么写btn。很清楚的记得我一个老师说过:写but,bto的程序也能正常运行,也没人说你错,但是我做面试官,就是不录用你,就说你不专业。

待命名对象 推荐名称 错误示范
按钮 btn but bto
背景 bg back background
模板 tpl tem
提示信息 msg mes
标签栏 tab tit
网站大图(广告宣传图) banner ban
注册 register sign-in

3.参数

对于函数而言,参数是用户设置最频繁,也是最关心的部分,合理设计函数参数,这一步很重要,直接影响函数的使用。

3-1.const入参

这个应该说是一个习惯吧,不要直接改变入参的值。这个规则的初衷是解决函数副作用问题。如果参数是一个引用类型的数据,如果在函数内修改了参数,到时候将会使得原本的数据发生改变,往往会发生难以追踪的问题。

3-2.控制参数数量

参数的数量,个人建议就是,超过3个,使用对象进行封装。因为如果API参数越多,那么使用对于这个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是在上面定义过的(字符串循环复制),大家注意哦
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)
}
}

大家可以看上面的注释,就知道这段代码的具体作用了,如果想想就找个参数,我必须要除了记得4个参数的作用,还要记得参数的顺序。

如果使用对象记录参数,用户只需要记得4个参数的作用,不需要记参数的顺序。

encryptStr: function (obj) {
var _default={
type:0,
replacement:'*'
};
for(var key in obj){
_default[key]=obj[key];
}
}, //调用方式
ecDo.encryptStr({str:'18819266335',regArr:[5],type:0,replacement:'-'});

这样还有一个好处就是,比如像刚才的函数,type这个参数,我想保留默认值,偷懒不传。原来的方案,就得这样传。

ecDo.encryptStr('1asd88465asdwqe3',[5],'','+');

这样肯定是会激起不少有代码洁癖的开发者,比如我。如果使用对象,就很好避免了。

ecDo.encryptStr({str:'18819266335',regArr:[5],replacement:'-'});

3-3.前置相关性高的参数

这个应该没什么可能,就一个意思:必填重要的参数前置,可省略的参数后置。

比如下面的例子

/格式化处理字符串
//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);
},

调用大家都看得出来。如果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);
},

就得这样调用,如果这样写API,被批斗的可能性很大!

ecDo.formatText('','','1234asda567asd890')

4.作用

4-1.支持批量处理

比如这个例子,页面有这样的元素

<div class="div1"></div>
<div class="div1"></div>
<div id="div2"></div>

有一个类似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.多态处理

一个类似jQuery的html这个API的API-html

之前遇到一个开发者的处理方式是:获取元素的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.可扩展性

可扩展性,就是建议遵守开放-封闭原则。对扩展开放,对修改关闭。比如jQuery的$.fn和$.fn.extend()。

说一个简单的例子-计算加薪额度

var addMoney = (function () {
//定义策略类
var strategies = {
A:function(money){
return money + 2000;
},
B:function(money){
return money + 1000;
}
};
//暴露接口
return {
//根据等级和现工资,输入加薪后的工资
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;
}
};

这样实现也简单,如果以后要增加S等级呢?又得改strategies。这里还有一个问题就是,如果增加的C等级只有在A模块需要用到,在B模块不会出现,那么在B模块引用addMoney的时候,又会把C等级的计算方式也引入进去,造成不必要的资源浪费。
建议的方式是,设置一个接口,扩展strategies。

var addMoney = (function () {
//定义策略类
let strategies = {
A:function(money){
return money + 2000;
},
B:function(money){
return money + 1000;
}
};
//暴露接口
return {
//根据等级和现工资,输入加薪后的工资
compute:function(lv,money){
return strategies[lv](money)
},
//扩展等级
addRule:function(lv,fn){
strategies[lv]=fn;
}
};
})();
//增加C等级的调用
addMoney.addRule('C',function(money){
return money + 500;
});
console.log(addMoney.compute('C',20000))//20500

4-4.避免副作用

函数的副作用,相信很多人都会遇到过,比如在一个函数体内修改一个外部作用域的变量,或者全局变量,在函数体内修改引用类型的参数,这些情况多少都会遇到过。

如何避免呢?主要是以下两个写代码习惯。

1.函数体内可以使用参数,进行操作,但是不能修改。如果修改,用一个临时变量记录参数(如果是引用类型,需要用深拷贝记录)。这样可以避免直接修改参数。

2.对于函数外的变量,如全局变量。函数体内可以访问,但是不能修改。

3.如果需要给函数外的变量赋值,不能在函数体内操作,把值返回到外部,在外部进行赋值。(感觉这里有点啰嗦,因为赋值了,就是修改了外部变量,就违反了第二点)。

//不好做法
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,并且使用了第一种方式传参。现在API改了,解决的方案有两个,要么把整个项目使用的这个API的方式,都改成第二种的传参方式,要么就是对接口进行向下兼容,兼容以前的方案。

encryptStr: function (obj) {
var _default={
type:0,
replacement:'*'
};
//如果还是以之前的方式调用函数,兼容性判断
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已经准备来一个大版本的更新,(比如从1.0.0升级到2.0.0,不是1.0.0升级到1.0.1,或者1.0.0升级到1.1.0)。不打算兼容以前的版本了。可以忽略这一步,毕竟兼容性的代码可能也很多。

6.简单

这一步可以说是API设计最高级的一步,也是最难开发的一步,这就是为什么这篇文章会带有‘大道至简’的字样,即使API的实现很难,但使用起来简单感觉就是高级的API。这一步也直接影响API的好用与否。简单的API不但是用起来简单,试试可以一看就懂的API。这样的API更易理解、记忆、调试和变更使用方式。

原生的API,比如Date,some、map、find等所有数组遍历操作函数,es6提供的Object.assign,Object.keys,Object.values等。

曾经的霸主jQuery,现在的王者react,黑马vue。这些项目让人拍手称赞的原因虽然有很多,但也不可否认的,那便是它们的API设计非常的巧妙。如:jQuery的$,siblings,toogleClass,animate等,react的cloneElement,replaceProps等,vue的nextTick,set等。

jQuery对于现在而言,虽然是过时了,但里面的知识还是值得学习,比如使用的淋漓尽致的 js 写作技巧,设计模式,以及 API 设计等。

自己写的API,我也是把API写得尽量的简单,最高境界就是让别人扫一眼文档,就知道记牢了API的使用方式。这个是我追求的目标,只是现在距离还是有点远。大家看我encryptStr这个API就知道(此处尴尬一天)。

7.小结

在我的眼里,一个好的API,会有一个一看就懂的名字,一个强大的功能,一个简单的调用方式。

API设计和命名的更多相关文章

  1. 从英文变形规则计算到Restful Api设计

    ➠更多技术干货请戳:听云博客 一天在研究Restful API设计,命名的时候我总是很纠结,我相信大多数人也有这种感觉,不是说想不出来某个单词怎么写的问题,像我这种没事背单词背到13000词量的人也要 ...

  2. javascript的api设计原则

    前言 本篇博文来自一次公司内部的前端分享,从多个方面讨论了在设计接口时遵循的原则,总共包含了七个大块.系卤煮自己总结的一些经验和教训.本篇博文同时也参考了其他一些文章,相关地址会在后面贴出来.很难做到 ...

  3. RESTful API 设计最佳实践

    背景 目前互联网上充斥着大量的关于RESTful API(为了方便,以后API和RESTful API 一个意思)如何设计的文章,然而却没有一个"万能"的设计标准:如何鉴权?API ...

  4. 我所理解的RESTful Web API [设计篇]

    <我所理解的RESTful Web API [Web标准篇]>Web服务已经成为了异质系统之间的互联与集成的主要手段,在过去一段不短的时间里,Web服务几乎清一水地采用SOAP来构建.构建 ...

  5. (转)Java API设计清单

    转自: 伯乐在线 Java API设计清单 英文原文 TheAmiableAPI 在设计Java API的时候总是有很多不同的规范和考量.与任何复杂的事物一样,这项工作往往就是在考验我们思考的缜密程度 ...

  6. effective OC2.0 52阅读笔记(三 接口与API设计)

    第三章:接口与API设计 15 用前缀避免命名空间冲突 总结:避免重名符号错误的唯一办法是变相实现命名空间.为所有符号都加上命名前缀.类和分类都应加三字前缀.注意类实现文件中的纯C函数及全局变量,是算 ...

  7. 来自HeroKu的HTTP API 设计指南(中文版)

    原文转自:http://get.jobdeer.com/343.get 来自HeroKu的HTTP API 设计指南(中文版) 翻译 by @Easy 简介 本指南中文翻译者为 @Easy ,他是国内 ...

  8. atitit.api设计 方法 指南 手册 v2 q929.docx

    atitit.api设计 方法 指南 手册 v2 q929.docx atitit.api设计原则与方法 1. 归一化(锤子钉子理论)1 1.1. 链式方法2 1.2. 规则5:建立返回值类型2 1. ...

  9. JavaScript API 设计原则

    网+线下沙龙 | 移动APP模式创新:给你一个做APP的理由>> 好的 API 设计:在自描述的同时,达到抽象的目标. 设计良好的 API ,开发者可以快速上手,没必要经常抱着手册和文档, ...

随机推荐

  1. Codeforces Round #271 (Div. 2) E. Pillars 线段树优化dp

    E. Pillars time limit per test 1 second memory limit per test 256 megabytes input standard input out ...

  2. 软件测试中Bug的生命周期以及Bug的严重等级

    软件测试中Bug的生命周期以及Bug的严重等级 我猜你们都会,但能说专业且全面不? 1.首先当测试人员接到一个项目或产品准备测试的时候,测试人员会根据测试用例一步步的来执行用例进行简单的功能测试.当测 ...

  3. Python -- jpype JVM的第三方库使用

    Python -- jpype 安装 jpype 如图:直接执行 命令:pip install jpype 可见失败,提示没有版本信息 可以使用 pip  search jpype 查看相关版本信息 ...

  4. pdf can't copy text 无法复制文字

    有些 pdf 是通过图片弄出来的,或者被 protect 了. 我们会无法 copy 里面的字. 这个时候可以用 OCR (Optical character recognition) 就是从图片中识 ...

  5. (转)c# control.Invoke control.BeginInvoke

    在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...

  6. English Voice of <<See You Again >>

    <See You Again >(<当我们再相见>) 演唱:Wiz Khalifa/Charlie Puth  维兹·卡利法/查理·普斯 It's been a long da ...

  7. 一款好用的取色工具TakeColor

    简介:TakeColor,一款还算好用的取色软件,一个很小很简洁的exe文件,无需安装 使用:打开exe文件后,使用“Alt + C” 组合键即可在鼠标悬停的位置上获取到颜色值,可以获取HTML.RG ...

  8. 11月29日 The Rails philosophy 完成rails on guide 的第一章getting started with rails

    the rails philosophy includes two major guiding principles: Don't repeat yourself: DRY is a principl ...

  9. java开学第一周测试自我感想

    开学第一周,王建民老师就对我们进行了java测试,对我们说测试题目是基于期末考试的基础难度来出的.我们的考试完全是靠暑假在家自学的基础,如果在家没有自学java,那完全就是看不懂试卷到底要考什么.由于 ...

  10. MyEclipse6.5的反编译插件的安装

    常用的几种反编译工具 1. JD-GUI[推荐] JD-GUI是属于Java Decompiler项目(JD项目)下个的图形化运行方式的反编译器.JD-Eclipse属于Java Decompiler ...