此文已由作者吴维伟授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

在编写程序时,总会有一些代码是我们不愿意一遍又一遍重复地去写的,比如一些UI或交互相似组件,或是一些相似的流程或逻辑。以前,面对这样的情况,我会将可以复用的部分抽象出来,做成可以复用的模块,放在专门存放公用模块的文件夹中,便于查找和引用。但是这样只能解决单个项目中公用模块复用的问题,如果你的模块需要被多个项目复用,那么就需要另寻它法了。本文讨论的是通过发布npm包来实现模块复用时有哪些注意事项。

新建一个包

在项目根目录下执行下面的命令,初始化package.json文件。

$ npm init

文件中各字段的含义可以参考官方文档
其中需要注意的是:

  • name指定包名,在发布包时包名不能与已有包名重复。在发布前可以通过npm info packageName查看包名是否已存在。

  • main指定加载包时默认加载的文件。

  • files指定发布包时需要发布的文件。一般来说,.git, node_modules, test等文件不应该发布到npm仓库中。

  • dependencies指定需要依赖的第三方包。在安装此包时,这些第三方包也同时会被安装。为了提高包的安装速度,不必要的依赖不要放在dependencies中。

打包

为什么需要打包

业务项目的打包过程中,为了提高打包速度,某些处理过程会过滤第三方包,如babel。而且业务项目中可能没有处理第三方库中某些特性的能力,如第三方包中使用了coffeescript。所以作为一个能被良好复用的第三方包,需要在发布前,对自己的源码进行编译打包。

注意事项

可以选择webpack进行打包。在打包过程中,与业务项目不同的是:

将output.libraryTarget配置为commonjs

业务项目中打包后的文件只需要被执行,并不需要对外导出变量。为了能够以commonjs规范对外导出变量,需要将output.libraryTarget配置为commonjs点击查看如何配置

打包时过滤第三方包

作为一个NPM包,可能会依赖一些第三方包,如React。如果直接进行打包,打包后的文件会包含React的代码。一般依赖这个NPM包的业务项目也会依赖React,所以最终React的代码会被打包两次。所以建议在打包时对第三方包进行过滤,并把用到的第三方包声明在dependencies中,使依赖的第三方包随业务项目一起打包。
点击查看如何过滤第三方包

使用babel-plugin-transform-runtime

我们通常会使用babel来编译代码。在编译的过程中,babel会额外注入一些代码,使编译后的代码有更好的兼容性,如继承,Promise, Map, Set等功能的实现。为了防止这些额外的代码被重复加入,可以在编译时使用babel-plugin-transform-runtime插件,使这些额外的功能从第三方包(babel-runtime)中导入,而不是直接添加实现代码。同时在打包时也要对babel-runtime进行过滤,使babel-runtime随业务项目一起打包。
点击查看如何使用transform-runtime

按需加载

有时一个NPM包会提供很多功能,而依赖它的业务项目只需要其中一部分。这个时候,NPM包需要提供按需加载的功能,即在打包时只会将引用的部分模块打包进来,从而减少打包后文件的体积。

babel-plugin-import的使用

babel-plugin-import插件可以实现按需加载的功能。

{
    "plugins": [
        ["import", {
            "libraryName": "abc",
            "style": true
        }]
    ]}

上面的代码是babel配置文件的一部分,声明对abc模块使用按需加载的功能。它对下面的一段代码进行编译:

// 从模块中导入var1, var2, var3 3个变量,只使用了var1import {var1, var2, var3} from 'abc'console.log(var1)

编译结果:

import { style as _style } from 'abc/lib/var1/style';import _default from 'abc/lib/var1';

console.log(_default);

编译前,导入整个abc包。编译后,只导入了使用了的var1。babel-plugin-import支持更灵活的配置,点击查看详情
点击查看如何配置babel

为了使babel-plugin-import能够按需加载我们的NPM包,在打包时需要有一些相应的配置。如上面的abc包,需要对var1, var2, var3等模块分别打包。所以打包后的文件除了abc/index.js外,还需要有abc/lib/var1/index.js, abc/lib/var1/style.js等

测试

对于一个公用模块,最重要的应该是它的稳定性。一个每次升级都可能带来新bug的公用模块并不是我们想要的。相比于业务项目,公用模块提供的功能变化较小,这样单元测试就有了用武之地。想象之中,公用模块的迭代过程是这样的:

  • 明确模块提供的功能,并针对这些功能编写测试用例。

  • 进行功能开发,通过测试用例。

  • 如果需要修复bug或新增功能,针对bug或新功能编写测试用例,通过用例。

    如何进行单元测试

    断言库

    断言库主要用于对数据进行比较,判断其是否与预期相符。对于不满足预期的断言,会抛出一个异常.

    assert.equal('a', 'b')

    如上面的断言会抛出一个异常AssertionError: 'a' == 'b'断言库推荐使用Chai,支持多种风格的断言。

    测试框架

    测试框架可以对测试用例进行分类管理。每一个测试用例在一个单独的闭包中执行,如果在这个闭包中捕获到异常,则认为这个测试用例没有通过。

// 一个分类describe('type1:', function () {    // 一个用例(通过)
    it('case 1:', function () {
        assert.equal('a', 'a')
    })   // 一个用例(未通过)
    it('case 2:', function () {
        assert.equal('a', 'b')
    })
})

测试框架推荐使用mocha

测试执行器

推荐使用karma,它可以集成webpack、 mocha、 chai、 浏览器,对测试代码进行打包,并在浏览器环境下执行测试用例,最后在控制台输出结果。

文档

一个公用模块应该有一个API文档,但是手动维护一个文档的成本过高。所幸现在有一些工具可以根据代码中的注释自动生成API文档,你需要做的只是根据规范添加代码注释。
点击查看注释规范生成网页形式的文档生成markdown形式的文档

网易云免费体验馆,0成本体验20+款云产品!

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 一行代码搞定Dubbo接口调用
【推荐】 HBase原理–所有Region切分的细节都在这里了

编写一个供浏览器端使用的NPM包的更多相关文章

  1. 编写一个 Chrome 浏览器扩展程序

    浏览器扩展允许我们编写程序来实现对浏览器元素(书签.导航等)以及对网页元素的交互, 甚至从 web 服务器获取数据,以 Chrome 浏览器扩展为例,扩展文件包括: 一个manifest文件(主文件, ...

  2. 一个在浏览器端将html 转为pdf 的js 插件 jsPDF

    <!DOCTYPE html> <html> <head> <title>test</title> <meta http-equiv= ...

  3. 从0到1发布一个npm包

    从0到1发布一个npm包 author: @TiffanysBear 最近在项目业务中有遇到一些问题,一些通用的方法或者封装的模块在PC.WAP甚至是APP中都需要使用,但是对于业务的PC.WAP.A ...

  4. Html5浏览器端less应用

    之前的一个布局是用rem来做的 我上一段代码 div { margin: 0.833333333rem 0; } /* 去处a标签的下划线*/ a { text-decoration: none; } ...

  5. Node.js 最早 npm 包 request 将被废弃

    相信 Node.js 开发者对 Request 都不会陌生,这是一个 Node.js 模块,以 npm 包的形式提供,是一个简单的 HTTP 客户端,通过它可方便地实现 HTTP 请求. 可以看到,r ...

  6. SeaJS:一个适用于 Web 浏览器端的模块加载器

    什么是SeaJS?SeaJS是一款适用于Web浏览器端的模块加载器,它同时又与Node兼容.在SeaJS的世界里,一个文件就是一个模块,所有模块都遵循CMD(Common Module Definit ...

  7. 2道acm编程题(2014):1.编写一个浏览器输入输出(hdu acm1088);2.encoding(hdu1020)

    //1088(参考博客:http://blog.csdn.net/libin56842/article/details/8950688)//1.编写一个浏览器输入输出(hdu acm1088)://思 ...

  8. Java基础---Java---网络编程---TCP的传输、客户端和服务端的互访、建立一个文本转换器、编写一个聊天程序

    演示TCP的传输的客户端和服务端的互访 需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息. 客户端: 1.建立Socket服务,指定要连接方朵和端口 2.获取Socket流中的输出流,将数 ...

  9. JAVA WEB快速入门之从编写一个基于SpringBoot+Mybatis快速创建的REST API项目了解SpringBoot、SpringMVC REST API、Mybatis等相关知识

    JAVA WEB快速入门系列之前的相关文章如下:(文章全部本人[梦在旅途原创],文中内容可能部份图片.代码参照网上资源) 第一篇:JAVA WEB快速入门之环境搭建 第二篇:JAVA WEB快速入门之 ...

随机推荐

  1. 校园网下对VMware网络的配置

    1.参考:https://jingyan.baidu.com/article/8275fc8675c22646a03cf6b8.html 2.因为校园网的每一次连接都是自动获得IP地址的,所以虚拟机要 ...

  2. ccf--20140303--命令行选项

    本题是常规思路,这里要注意:1)带参命令没有参数和参数错误时终止2)命令不存在时终止3)命令都是错误的,不以—开头. 题目和代码如下: 问题描述 试题编号: 201403-3 试题名称: 命令行选项 ...

  3. ABAP 7.50 新特性之另一个CORRESPONDING

    在ABAP中,存在着一条法则:同样的名称代表的不一定是同样的东西(具体可看最近的相关讨论). 但是如你们所知的,存在着一个很好的例外: 所有涉及到使用CORRESPONDING为结构赋值的关键字的语法 ...

  4. (15)Python时间

  5. centos7下安装docker(13.1docker存储--data volume)

    我们现在知道docker 有两种存储方式:storage driver和data volume stroage driver这种存储方式主要是存储那些无状态的数据,是镜像层和容器层组成的,而data ...

  6. python MD5加密方法

    import hashlibhash = hashlib.md5()hash.update('admin')print hash.hexdigest()

  7. SpringMVC+Spring+Mybatis框架集成

    一.基本概念 1.Spring      Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-O ...

  8. 【转】windows 控制台cmd乱码的解决办法

    windows 控制台cmd乱码的解决办法 我本机的系统环境: OS Name: Microsoft Windows 10 企业版 OS Version: 10.0.14393 N/A Build 1 ...

  9. 使用RMAN Active duplicate创建异地auxiliary Database

    1g的RMAN duplicate 可以通过Active database duplicate和Backup-based duplicate两种方法实现.这里的测试使用的是Active databas ...

  10. leetcode 199. Binary Tree Right Side View 、leetcode 116. Populating Next Right Pointers in Each Node 、117. Populating Next Right Pointers in Each Node II

    leetcode 199. Binary Tree Right Side View 这个题实际上就是把每一行最右侧的树打印出来,所以实际上还是一个层次遍历. 依旧利用之前层次遍历的代码,每次大的循环存 ...