Codebase: We use them all the time. Code libraries are where developers package the code they will use in their projects, which always saves time and avoids reinventing the wheel. Having a reusable package, whether open source or closed source, is better than building packages with the same features over and over again or manually copying and pasting from past projects.
Stop Maiming Bodies: The Perils of Pixel Font-Size
ES2016: Should the Future of JavaScript Be Developer-Driven?
In addition to packaged code, can the code base be described more accurately? With few exceptions, a codebase is usually just one file, or several files in the same folder. Its code should be able to be saved separately and used normally in your project. Libraries allow you to adjust the structure or behavior depending on your project. Imagine a USB device that can only communicate through the USB interface. Some devices, such as mice and keyboards, can be configured through interfaces provided by the device.
In this article, I will explain how to build the library file. Although most of the methods can be applied to other languages, this article focuses on building JavaScript libraries.
First and most important, library files allow existing code to be easily reused. You don't need to dig out an old project to copy the files, just bring in the library files. This also allows you to componentize your application, making your application's code base smaller and easier to maintain.
Christ Church Library (source)
Any abstract code that makes it easier to implement a specific function or can be reused can be Package it into a library file. jQuery is an interesting example. Although jQuery's API has a large number of simplified DOM APIs, it was of considerable significance in the past when cross-browser DOM operations were difficult.
If an open source project becomes popular and has many developers using it, there is a good chance that people will participate in its development by asking questions or contributing code. Either way, it helps the library and the projects that depend on it.
A popular open source library will also bring great opportunities. The company may recognize the quality of your work and make you an offer. Maybe the company will ask you to integrate your project into their application. After all, no one knows your project better than you.
Of course it might just be a habit - enjoy typing, helping others and learning and growing in the process. You can push your limits and try new things.
Before writing the first line of code, you need to determine what your library does - and you need to set a target. With this goal, you can focus on the problem you want to solve with this library. Keep in mind that the original form of your code base will be easier to use and remember when solving problems. The simpler the API, the easier it is for users to learn your code base. Introducing a Unix design philosophy:
Just do one thing and do it well
Ask yourself: What problem does your code base solve? How are you going to solve it? Will you do it all by yourself, or will you bring in someone else's code base?
No matter how big your codebase is, try to create a roadmap. List every feature you want and break them down into as many pieces as possible until you have a small but problem-solving code base that's like a minimum viable product. This will become your first version. From here, you can establish milestones for each new feature. Essentially, you turn your project into bit-level chunks of code, making each feature work better and more interesting. Trust me, this will keep you in good shape.
In my opinion, I want to develop my code base from the user's perspective. You could call it user-centered design. In essence, you are creating an outline of your codebase, giving it more thought and making it easier to use for those who choose it. At the same time, you need to think about where you need customization, which will be discussed later in this article.
The ultimate API testing is to try your own techniques and use your code base in your project. Try replacing the previous code with yours and see if it meets the features you want. Try to make your code base as intuitive as possible, allowing it to be used more flexibly in boundary conditions and customizable (more on this in a later article).
Here's an outline of what a code base for user agent strings might look like:
// 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();
Depending on the complexity of your code, you might spend some time organizing it. . Utilizing design patterns is a great way to organize your code base and can even solve some technical problems. This also avoids extensive refactoring to add new features.
Flexibility is what makes a code base powerful, but determining the boundaries of what can and cannot be customized is difficult. chart.js and D3.js are good examples. Both code libraries are used for data visualization. Chart.js makes it easy to create built-in charts in different forms. But if you want more control over images, D3.js is what you need.
There are several ways to give control to the user: configuration, exposing public methods, through callbacks and events.
Configuring the code base is usually done before initialization. But some code libraries allow you to modify configurations at runtime. Configurations are usually restricted to small parts, and only modifying their values for later use is allowed.
// 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('Basic usage', function () { it('should generate a single product', function () { // Create a single product var product = new UserAgent.Product('EvilCorpBrowser', '1.2'); product.setComment('X11', 'Linux', 'en-us'); expect(product.toString()) .toBe('EvilCorpBrowser/1.2 (X11; Linux; en-us)'); }); it('should combine several products', function () { var userAgent = new UserAgent; // Create and add first product var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // Create and add second product var engine = new UserAgent.Product('Blink', '20420101'); userAgent.addProduct(engine); expect(userAgent.toString()) .toBe('EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101'); }); it('should update products correctly', function () { var userAgent = new UserAgent; // Create and add first product var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // Update first product application.setComment('X11', 'Linux', 'nl-nl'); expect(userAgent.toString()) .toBe('EvilCorpBrowser/1.2 (X11; Linux; nl-nl)'); }); });
一旦你对你的API设计的第一个版本完全满意,是时候开始思考结构和你的代码库应该如何被使用。
你或许使用过模块加载器。使用你的代码库的开发者有可能使用加载器,所以你会希望自己的代码库与模块加载器是兼容的。但兼容哪一个呢?应该怎么从CommonJS,RequireJS,AMD和其他加载器中挑选呢?
Actually, you don’t need to pick! Universal Module Definition (UMD) is a rule whose goal is to support multiple loaders. You can find snippets of different styles online, or learn from the UMD GitHub repository and make it compatible with your codebase. Start with one of these templates, or add UMD with your favorite build tool and you don't have to worry about the module loader anymore.
If you want to use the ES2015 import/export syntax, I recommend using Babel and Babel’s UMD plugin to convert the code to ES5. This way you can use ES2015 in your project while generating a compatible code base.
I am all for using documentation on every project. But this often involves a lot of work, resulting in documentation being put off and eventually forgotten.
The writing of the document should start with basic information such as the name and description of the project. This will help others understand what your codebase does and whether it is useful to them.
You can provide information such as scope and goals to better inform users, and provide a roadmap to let them understand what new changes may be in the future and what they can provide help.
Of course, you need to make sure users know how to use your code base. This starts with the API documentation. Tutorials and examples are great additions, but writing them can be a huge job. However, this is not the case with Inline documentation. Here are some comments that can be parsed and converted into documentation pages using JSDoc
Some users want to make improvements to your code base. In most cases, this will be contributed code, but some will create a custom version for private use. For these users, it is helpful to provide documentation for meta-tasks like building the code base, running tests, generating, transforming, and downloading data.
When you open source your code base, it can be helpful to get code contributions. To guide contributors, you can add some documentation about the steps to contribute code and the criteria that need to be met. This will help you review and accept the contributed code and their correct contribution code.
Last point, use licensing. Technically, even if you don't choose any technology license, your code base is still copyrighted, but not everyone knows this.
I discovered that ChooseALicense.com is a website that allows you to choose a license without being a legal expert. After selecting the license, just add the LICENSE.txt file to the root directory of the project.
对一个好的代码库来说,版本是很重要的。如果你想要加入重大的变化,用户可能需要保留他们现在正在使用的版本。
Semantic Versioning是流行的版本命名标准,或者叫它SemVer。SemVer版本包括三个数字,每一个代表不同程度的改变:重大改变,微小的改变和补丁
如果你有一个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)!