计算机教程

当前位置:3522.com > 计算机教程 > JavaScript API 设计原则3522.com

JavaScript API 设计原则3522.com

来源:http://www.4sports-uk.com 作者:3522.com 时间:2019-06-29 18:10

引言:如今的JavaScript已经是Web上最流行的语言,没有之一。从Github上的语言排行榜https://github.com/languages上即可看出,也是如今最为活跃的开源社区。随着Node的加入,JavaScript开枝散叶进入服务器领域,为这个语言榜的占比,也贡献了几分热度。尽管经历了Web2.0的洗礼 ,但在国内谈及开源,开源人士似乎都当这门语言并不存在,这也意味着国内的开发中坚阶层,并没有改变JavaScript以及前端过去二流形象的认识,也没意识到JavaScript如今真正的价值。历史原因造就如今的局面,但是你并不能就此否认,一个出身不好,小时候还挺调皮的孩子,他长大后就是没有出息。本文将介绍一个优秀的JavaScript模块应该是怎样炼成的,以期望未来国内的开源社区能够涌现出更多的优秀模块。

作者:Alon
原文地址:
http://jinlong.github.io/2015/08/31/secrets-of-awesome-javascript-api-design/

相关厂商内容

天翼空间借力Windowsphone 8,帮广大开发者赚钱!

人人都可以开发的云平台应用,阿里云开发者大赛百万大奖等你拿!

QCon杭州2012:Pragmatic Programmer作者、敏捷宣言创始人之一Dave Thomas

RightScale创始人、云计算专家Jonathan Siegel确认参加QCon杭州2012

Amazon五星敏捷图书作者、敏捷教练Amr Elssamadisy确认参加QCon杭州2012

前段时间组织优化我们的原生模块 API(iOS、Android 模块封装成 JavaScript 接口),于是学习了几篇 JavaScript API 设计的文章,尽管是旧文,但受益匪浅,这里记录一下。

相关赞助商

3522.com 1

QCon杭州2012大会10月25~27,现在报名享8折优惠,团购更多折扣!

 

 

 

 

引起我写这篇文章冲动的是近期接触到moment模块时的震惊。用JavaScript写程序,通常要经历两个阶段:第一个阶段是采用一些模块来擦除JavaScript语言(跨平台)自身的问题;第二个阶段是自己写一些方法来擦除引入的模块的问题。第三个阶段才会真正进入业务开发中,前两个阶段俗称“擦屁股”。时常在第三个阶段时,我会陷回到第二个阶段,和各种模块碰撞,往往会引起一些心情上的不舒爽。而在接触到moment时,这些烦恼瞬间消散,我在心底知道为何会如此释怀,如碰到心仪的女神。这也让我回头反思过去在使用和写模块过程中遇到的挫折和收获,这落差让我产生了如此的冲动,此文算是一个总结,期望能在汲取优秀模块的经验中,国内开源社区能够成长起来。

好的 API 设计:在自描述的同时,达到抽象的目标。

为什么是模块,而不是库或者框架

在过去几年做前端的同学多半谈论的是库,或是框架,提及库则是Prototypejs和jQuery,框架则是YUI3,Dojo、Extjs等。关于库或者框架的,对应的比喻则是大教堂和集市。在最早的时间,大教堂和集市的建设都在同步行进,具有代表意义的是YUI3和jQuery,前者Yahoo!投入许多精力,历时两代完成,后者则是集市建造的典范,由John Resig创建,社区参与。最后看看后续的反馈:

设计良好的 API ,开发者可以快速上手,没必要经常抱着手册和文档,也没必要频繁光顾技术支持社区。

大教堂未必优质

大教堂模式产出的作品,让享用者可以有一步到位的服务,无需其他。但是由于大教堂的建设过程中承担着解决所有问题的使命,这导致它逐渐变得庞大,尽管它有着宏大而美妙的结构,像一首长长的赞美诗,任何一段都可以被唱诗班演唱得动听。但是,弱点依旧暴露出来:

  1. 局部的优良程度未必是最好的;
  2. 庞大的结构导致灵活度下降,升级困难。
  3. 任何一条龙服务,都不能保证每个环节都是优异的。

流畅的接口

方法链:流畅易读,更易理解。

//常见的 API 调用方式:改变一些颜色,添加事件监听
var elem = document.getElementById("foobar");
elem.style.background = "red";
elem.style.color = "green";
elem.addEventListener('click', function(event) {
  alert("hello world!");
}, true);

//(设想的)方法链 API
DOMHelper.getElementById('foobar')
  .setStyle("background", "red")
  .setStyle("color", "green")
  .addEvent("click", function(event) {
    alert("hello world");
  });

设置和获取操作,可以合二为一;方法越多,文档可能越难写。

var $elem = jQuery("#foobar");

//setter
$elem.setCss("background", "green");
//getter
$elem.getCss("color") === "red";

//getter, setter 合二为一
$elem.css("background", "green");
$elem.css("color") === "red";

小集市钻石闪烁,沙砾良多

小集市的特点是开放式建设、周期短、成本低。大多数创建出来的集市是功能简陋、品质平庸的。jQuery是一个值得玩味的现象,品质极高,但是它带来的插件市场,却体现了小集市的另一面,大多数的jQuery插件的质量却十分低下。

可以说小集市模式创建出来的作品,在单点上,是能够超越教堂模式作品中对应的那部分。前者缺乏一个宏伟的结构,但是在微观上,它是完善的,可以随意挪移的。后者虽然具备优良的结构,但在单点上并非优秀,这也容易让人怀疑是否其他点上也并不优秀,而且教堂式作品的一个特点则是,局部是不可移植的,非独立性的,这在YUI3中表现十分明显。

上述对比很容易让人联想到,如果一个架构的特性具备单点可移植,可拆卸,自身极其轻量,汲取了大教堂和小集市的优点。那必将是一个全新的时代,那是一个可以DIY的时代。原有的大教堂模式由于缺失了灵活性,在迭代迅速的开发中,必将淘汰。而小集市尽管良莠不齐,好在预留了选择权利给用户,并且他的开放性也预示着它的可成长性,所以还有未来可期。

没错,如今这个时代已经到来。库通常是一个比框架小一个粒度的单元,模块则是比库更小一个粒度的单元。一个库可能由几个模块组成,框架则可能是在几个库的基础上构建。不再谈论库和框架的原因是将库和框架的架构部分还给开发者,以便开发者可以根据实际业务去构建合适的解决方案。任何框架或库都只具备解决某一方面的能力,不具备普适性。当粒度降低到模块级别的时候,构建任何上层业务,可以实现按需使用。由于模块的粒度更小,所以相对库而言,更加专注,变优秀的成本更低。类比孩子在充满沙砾的海边奔跑,但很容易装满一口袋的贝壳。对于稍具慧眼的开发者而言,挑选一堆适合的模块来解决业务的问题,比使用教堂式作品或者更高效。

这种方案因为CommonJS模块规范的影响,已经在既定事实中成型。在Node中,NPM平台(https://npmjs.org/)上13000 的模块数量可以说明这个问题。前端中由于受到历史原因的影响,进展较慢,国内玉伯的SeaJS在推动此事,Arale是在SeaJS基础上创建了一些模块,这类模块可以非常容易迁移到其他符合CommonJS模块规范的环境中。除此之外腾讯的JX也具备SeaJS的特性,可以轻松将别的库应用到JX中,但是还不够规范。

如果非得给这种模块提供方式一个名称,我觉得该叫做小教堂模式,具备小集市的小,意味着这个教堂可能只提供祈祷服务,如果注册结婚,则需要换另一家小教堂。但是这类小教堂的服务是最优质的。创建这类小教堂只比创建一个优异的小集市略费成本,换言之,优异的小集市就是小教堂,它的创建方式是沿袭小集市的开放、透明的、有既定目标的成长性的。

另一个考虑是,在大部分的情况下,我们是不需要大教堂的,现实中为了整体环境,我们宁愿在大教堂中祈祷,但是程序设计中,我们不会因为喜欢一棵树木,而买下整块山头,我们几乎不喜欢任何看起来多余的部分。同时我们也不需要小集市,我们需要的是一个品质优良的商场。开发者是采购者,采购模块的过程既是架构的设计的过程。

前文我也提到这种小教堂的模式依然存在不理想的功能重叠、功能多余、功能缺陷、功能冲撞等问题。可谓说完美的小教堂是可遇不可求的,但一旦它完美,将再难被替换。目前阶段的JavaScript模块开发还存在着这些问题。是故,如果开发者了解如何去打造一个优秀的JavaScript模块,并乐于贡献到开源社区,这将大幅提升社区JavaScript水平,后续的开发者在做业务架构时,将具备更优质的材料来DIY更优异的产品。

一致性

相关的接口保持一致的风格,一整套 API 如果传递一种熟悉和舒适的感觉,会大大减轻开发者对新工具的适应性。

命名这点事:既要短,又要自描述,最重要的是保持一致性。

“There are only two hard problems in computer science: cache-invalidation and naming things.”“在计算机科学界只有两件头疼的事:缓存失效和命名问题”— Phil Karlton

选择一个你喜欢的措辞,然后持续使用。选择一种风格,然后保持这种风格。

炼成优秀模块的最佳实践

其实这并不算是精确的最佳实践,只是从别的模块哪里学习到的和自己过去的一些经验,仅做一定的总结。欢迎补充和讨论。

处理参数

需要考虑大家如何使用你提供的方法,是否会重复调用?为何会重复调用?

你的 API 如何帮助开发者减少重复的调用?接收map映射参数,回调或者序列化的属性名,不仅让你的 API 更干净,而且使用起来更舒服、高效。

jQuery 的 css()方法可以给 DOM 元素设置样式:

jQuery("#some-selector")
  .css("background", "red")
  .css("color", "white")
  .css("font-weight", "bold")
  .css("padding", 10);

这个方法可以接受一个 JSON 对象:

jQuery("#some-selector").css({
  "background" : "red",
  "color" : "white",
  "font-weight" : "bold",
  "padding" : 10
});

//通过传一个 map 映射绑定事件
jQuery("#some-selector").on({
  "click" : myClickHandler,
  "keyup" : myKeyupHandler,
  "change" : myChangeHandler
});

//为多个事件绑定同一个处理函数
jQuery("#some-selector").on("click keyup change", myEventHandler);

模块自身的素质要求

要写出一个优秀的模块,模块自身的素质十分重要,如果自身条件太差,成为优秀模块的概率是极小的。这些自身素质包括美好的愿景、专注的定位、名字、API设计、文档、测试等。

处理类型

定义方法的时候,需要决定它可以接收什么样的参数。我们不清楚人们如何使用我们的代码,但可以更有远见,考虑支持哪些参数类型。

//原来的代码
DateInterval.prototype.days = function(start, end) {
  return Math.floor((end - start) / 86400000);
};

//修改后的代码
DateInterval.prototype.days = function(start, end) {
  if (!(start instanceof Date)) {
    start = new Date(start);
  }
  if (!(end instanceof Date)) {
    end = new Date(end);
  }

  return Math.floor((end.getTime() - start.getTime()) / 86400000);
};

加了短短的6行代码,我们的方法强大到可以接收Date对象,数字的时间戳,甚至像Sat Sep 08 2012 15:34:35 GMT 0200 (CEST)这样的字符串。

如果你需要确保传入的参数类型(字符串,数字,布尔),可以这样转换:

function castaway(some_string, some_integer, some_boolean) {
  some_string  = "";
  some_integer  = 0; // parseInt(some_integer, 10) 更安全些
  some_boolean = !!some_boolean;
}

合理的愿景:设定既定目标

如果你打算写作一个模块,并贡献到开源社区中,并期望它是优异的,并被许多人使用的,那么为它设定一个既定目标是首要的。如果这个目标是没有意义,没有趣味的,那多半没有人使用,甚至自己开发到一半都没有兴趣继续写下去。没有理想的屌丝,注定不能成为高富帅。

对于具备众多坑爹问题的JavaScript语言而言,找到一个目标并非难事。典型的例子如:jQuery专注解决DOM操作和Ajax、Underscore专注对象和集合的操作、QUnit和Jasmine专注BDD和TDD的单元测试、moment模块专注解决从Java那里学过来的Date的问题;拿近一些的例子,玉伯的SeaJS专注模块加载,老赵的Wind.js专注异步编程同步化来解决流程控制问题;拿一个有趣的例子,PNGDrivehttps://github.com/MadeInHaus/PNGDrive这个项目虽然没有什么实际用处,但是将文件编码为图片显示出来的方式足够有趣。

另外这个目标必须是既定的。也就意味着饼不用画为无限的,这个目标一定是可以完成的。如果目标太大,也就意味着模块自身会变复杂。jQuery兼容各种浏览器的DOM操作这个目标,在移动平台上变得没有意义,所以存在着Zepto.js这样的项目。更小的目标意味着模块自身简洁,且能够更专注,目标更容易抵达。一旦抵达目标,该模块就是稳定的,未来被替换的机会极小。

处理 undefined

为了使你的 API 更健壮,需要鉴别是否真正的undefined值被传递进来,可以检查arguments对象:

function testUndefined(expecting, someArgument) {
  if (someArgument === undefined) {
    console.log("someArgument 是 undefined");
  }
  if (arguments.length > 1) {
    console.log("然而它实际是传进来的");
  }
}

testUndefined("foo");
// 结果: someArgument 是 undefined
testUndefined("foo", undefined);
// 结果:  someArgument 是 undefined , 然而它实际是传进来的

为模块或项目起一个贴切的名字

模块需要一个贴切而好记的名字。这个名字何以帮助用户最直观地感受模块。SeaJS在起名上算是一个表率,让人很容易有海纳百川的联想,这也正是SeaJS的行为。一个好的名字可以使得模块的后期推广事半功倍,而且一旦开始推广,尽量不要换名字。

给参数命名

event.initMouseEvent(
  "click", true, true, window,
  123, 101, 202, 101, 202,
  true, false, false, false,
  1, null);

Event.initMouseEvent 这个方法简直丧心病狂,不看文档的话,谁能说出每个参数是什么意思?

给每个参数起个名字,赋个默认值,可好:

event.initMouseEvent(
  type="click",
  canBubble=true,
  cancelable=true,
  view=window,
  detail=123,
  screenX=101,
  screenY=202,
  clientX=101,
  clientY=202,
  ctrlKey=true,
  altKey=false,
  shiftKey=false,
  metaKey=false,
  button=1,
  relatedTarget=null);

ES6, 或者 Harmony 就有 默认参数值rest 参数 了。

不做逾越的事

并非每个使用者都喜欢买一送一的感觉,因为后面这个一,对于使用者而言,并非是期望的,所以它的优良无法直观的判定好坏。无关的方法一定不要提供。评判一个模块是否完美,不是可以添加API,而是无法再减少API了。

参数接收 JSON 对象

与其接收一堆参数,不如接收一个 JSON 对象:

function nightmare(accepts, async, beforeSend, cache, complete, /* 等28个参数 */) {
  if (accepts === "text") {
    // 准备接收纯文本
  }
}

function dream(options) {
  options = options || {};
  if (options.accepts === "text") {
    // 准备接收纯文本
  }
}

调用起来也更简单了:

nightmare("text", true, undefined, false, undefined, /* 等28个参数 */);

dream({
  accepts: "text",
  async: true,
  cache: false
});

不污染公共环境

每个人都不喜欢公共环境被人污染。破窗效应揭示,如果一辆汽车的门窗稍有损坏,不立即修复,那么很快整辆车就会被破坏甚至偷走。 在JavaScript,公共环境包括全局变量,原型链等。jQuery和Underscore为了代码的写作方便,占用了$和_两个符号,尽管他们都提供了noConflict方法来避免冲突,但是抱怨者还是大有人在。所幸这两个库太过于知名,几乎没有再有的库来使用这两个变量名。另一个例子是Prototype.js库对对象进行扩展时,直接在Array、Object等原生对象的原型链上添加方法,尽管它看起来不影响,但是总有冲突的一天,并且使用者并不一定知晓原型链被改动,在他的默认上下文中,难保他也不去修改原型链。相比Prototype.js的做法,Underscore提供的方法则优雅许多,另行提供API来处理操作,而不是修改共有的原型链。

在Node和浏览器中,global和window是全局对象,如果随意放置变量到全局对象上,也容易遭到他人修改或者覆盖你变量的事情。
CommonJS提供的require、exports则十分优雅解决这个问题。谁使用,谁引入。而不是通过全局变量。在前端没有AMD或者CommonJS环境下,则是采用命名空间和闭包来解决这个问题。

不污染公共环境是模块与模块之间互相不影响的基本保证。如果引入你的模块,导致其他模块失效的事情,多半是不招人待见的。

参数默认值

参数最好有默认值,通过 [jQuery.extend()](http://api.jquery.com/jQuery.extend/ , _.extend()) http://underscorejs.org/#extend) 和 Protoype 的 Object.extend ,可以覆盖预设的默认值。

var default_options = {
  accepts: "text",
  async: true,
  beforeSend: null,
  cache: false,
  complete: null,
  // …
};

function dream(options) {
  var o = jQuery.extend({}, default_options, options || {});
  console.log(o.accepts);
}

dream({ async: false });
// prints: "text"

抵制墨菲效应

有可能变糟糕的事情,它变糟糕的可能性就会变大。模块在升级,迭代的过程中,如何避免这种糟糕的事情发生呢?答案是单元测试。当单元测试覆盖了你认为会出问题的地方,可以避免相同的错误再次发生。这是模块稳定迭代的基本保证。当我发现一个仅仅为了解决日期操作的moment模块,它为数不多的API竟然具有多达7000 的断言时,十分惊讶。

过去JavaScript只做简单的事情,地位低下,所以对于JavaScript的质量保证也极少。这个思维需要改变,一个用户在评估采用你的模块时,如果单元测试都无法看到,心里该是有多不踏实。

除此之外,还有性能测试,性能测试结果可以横向比较,也可以纵向比较,有利于感知模块的具体性能表现。

数据通常容易打动理性的人。

扩展性

回调(callbacks)

通过回调, API 用户可以覆盖你的某一部分代码。把一些需要自定义的功能开放成可配置的回调函数,允许 API 用户轻松覆盖你的默认代码。

API 接口一旦接收回调,确保在文档中加以说明,并提供代码示例。

事件(events)

事件接口最好见名知意,可以自由选择事件名字,避免与原生事件重名。

保持简洁

对于任意看起来复杂的事物,用户均会觉得它很复杂。老赵的Wind.js(前身是Jscex)是一个想法独特的模块,但在提供的API上由于eval,以及需要引入的模块较多,让它看起来比较复杂,让用户感觉潜意识里复杂,这种复杂的信号很容易变成它有问题的信号,让人心生疏远。

将能不暴露给用户看到的东西,尽量隐藏,过多的步骤只会让用户觉得麻烦和不靠谱。

处理错误

不是所有的错误都对开发者调试代码有用:

// jQuery 允许这么写
$(document.body).on('click', {});

// 点击时报错
//   TypeError: ((p.event.special[l.origType] || {}).handle || l.handler).apply is not a function
//   in jQuery.min.js on Line 3

这样的错误调试起来很痛苦,不要浪费开发者的时间,直接告诉他们犯了什么错:

if (Object.prototype.toString.call(callback) !== '[object Function]') { // 看备注
  throw new TypeError("callback is not a function!");
}

备注:typeof callback === "function"在老的浏览器上会有问题,object会当成个function

职责单一

这里的反面例子来自于Require.js。如果你用过RequireJS,可以看到require方法是一个变态的设置,参数为’a’、[‘a’]、[‘a.js’]、{}他们的行为都是不一致的。这种参数类型可以随意变化是JavaScript的灵活的地方,但是函数的行为如果也发生变化,这会让人产生迷惑。适当的重载并不意味这行为也要完全不同。

功能过多,带来的问题的可能性也会变大,使用者在调用过程中也会增加无形的压力。分离功能可以保持方法职责单一会是你API优秀的一部分。

可预测性

好的 API 具有可预测性,开发者可以根据例子推断它的用法。

Modernizr’s 特性检测 是个例子:

a) 它使用的属性名完全与 HTML5、CSS 概念和 API 相匹配
b) 每一个单独的检测一致地返回 true 或 false 值

// 所有这些属性都返回 'true' 或 'false'
Modernizr.geolocation
Modernizr.localstorage
Modernizr.webworkers
Modernizr.canvas
Modernizr.borderradius
Modernizr.boxshadow
Modernizr.flexbox

依赖于开发者已熟悉的概念也可以达到可预测的目的。

jQuery’s 选择器语法 就是一个显著的例子,CSS1-CSS3 的选择器可直接用于它的 DOM 选择器引擎。

$("#grid") // Selects by ID
$("ul.nav > li") // All LIs for the UL with class "nav"
$("ul li:nth-child(2)") // Second item in each list

编码风格

编码风格一定需要统一,而且编码风格一定不要显得外行。如果你的用户发现你的编码风格是PHP或者C#的,他们可能会产生你不是专业的这个感觉。推荐贴近JavaScript社区,采用JSLint或JSHint来矫正编码风格。这有利于源代码的阅读,在不同的人之间传递不会有不换了一门语言的感觉。

比例协调

好的 API 并不一定是小的 API,API 的体积大小要跟它的功能相称。

比如 Moment.js ,著名的日期解析和格式化的库,可以称之为均衡,它的 API 既简洁又功能明确。

像 Moment.js 这样特定功能的库,确保 API 的专注和小巧非常重要。

API接口漂亮

API接口漂亮包含几个方面:

编写 API 文档

软件开发最艰难的任务之一是写文档,实际上每个人都恨写文档,怨声载道的是没有一个好用的文档工具。

以下是一些文档自动生成工具:

最重要的是:确保文档跟代码同步更新。

命名

对外API的命名需要谨慎对待,方法名太长、方法名不直观、方法名大小写不对、方法名单词太复杂都会影响到使用者的直观感受。由于国内的英文水平高低不一,使用者遇见不认识的单词,都会造成障碍。

jQuery在方法命名上十分优秀。简短,直观,优雅。

参考资料:

调用

实参的传入也是考验API设计者的地方。如果需要调用者传入的参数较多,则该反思该API是否适合暴露给调用者。如果调用参数确实较多,并且支持可选项,则传入一个对象作为参数较为合适。由于对象带有明确的key,获取参数也无需一个一个检测。

$.ajax()是一个典型的例子,它支持的参数非常多,并且大多可选。所以暴露的API为$.ajax(url, obj)或$.ajax(obj)。

在JavaScript中,链式调用也是让用户较为喜爱的一点。Underscore除了提供普通的API外,还支持包装对象之后进行链式调用。这让熟悉函数式编程的人,顿生亲切。

习惯

Zepto.js是一个经典的案例,它提供了与jQuery几乎完全兼容的API,为的是照顾用户对于jQuery的熟悉。这让它可以零成本被应用到移动浏览器上。
jQuery的另一件反面例子则是它的each方法,与原生数组的forEach方法的回调传入值次序不同,这与习惯不同的接口,会造成一定反感心理。 在设计API的过程中,尽量寻找贴合用户习惯的已有形式,这会让用户易于接受。

可扩展性

模块在开发的过程中,可以包括有限的部分和无限的部分。有限的部分将会通过项目迭代,臻于完美。如果模块存在无限的部分,并且在有限的部分留出扩展来衍生无限,这对于模块的成长,这是一个大大的加分项。

jQuery留出$.fn来供用户扩展它,形成的影响是大量的jQuery插件涌现了出来。尽管大多数情况没有被正确使用,但不能掩盖它是一个漂亮的设计。

moment模块在它的lang部分,也提供了优雅的扩展它的部分。使得不同地区的用户可以自定义语言的显示。

扩展性的存在,使得开源社区能够参与,能够起到抛砖引玉的效果,反过来会增进模块的功能。

使用合适的设计模式

合适的设计模式可以让模块自身无瑕。不合适的设计模式则会适得其反。

这里的正反例子都与jQuery相关。正例子是Deferred的应用。过去ajax操作success和fail回调都必须写到$.ajax(obj)的参数对象中,但是Deferred对象使得调用更加自然:

$.get("test.php").done(function() {    alert("$.get succeeded");  }); 

LABjs的设计模式也十分优秀,script和wait两个用于加载和阻塞的API,通过链式调用,其乐融融:

<script>    $LAB    .script("framework.js").wait()    .script("plugin.framework.js")    .script("myplugin.framework.js").wait()    .script("init.js").wait(); </script> 

反例子则是来自jQuery插件。jQuery.fn不失为一个好的扩展点,但是有大量的jQuery插件操作的并非DOM,却生搬硬套将方法挂靠在jQuery.fn上。另一个例子则来自jQuery社区得意的UI组件。

$(foo).dialog('open') 

这类通过传入参数,又不能得到期望的返回值的场景,并不适合操作这个组件对象,API的参数传递更是显得不伦不类。如果是直接操作组件对象,则更友好一些。相比jQuery UI,YUI3的组件则优秀太多,API漂亮,组件的层次结构分明,易于扩展和自定义,jQuery UI通过插件方法的调用方式,自定义组件的代价极大。

合理的目录结构

开发方式多半会影响到后续的使用方式。尽管前端脚本多半都是提供一个文件给用户,但是在开发过程中,合理地组织自己项目的目录结构是值得注意的。jQuery的源代码目录中,各个功能点都分别写在各自的文件中,使得开发过程中编写代码方便。

另一方面,CommonJS的包规范还定义了以下目录和文件:

bin doc test lib package.json 

分别用于存放二进制文件、项目文档、单元测试用例、源代码。package.json文件则用于描述该包的一些包括包名、版本号、依赖等的信息,详情见http://wiki.commonjs.org/wiki/Packages/1.0。遵循规范的目录结构通常更好一点,因为大家都有同一个准则来参考,彼此更熟悉。

巨细的注解

通常用户真正需要去阅读你的代码的时候,是出现问题的时候。在开源社区,如何让发现你问题的人刚你改进代码,注释的作用功不可没。

另外,当一个用户真正是来欣赏你的代码时,如果看到Underscore这样密度的注解时(http://documentcloud.github.com/underscore/docs/underscore.html),还有拒绝它的勇气吗?

清晰明了API的文档

优秀的模块不仅仅体现在代码写得好上,更多的体现在如何让用户使用时更舒适。API文档必不可少。我心目中的API文档应该详细描述方法作用、参数、返回值的。甚至还应该有demo代码伴随。

API文档可以通过jsdoc之类的注解文档来生成。也可以另起文档来写。

API文档的一个特点是应该能方便查找,能在一个页面中展现完成的,尽量不要页面跳转或翻页。

API文档最大的作用让用户精确理解API,使得不造成误解,和清楚输入输出。

Underscore的API文档(http://documentcloud.github.com/underscore/)借用模版生成了漂亮的页面,使得查阅方便。

一见钟情的demo

相比Node,前端JavaScript模块更擅长做这件事情,尤其是UI库。男女之间首次见面的第一印象,很大程度可以决定某两个人是否会谈恋爱的概率。demo提供的形式可以影响到用户的直观体验,一般而言,用户觉得越炫,但是旁边提示的示例代码越简单,越会引发用户的好奇心。如果只有代码,或者只有demo,都会在表现性上打折扣。

对于Node的模块而言,由于没有live demo的感觉,尽量展示sample代码,让用户了解到他的目标是否与模块的目标一致。不要让用户错过你的模块,也不要让你的模块错过了它的用户。

README.md

README文件承担的作用仅次于demo,无需让用户产生心动的感觉,但是一定要引导用户更深度的了解你的模块,详细阅读你README的人,多半是有兴趣使用你的模块的人在做实地考察了。一个geting start入门是必不可少的,到API文档的链接也应该有。如果空落落的README,会立马有生疏感的。后期用户的心得体会等相关文章,也记得更新到README中。

本文由3522.com发布于计算机教程,转载请注明出处:JavaScript API 设计原则3522.com

关键词: 3522.com

上一篇:3522.combootstrap 3 来重构企业站

下一篇:没有了