搜索
首页web前端js教程设计和构建你自己的JavaScript代码库:提示与技巧

代码库:我们一直在使用它们。代码库是开发者把他们会在项目中使用到的代码打包起来形成的,这总能节省时间和避免重复造轮子。拥有一个可重复使用的包,不管是开源的还是闭源的,总比重复构建一样特性的包或者从过去的项目中手动复制粘贴要好。

来自作者的更多文章

  • Stop Maiming Bodies: The Perils of Pixel Font-Size

  • ES2016: Should the Future of JavaScript Be Developer-Driven?

除了被打包的代码,还可以更精确的形容代码库吗?除了少数的例外,代码库通常只是一个文件,或者是在同一个文件夹里的几个文件。它的代码应该可以单 独保存和在你的项目中正常使用。库文件允许你根据项目的不同来调整结构或者行为。想象一下只能通过USB接口进行通讯的USB设备。一些设备,例如鼠标和 键盘,可以通过设备提供的接口来进行配置。

在这篇文章,我会解释如何构建库文件。尽管大部分的方法可以应用到其他语言,但这篇文章主要讲述的是构建JavaScript库文件。

为什么构建你自己的Javascript库?

首先和最重要的,库文件可以让现有的代码方便的重复利用。你不需要挖出陈旧的项目来复制文件,只需要引入库文件。这也可以让你的应用组件化,让应用的代码库更小更易维护。

设计和构建你自己的JavaScript代码库:提示与技巧

Christ Church Library (source)

任何可以让实现一个具体的功能更容易或者可以被重复利用的抽象的代码,都可以被打包进去库文件。jQuery是一个有趣的例子。尽管jQuery的API有大量的简化的DOM API,在跨浏览器DOM操作比较困难的过去有着相当重要的意义。

如果一个开源项目变得流行并且让许多开发者使用它,人们很有可能通过提出问题或者贡献代码来参加它的开发。不管怎样,都对库和依赖它的项目有所帮助。

一个流行的开源库也会带来很好的机会。公司可能会对你的工作质量表示认可并给你发offer。可能公司会要求你把你的项目整合进他们的应用。毕竟,没人可以比你更了解你的项目。

当然它可能只是一个习惯——享受敲码,帮助他人并在这个过程中学习和成长。你可以提升你的极限和尝试新的东西。

范围和目标

在写下第一行代码之前,你需要确定你的库的功能——你需要设置一个目标。通过这个目标,你可以专注于你想利用这个库来解决的问题。要牢记你的代码库的原本的形式在解决问题上要更容易使用和记忆。API越简单,使用者学习你的代码库就更容易。引入一个Unix的设计哲学:

只做一件事情并把它做好

问下你自己:你的代码库解决了什么问题?你打算怎么取解决它?你将会一个人完成全部工作,还是引入别人的代码库?

不管代码库体积有多大,尝试制作一个路线图。列出你想要的每一个特性,并将它们尽可能的打散,知道你有一个小巧但是能解决问题的代码库,就像 minimum viable product。这会成为你的第一个版本。从这里开始,你可以在每一个新特性建立里程碑。从本质上来说,你把你的项目变成了比特级的代码块,让每一个特性完成的更好和更有趣。相信我,这会让你保持状态良好。

API设计

在我看来,我想以使用者的角度来开发我的代码库。你可以称呼它为以用户为中心的设计。在本质上,你正在创造你的代码库的大纲,给予它更多的思考和让选择它的人使用起来更方便。在同时,你需要思考在什么地方需要可以定制,这会在这篇文章的后面讨论。

终极的API测试是尝试一下自己的技术,在你的项目使用你的代码库。试着用你的代码库替换之前的代码,看它是否满足了你想要的特性。试着让你的代码库尽可能的直观,让它可以更灵活的在边界条件下使用,还有定制化(在之后的文章会讲述)。

这是一个关于用户代理字符串的代码库的大纲的可能会有样子:

// Start with empty UserAgent string var userAgent = new UserAgent; 
// Create and add first product: EvilCorpBrowser/1.2 (X11; Linux; en-us) var application = new UserAgent.Product('EvilCorpBrowser', '1.2');
application.setComment('X11', 'Linux', 'en-us');
userAgent.addProduct(application); // Create and add second product: Blink/20420101 var engine = new UserAgent.Product('Blink', '20420101');
userAgent.addProduct(engine); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 userAgent.toString(); 
// Make some more changes to engine product engine.setComment('Hello World'); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World) userAgent.toString();

根据你的代码的复杂度,你可能会在组织结构上花费一些时间。利用设计模式是组织你的代码库的好办法,甚至可以解决一些技术问题。这还能避免加入新特性带来的大面积重构。

灵活性和定制性

灵活性是让代码库变得强大的要素,但是确定可以定制与不能定制的界限是很困难的。 chart.js 和D3.js是很好的例子。这两个代码库都是用来进行数据可视化的。Chart.js可以很简单的创建不同形式的内置图表。但是如果你想对图像进行更多的掌控,D3.js才是你需要的。

有好几种方法可以把控制权交给用户:配置,暴露公共方法,通过回调和事件。

配置代码库通常在初始化之前完成。但是一些代码库允许尼在运行时对配置进行修改。配置通常被细小的部分限制,只有为了之后的使用而修改它们的数值才是被允许的。

// Configure at initialization var userAgent = new UserAgent({
  commentSeparator: ';' }); // Run-time configuration using a public method userAgent.setOption('commentSeparator', '-'); 
  // Run-time configuration using a public property userAgent.commentSeparator = '-';

方法通常是暴露给实例使用的,比如说从实例中获取数据,或者设置实例的数据和执行操作。

var userAgent = new UserAgent; // A getter to retrieve comments from all products userAgent.getComments(); 
// An action to shuffle the order of all products userAgent.shuffleProducts();

回调通常是在公共的方法中被传递的,通常在异步操作后执行用户的代码。

var userAgent = new UserAgent;

userAgent.doAsyncThing(function asyncThingDone() { // Run code after async thing is done });

事件有很多种可能。有点像回调,除了增加事件句柄是不应该触发操作的。事件通常用于监听,你可能会猜到,这可是事件!更像回调的是,你可以提供更多的信息和返回一个数值给代码库去进行操作。

var userAgent = new UserAgent; // Validate a product on addition userAgent.on('product.add', function onProductAdd(e, product) { 
var shouldAddProduct = product.toString().length < 5; // Tell the library to add the product or not return shouldAddProduct;
});

在一些例子中,你可能允许用户对你的代码库进行扩展。因此,你需要暴露一些公共方法或者属性来让用户填充,像Angular的模块 (angular.module('myModule'))和Jquery的 fn(jQuery.fn.myPlugin)或者什么都不做,只是简单的让用户获取你的代码库的命名空间:

// AngryUserAgent module // Has access to UserAgent namespace (function AngryUserAgent(UserAgent) { 
// Create new method .toAngryString() UserAgent.prototype.toAngryString = function() { return this.toString().toUpperCase();
  };

})(UserAgent); // Application code var userAgent = new UserAgent; // ... // EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101 userAgent.toAngryString();

类似的,这允许你重写方法。

// AngryUserAgent module (function AngryUserAgent(UserAgent) { // Store old .toString() method for later use var _toString = UserAgent.prototype.toString; 
// Overwrite .toString() UserAgent.prototype.toString = function() { return _toString.call(this).toUpperCase();

  };

})(UserAgent); var userAgent = new UserAgent; // ... // EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101 userAgent.toString();

在后面的例子中,允许你的用户获取代码库的命名空间,让你在对扩展和插件的定义方面上的控制变小了。为了让插件遵循一些约定,你可以(或者是应该)写下文档。

测试

对测试驱动开发(test-driven development)来 说,写下大纲是良好的开始。简单来说,指的是在你写实际的代码库之前,在你写下测试准则的时候。如果测试检查的是你的代码特性是否跟期待的一样,以及你在 写代码库之前写测试,这就是行为驱动开发。不管怎样,如果你的测试覆盖了你的代码库的每一个特性,而且你的代码通过了所有的测试。你可以确定你的代码是可 以正常工作的。

Jani Hartikainen讲述了如何利用Mocha来进行单元测试 Unit Test Your JavaScript Using Mocha and Chai。在使用Jsmine,Travis,Karma测试JavaScript (Testing JavaScript with Jasmine, Travis, and Karma)这篇文章中,Tim Evko展示了怎么通过另一个叫做Jasmine的框架来设置良好的测试流程。这两个测试框架都是非常流行的,但还有适应别的需求的其他框架。

我在这篇文章前面撰写的大纲,已经讲述了它期待怎样的输出。这是一切测试的开始:从期望出发。关于我的代码库的一个Jasmine测试像是这样:

describe(&#39;Basic usage&#39;, function () {
  it(&#39;should generate a single product&#39;, function () { // Create a single product var product = new UserAgent.Product(&#39;EvilCorpBrowser&#39;, &#39;1.2&#39;);
    product.setComment(&#39;X11&#39;, &#39;Linux&#39;, &#39;en-us&#39;);

    expect(product.toString())
      .toBe(&#39;EvilCorpBrowser/1.2 (X11; Linux; en-us)&#39;);
  });

  it(&#39;should combine several products&#39;, function () { var userAgent = new UserAgent; 
  // Create and add first product var application = new UserAgent.Product(&#39;EvilCorpBrowser&#39;, &#39;1.2&#39;);
    application.setComment(&#39;X11&#39;, &#39;Linux&#39;, &#39;en-us&#39;);
    userAgent.addProduct(application); // Create and add second product var engine = new UserAgent.Product(&#39;Blink&#39;, &#39;20420101&#39;);
    userAgent.addProduct(engine);

    expect(userAgent.toString())
      .toBe(&#39;EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101&#39;);
  });

  it(&#39;should update products correctly&#39;, function () { var userAgent = new UserAgent; 
  // Create and add first product var application = new UserAgent.Product(&#39;EvilCorpBrowser&#39;, &#39;1.2&#39;);
    application.setComment(&#39;X11&#39;, &#39;Linux&#39;, &#39;en-us&#39;);
    userAgent.addProduct(application); // Update first product application.setComment(&#39;X11&#39;, &#39;Linux&#39;, &#39;nl-nl&#39;);

    expect(userAgent.toString())
      .toBe(&#39;EvilCorpBrowser/1.2 (X11; Linux; nl-nl)&#39;);
  });
});

一旦你对你的API设计的第一个版本完全满意,是时候开始思考结构和你的代码库应该如何被使用。

模块加载器兼容性

你或许使用过模块加载器。使用你的代码库的开发者有可能使用加载器,所以你会希望自己的代码库与模块加载器是兼容的。但兼容哪一个呢?应该怎么从CommonJS,RequireJS,AMD和其他加载器中挑选呢?

实际上,你不需要挑选!通用模块定义(UMD)是一个目标就是支持多种加载器的规则。你可以在网上找到不同风格的代码段,或者从这里 UMD GitHub repository学习并让它与你的代码库兼容。从其中的某个模板开始,或者使用你喜欢的构建工具add UMD with your favorite build tool,你就不必再担心模块加载器的问题了。

如果你希望用上ES2015的 import/export 语法,我建议使用Babel和Babel’s UMD plugin来将代码转换成ES5。通过这个方法你可以在你的项目中使用ES2015,同时生成兼容性良好的代码库。

文档

我完全赞成在每一个项目中使用文档。但这通常牵涉到大量的工作,导致编写文档被推迟和在最后被遗忘。

基本信息

文档的编写应当从项目的名字和描述之类基本的信息开始。这会对别人明白你的代码库做了什么和是否对他们有用有所帮助。

你可以提供像是适用范围和目标之类的信息来更好的给用户提供信息,而提供路线图可以让他们明白在未来可能会有什么新变化以及他们可以提供怎样的帮助。

API,教程和例子

当然,你需要确保用户知道如何去使用你的代码库。这从API文档开始。教程和例子是很好的补充,但编写他们会是一个庞大的工作。然而,内联文档Inline documentation不会这样。下面是一些利用JSDoc的,可以被解析和转换成文档页的注释

元任务

一些用户想对你的代码库作出改进。在大多数情况中,会是贡献代码,但有一些会创建一个私人使用的定制版本。对于这些用户,为类似构建代码库,运行测试,生成,转换和下载数据之类的元任务提供文档很有帮助的。

贡献

当你对你的代码库进行开源,得到代码贡献是很有帮助的。为了引导贡献者,你可以增加一些关于贡献代码的步骤和需要满足的标准的文档。这会对你审阅和接纳贡献的代码和他们正确的贡献代码有所帮助。

授权许可

最后一点,使用授权许可。从技术上讲,即时你没有选择任何一种技术许可,你的代码库依然是拥有版权的,但不是每一个人都知道这一点。

我发现 ChooseALicense.com这个网站可以让你一个不需要成为法律专家也能挑选授权许可。在挑选授权许可之后,只需要在项目的根目录下添加 LICENSE.txt 文件。

将它打包和发布到包管理器

对一个好的代码库来说,版本是很重要的。如果你想要加入重大的变化,用户可能需要保留他们现在正在使用的版本。

Semantic Versioning是流行的版本命名标准,或者叫它SemVer。SemVer版本包括三个数字,每一个代表不同程度的改变:重大改变,微小的改变和补丁

在你的Git仓库加入版本和发布

如果你有一个git仓库,你可以在你的仓库添加版本数字。你可以把它想象成你的仓库的快照。我们也叫它标签 Tags。可以通过开启终端和输入下面的文字来创造标签:

# git tag -a [version] -m [version message]
git tag -a v1.2.0 -m "Awesome Library v1.2.0"

很多类似GitHub的服务会提供关于所有版本的概览和提供它们的下载链接。

发布一个通用仓库

npm

许多编程语言自带有包管理器,或者是第三方包管理器。这可以允许我们下载关于这些语言的特定代码库。比如PHP的Composer 和Ruby的RubyGems

Node.js,一种独立的JavaScript引擎,拥有 npm,如果你对npm不熟悉,我们有一个很好的教程beginner’s guide。

默认情况下,你的npm包会发布为公共包。不要害怕,你也可以发布私有包 private packages, 设置一个私有的注册private registry, 或者根本不发布avoid publishing.

为了发布你的包,你的项目需要有一个 package.json 文件。你可以手动或者交互问答的方式来创建。通过输入下面的代码来开始问答:

`npm init`

这个版本属性需要跟你的git标签吻合。另外,请确定有README.md 文件。像是GitHub,npm在你的包的展示页使用它。

之后,你可以通过输入下面的代码来发布你的包:

`npm publish`

就是这样!你已经成功发布了你的npm包。

Bower

几年前,有另一个叫做Bower的包管理器。这个包管理器,实际上不是为了特定的语言准备的,而是为了互联网准备的。你可以发现大部分是前端资源。在Bower发布你的包的关键一点是你的代码库是否跟它兼容。

如果你对Bower不熟悉,我们也有一个教程beginner’s guide 。

跟npm一样,你也可以设置一个私有仓库private repository。你可以通过问答的方式避免发布。

有趣的是,在最近的一两年,很多人转为使用npm管理前端资源。近段npm包主要是跟JavaScript相关,大部分的前端资源也发布在了npm上。不管怎样,Bower仍然流行。我明确的推荐你在Bower上发布你的包。

我有提到Bower实际上是npm的一种模块,并最初是得到它的启发吗?它们的命令是很相似的,通过输入下面的代码产生bower.json文件:

`bower init`

跟npm init类似,指令是很直白的,最后,发布你的包:

`bower register awesomelib http://www.php.cn/`

像是把你的代码库放到了野外,任何人可以在他们的Node项目或者网络上使用它!

总结

核心的产品是库文件。确定它解决了问题,容易和适合使用,你会使得你的团队或者许多开发者变得高兴。

我提到的很多任务都是自动化的,比如:运行测试,创建标签,在package.json升级版本或者在npm或者bower重发布你的包。 这是你像Travis CI 或 Jenkins一样踏入持续集成和使用工具的开始。我之前提到的文章 article by Tim Evko 也讲述到了这点。

你构建和发布代码库了吗?请在下面的评论区分享!

 

以上就是设计和构建你自己的JavaScript代码库:提示与技巧的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

了解JavaScript引擎:实施详细信息了解JavaScript引擎:实施详细信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:学习曲线和易用性Python vs. JavaScript:学习曲线和易用性Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

Python vs. JavaScript:社区,图书馆和资源Python vs. JavaScript:社区,图书馆和资源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

从C/C到JavaScript:所有工作方式从C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript引擎:比较实施JavaScript引擎:比较实施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

超越浏览器:现实世界中的JavaScript超越浏览器:现实世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

使用Next.js(后端集成)构建多租户SaaS应用程序使用Next.js(后端集成)构建多租户SaaS应用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前By尊渡假赌尊渡假赌尊渡假赌
威尔R.E.P.O.有交叉游戏吗?
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境