Home >Web Front-end >JS Tutorial >Design and Build Your Own JavaScript Library: Tips & Tricks
Build and publish your own JavaScript library: a detailed guide
Core points
This article was peer-reviewed by Adrian Sandu, Vildan Softic and Dan Prince. Thanks to all SitePoint peer reviewers for getting SitePoint content to its best!
We often use libraries. Library is packaged code that developers can use in their projects, which undoubtedly saves workload and avoids duplicate wheels. Having reusable packages (both open source or closed source) is better than rebuilding the same functionality or copy-pasting manually from past projects.
But besides the packaged code, what exactly is the library? With some exceptions, the library should always be a single file, or several files located in a single folder. Its code should be maintained separately and remains the same when it is implemented into the project. The library should allow you to set project-specific configurations and/or behaviors. Think of it as a USB device that only allows communication through a USB port. Some devices, such as mice and keyboards, allow configuration through the interfaces provided by the device.
In this article, I will explain how the library is built. Although most of the topics covered are suitable for other languages, this post focuses on building JavaScript libraries.
First of all, the library makes reusing existing code very convenient. You don't have to dig out old projects and copy some files, just bring in the library. This will also fragment your application, making the application code base smaller and easier to maintain.
A popular open source project can also bring huge opportunities. A company may be impressed by the quality of your job and offer you a job. Maybe a company will ask you for help integrating your project into their application. After all, no one knows your library better than you.
For many people, it is just a hobby – enjoying the process of writing code, helping others, and learning and growing in the process. You can push your limits and try new things.
Scope and Target
Do only one thing and do it well
Ask yourself: What problems does your library solve? How are you going to solve it? Would you write everything yourself or could you use someone else's library?
Try to create a roadmap regardless of the size of the library. List all the features you want and then remove as many features as you can until you have a tiny but fully functional library like the smallest viable product. This will be your first version. From there, you can create milestones for each new feature. Essentially, you break the project down into small pieces, making each feature more like an achievement and more enjoyable. Trust me, this will keep you sane.
API Design
The final API quality test is to eat your own dog food and use your library in your own project. Try replacing the application code with your library and see if it covers all the features you want. Try to make the library as thin as possible while making it flexible enough to make it suitable for their extreme situations as well by customization (as described later in this article).
The following is what the implementation or summary of the user agent string library might look like:
<code>// 以空的UserAgent字符串开始 var userAgent = new UserAgent; // 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us) var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // 创建并添加第二个产品:Blink/20420101 var engine = new UserAgent.Product('Blink', '20420101'); userAgent.addProduct(engine); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 userAgent.toString(); // 对引擎产品进行更多更改 engine.setComment('Hello World'); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World) userAgent.toString(); </code>
Depending on the complexity of the library, you may also need to consider the structure. Utilizing design patterns is an excellent way to build libraries or overcome certain technical problems. It also reduces the risk of refactoring most of the code when adding new features.
What makes the library powerful is flexibility, but it is also difficult to draw a line between what you can customize and what you can't. A perfect example is chart.js and D3.js. Both are excellent libraries for visualizing data. Chart.js makes it very easy to create and set up different types of built-in charts. However, if you need more control over the graphics, you need to use D3.js. There are several ways to give users control: configuration, exposing public methods, and through callbacks and events.
The configuration of thelibrary is usually done during initialization, but some libraries allow you to modify options at runtime. Options are usually limited to the details, changing these options should not do anything except updating these values for later use.
Methods can be disclosed to interact with instances, such as retrieving data from instances, putting data into instances (setters), and performing operations.
<code>// 在初始化时配置 var userAgent = new UserAgent({ commentSeparator: ';' }); // 使用公共方法进行运行时配置 userAgent.setOption('commentSeparator', '-'); // 使用公共属性进行运行时配置 userAgent.commentSeparator = '-'; </code>
Callbacks are sometimes passed along with public methods, usually running user code after an asynchronous task.
<code>var userAgent = new UserAgent; // 获取器,用于从所有产品中检索注释 userAgent.getComments(); // 用于打乱所有产品顺序的操作 userAgent.shuffleProducts(); </code>
Events have great potential. They are similar to callbacks, except that adding event handlers should not trigger actions. Events are usually used to indicate (you may have guessed) events! Just like a callback, you can provide additional information and return values that the library can use.
<code>var userAgent = new UserAgent; userAgent.doAsyncThing(function asyncThingDone() { // 异步操作完成后运行代码 }); </code>
In some cases, you may want to allow users to extend your library. To do this, you can expose a public method or property for user to fill, just like Angular module (angular.module('myModule')) and jQuery's fn (jQuery.fn.myPlugin), or do nothing, just Give users access to the namespace of your library:
<code>var userAgent = new UserAgent; // 验证添加的产品 userAgent.on('product.add', function onProductAdd(e, product) { var shouldAddProduct = product.toString().length // 告诉库添加或不添加产品 return shouldAddProduct; }); </code>
Again, this allows you to override the method.
<code>// AngryUserAgent 模块 // 可以访问UserAgent命名空间 (function AngryUserAgent(UserAgent) { // 创建新的方法 .toAngryString() UserAgent.prototype.toAngryString = function() { return this.toString().toUpperCase(); }; })(UserAgent); // 应用程序代码 var userAgent = new UserAgent; // ... // EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101 userAgent.toAngryString(); </code>
In the latter case, allowing users to access the namespace of your library will reduce your control over how the extensions/plugins are defined. To ensure that the extension follows certain conventions, you can (and should) write documentation.
<code>// AngryUserAgent 模块 (function AngryUserAgent(UserAgent) { // 存储旧的 .toString() 方法以供以后使用 var _toString = UserAgent.prototype.toString; // 覆盖 .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(); </code>
Test
Jani Hartikainen explains how to write unit tests in "Using Mocha and Chai for unit testing your JavaScript". In "Testing JavaScript with Jasmine, Travis, and Karma", Tim Evko shows how to set up a great test pipeline with another framework called Jasmine. These two testing frameworks are very popular, but there are many other types of frameworks.
The summary I created earlier in this article has commented on the expected output. This is where all tests begin: start with expectations. Jasmine tests for my library are as follows:
<code>// 以空的UserAgent字符串开始 var userAgent = new UserAgent; // 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us) var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // 创建并添加第二个产品:Blink/20420101 var engine = new UserAgent.Product('Blink', '20420101'); userAgent.addProduct(engine); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 userAgent.toString(); // 对引擎产品进行更多更改 engine.setComment('Hello World'); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World) userAgent.toString(); </code>
Once you are completely satisfied with the first version of the API design, it is time to start thinking about the architecture and how your library will be used.
You may or may not use module loaders. However, developers who choose to implement your library may use it, so you want your library to be compatible with the module loader. But which one? How do you choose between CommonJS, RequireJS, AMD and other module loaders?
Actually, you don't need to choose! Universal Module Definition (UMD) is another strategy designed to support multiple module loaders. You can find different versions of code snippets online, and you can also find different versions of UMD in the UMD GitHub repository to make your library compatible with UMD. Start your library with one of the templates, or add UMD with your favorite build tool, and you don't have to worry about module loaders.
If you wish to use ES2015 import/export syntax, I highly recommend using Babel to compile to ES5, combined with Babel's UMD plugin. This way, you can use ES2015 in your project while still generating libraries that are suitable for everyone.
I am completely in favor of thorough documentation of all projects, but this is often considered a lot of work, delayed and eventually forgotten.
Documentation should always start with basic information, such as project name and description. This will help others understand the functionality of your library and whether it works for them.
You can provide additional information, such as scope and goals, to better inform users, as well as a roadmap so they know what will happen in the future or how they can contribute.
Of course, you need to let the user know how to use your library. This starts with the API documentation. Tutorials and examples are great additions, but writing these can take a lot of work. However, this is not the case with inline documentation. These are comments that can be parsed and converted to a document page using JSDoc.
Some users may want to change your library. In most cases, this will be for contribution, but some people may wish to create a custom version for private use. For these users, documents containing metatasks are useful, such as lists of commands for building libraries, running tests, generating, converting, or downloading data, etc.
Contribution is very important when you open source your library. To guide contributors, you can add documentation to it, explaining the steps to contribute and the criteria it should meet. This will make it easier for you to review and accept contributions and make it easier for them to contribute correctly.
Last but not least, please include a license. Technically, if you choose not to include a license, it is still copyrighted, but not everyone knows this.
I found ChooseALicense.com to be a great resource for you to choose a license without becoming a legal expert. After selecting a license, just save the text in the LICENSE.txt file in the root directory of the project.
Versioning is essential for a good library. If you choose to make a significant change, users may want to continue using the version that works for them.
The current de facto version naming criteria are semantic version control, or SemVer. The SemVer version consists of three numbers, each representing a different change: the primary, minor, and patch versions.
If you have a git repository, you can add a version number to the repository. You can think of them as snapshots of the repository. We call it a label. To create a tag, open a terminal and type:
<code>// 以空的UserAgent字符串开始 var userAgent = new UserAgent; // 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us) var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // 创建并添加第二个产品:Blink/20420101 var engine = new UserAgent.Product('Blink', '20420101'); userAgent.addProduct(engine); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 userAgent.toString(); // 对引擎产品进行更多更改 engine.setComment('Hello World'); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World) userAgent.toString(); </code>
Many services (such as GitHub) will provide an overview of all versions and a download link for each version.
Many programming languages come with package managers, or third-party package managers can be used. These allow us to introduce libraries specifically for these languages. For example, PHP's Composer and Ruby's RubyGems.
Node.js (a standalone JavaScript engine) comes with npm. If you are not familiar with npm, we have a great beginner's guide.
By default, your npm package will be published publicly. don’t worry! You can also publish private packages, set up private registry, or avoid publishing altogether.
To publish your package, your project needs a package.json file. You can do this manually or use the interactive wizard. To start the wizard, type:
<code>// 在初始化时配置 var userAgent = new UserAgent({ commentSeparator: ';' }); // 使用公共方法进行运行时配置 userAgent.setOption('commentSeparator', '-'); // 使用公共属性进行运行时配置 userAgent.commentSeparator = '-'; </code>The
version attribute should match your git tag. Also, make sure you have the README.md file. Just like GitHub, npm uses it as a page that renders your package.
After, you can publish your package by typing the following command:
<code>var userAgent = new UserAgent; // 获取器,用于从所有产品中检索注释 userAgent.getComments(); // 用于打乱所有产品顺序的操作 userAgent.shuffleProducts(); </code>
That's it! You have posted your npm package.
A few years ago, another package manager called Bower appeared. However, this package manager is not designed for a specific language, but for a specific platform-Web. You can find all the major front-end resources there. It only makes sense to publish your package on Bower if your library is compatible with the browser.
If you are not familiar with Bower, we also have a beginner's guide.
Like npm, you can also set up a private repository. You can also prevent it from publishing completely in the wizard.
Interestingly, over the past year or two, many people seem to be turning to npm for front-end resources. Although the npm package is mainly JavaScript, many front-end packages are also released on npm. Either way, Bower is still very popular, so I definitely recommend you post your package to Bower too.
Did I mention that Bower is actually an npm module and was initially inspired by it? The command is very similar. To generate bower.json file, type:
<code>// 以空的UserAgent字符串开始 var userAgent = new UserAgent; // 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us) var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // 创建并添加第二个产品:Blink/20420101 var engine = new UserAgent.Product('Blink', '20420101'); userAgent.addProduct(engine); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 userAgent.toString(); // 对引擎产品进行更多更改 engine.setComment('Hello World'); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World) userAgent.toString(); </code>Just like npm init, the explanation is self-explanatory. Finally, publish your package:
<code>// 在初始化时配置 var userAgent = new UserAgent({ commentSeparator: ';' }); // 使用公共方法进行运行时配置 userAgent.setOption('commentSeparator', '-'); // 使用公共属性进行运行时配置 userAgent.commentSeparator = '-'; </code>That's it, you've posted your library to the internet for everyone to use on their Node projects and/or on the web!
Conclusion
Many of the tasks I mentioned are easy to automate, such as running tests, creating tags, updating versions in package.json, and republishing your packages to npm and bower. This is where you get into the realm of continuous integration and use tools like Travis CI or Jenkins. I mentioned earlier about Tim Evko's article also touches on this.
Have you built and published the library? Please share in the comment section below!
Frequently Asked Questions (FAQ) on Designing and Building Your Own JavaScript Library
How do I start creating JavaScript libraries?
How do I test my JavaScript library?
How do I record my JavaScript library?
There are multiple ways to distribute JavaScript libraries. A common way is to publish it to a package manager like npm. This allows other developers to easily install your library using simple commands. You can also distribute your library by hosting it on a CDN (Content Distribution Network) or providing a download link on your website.
Maintaining JavaScript libraries includes fixing bugs, adding new features, and keeping the library in sync with the latest JavaScript standards and practices. It is important to test your library regularly and listen to user feedback. You can also consider versioning your library so that users can choose to use a stable version or a latest version with new features.
To ensure that your JavaScript library is efficient, you should focus on writing concise and clear code. Avoid unnecessary computation and memory allocation. Use tools like Chrome DevTools to analyze your library and identify any performance bottlenecks. You can also consider compressing your library to reduce its file size and increase loading time.
Since each browser interprets JavaScript differently, ensuring browser compatibility can be a challenge. You can use tools like Babel to convert your code into a JavaScript version that is compatible with older browsers. You should also test your library in different browsers to identify and fix any compatibility issues.
Error handling is an important part of developing JavaScript libraries. You should strive to provide clear and helpful error messages to help users understand what is wrong. You can use the try/catch block to catch and handle errors. You can also consider providing a way to let users report errors and problems.
There are several ways to get feedback about your JavaScript library. You can ask other developers to review your code, post your library to forums or social media, or post it to a package manager like npm and seek feedback. You should be open to criticism and be willing to make changes based on the feedback you receive.
The above is the detailed content of Design and Build Your Own JavaScript Library: Tips & Tricks. For more information, please follow other related articles on the PHP Chinese website!