NPM开源模块贡献指南(上)—— 开发之前

在IT界,鉴定一个程序员能力时有一个很重要的指标 —— 造轮子的能力。同样地,对于前端开发者来说,在NPM贡献、维护模块是一件很自豪、很有成就感的事情。那么,作为只写过业务,没有进行过这方面尝试的小白可能会有一些问题:

  • 如何开发一个高质量高可用的模块
  • 如何将我的模块发布到NPM进行管理

这篇文章,我们先了解一下开发一个模块需要知道些什么,主要是开发前的构思和注意事项,下篇中我们再讲解实际开发的步骤。

在着手开发一个模块之前,你需要考虑一下几个问题:

  • 模块是提供给浏览器环境还是Node环境使用?
  • 准备使用什么语法特性来编写模块?
  • 是否需要编译为兼容性更好的版本?
  • 是否需要打包成一个单独的可引用文件?
  • 哪些文件需要推送到NPM,哪些不需要?
  • 如何引入及管理模块自身的依赖?

我们来逐个讨论上面的问题

模块是提供给浏览器环境还是Node环境使用?

两者主要区别在于浏览器和 Node 环境的运行时不同。浏览器端主要是对页面做操作,而 node 端则包含文件读写、数据存取、网络服务等。比如,浏览器环境自带 window document 等 Node 没有的对象,而 node 自带 global process 等浏览器中不存在的对象。如果没弄清楚环境随意使用,则很可能会导致运行异常。

如果模块可以同时运行在浏览器和Node环境中,我们称该模块为同构的 (isomorphic)。此时尤其需要考虑对不同环境 api 的调用是否合理。关于这个问题完全可以新开一篇博客,这次就先不讲了,感兴趣的可以先看看 这篇文章

准备使用什么语法特性来编写模块

不同的运行环境支持的语法不同。例如,一段在浏览器环中运行的 JS 代码里,如果使用了箭头函数 () => {}, 那么在 IE 系列的浏览器中就会报错,因为 IE 浏览器全家都不支持该语法;又例如,一段在 node 端运行的代码里,使用了异步函数 async await, 那么它在node v7.6 以下的版本中运行就会报错,因为 node v7.6 才开始原生支持异步函数语法。

因此,在编写模块之前,如果不打算做兼容性的编译,那么需要考虑使用的语法特性在目标环境是否支持,或者针对目标环境使用对应的语法特性。

如果希望使用最新的语法特性,同时兼容旧版本环境,那么就需要考虑接下来的问题

是否需要编译为兼容性更好的版本

编写NPM模块时,为了解决语法特性的兼容问题,一般有两种方式:

  1. 使用语法转换器(transpiler) 把自己的源代码转换为更兼容的代码。常用的转换器有 babel typescript 等;
  2. 在目标环境添加语法兼容代码(polyfill),提供新语法特性里加入的 api。这种方式需要模块使用者自行添加。常用的 polyfill 有 babel-polyfillcore-js 等。

需要注意的是,polyfill 并非万能,语法特性关键字、部分 api(例如 ES6 Proxy) 无法被 polyfill兼容。同时,作为模块开发者,我们无法预测模块运行的目标环境,因此使用语法转换器在大部分情况下是更好的做法。

是否需要打包成一个单独的可引用文件

一般而言,模块开发者都没有做这一步的必要。

  1. 在Node端使用的CommonJS 规范对模块有良好的引用及隔离,没有打包的必要
  2. 浏览器中使用时,目前主流的方案都是基于 webpack或者其他的打包工具,源代码会被打包工具索引并引入,也不需要打包

只有在模块需要提供可以单独直接使用的文件时,才需要打包,比如打包一个可直接在script标签中引用的完整文件

常见的例子有

<script src="//cdn/vue.min.js"></script>

常见的打包方式有 webpack browserify + uglifyjs

哪些文件需要推送到NPM,哪些不需要

一般而言,一个完整的NPM模块的项目目录如下:

your_module
├── node_modules
│   └── ...
├── dist                // 打包后的文件存放目录
│   └── your_module.min.js
├── src                    // 源代码目录
│   └── index.js
├── lib                    // 编译后的代码目录
│   ├── index.js
├── .npmignore
├── .gitignore
├── .babelrc            // 使用babel进行语法转换时需要
├── index.js            // 模块默认入口文件
├── package.json
├── LICENSE                // 开源证书
├── README.md            // 接入文档
└── CHANGELOG.md        // 更新日志

不需要推送到NPM的文件在 .npmignore 文件中体现,如果模块没有定义 .npmignore, npm 默认会采用与目录中 .gitignore 相同的规则忽略文件

.gitignore

dist
src
node_modules

yarn-error.log
yarn.lock
package-lock.json

不需要推送到代码仓库的在 .gitignore 中体现

lib
node_modules

yarn-error.log
yarn.lock
package-lock.json

如何引入及管理模块自身的依赖

当需要在模块中引入其他模块依赖时,需要注意 npm install 的三种安装类型

  1. dependency 是模块正常工作所必须安装的依赖,使用者安装模块时,dependency依赖模块会被自动安装,安装命令为 npm install -S <module_name>,保存在package.json中的dependencies
  2. devDependency是开发此模块过程中所需要的模块,使用者无需安装,比如语法转换器 babel 在开发过程需要用来进行语法转换,但使用者安装的已经是编译后的版本,所以无需安装,对应的命令是 npm install -D <module_name>,对应到package.json中的devDependencies
  3. peerDependency 也是模块正常工作所必须的依赖,但是使用者安装模块时,这些模块不会被自动安装,仅在安装完成后检查当前目录下此依赖是否已经被安装。例如,为 vue 框架开发了某个组件并提炼为单独的模块,使用该组件时,一定要搭配 vue 框架;通常 vue 已经被使用者单独安装过了,组件模块自身无需安装 vue,这时把 vue 声明为组件模块的 peerDependency 就比较合适。

总结

以上是开发一个NPM包之前需要考虑的一些问题,下一篇中我们再详细讲讲开发、发布NPM包的流程、步骤和注意事项。

0%