Npm 多模块依赖解决方案
作者: dkvirus 发表于: 2018-08-18 17:40:03 最近更新: 2018-08-18 18:31:53

一、问题

以电商后管系统为例,有三个项目:

  • 商品项目:mail-goods
  • 订单项目:mail-order
  • 集成项目:mail-integration

其中:mail-integration 项目依赖 mail-goods 和 mail-order。我们称 mail-integration 为 集成项目,mail-goods 和 mail-order 为 底层依赖项目

如果商品项目(mail-goods)的代码有改动,需要经过以下几个步骤才能在集成项目(mail-integration)中看到效果:

  • mail-goods 原本版本号为 1.0.0,修改 mail-goods 的代码;
  • 修改 mail-goods 版本号为 1.1.0,发布最新包:npm publish
  • 切换到 mail-integration 目录下,更新高版本的 mail-goods:npm install mail-goods@1.1.0
  • 重新运行 mail-integration,才可以看到 mail-goods 中修改的代码效果。

这样做有几个问题:

  • 即便底层依赖包 mail-goods 只是修改了一个字符,也要经过 publish、install 操作,过程繁琐;
  • Npm 公共仓库 publish 不能发布同版本的包,也就是说每次 publish 都必须升级底层依赖包的版本号,不利于版本的管理。

为了解决上面的问题,经过调研,有三个方案可以解决问题,下面一一介绍。

二、方案一:lerna

很多开源项目都用到了 lerna 做多模块依赖管理,如:dva、umi。如果第一次接触 lerna,可以动手操作一下下面的步骤感受一下 lerna 是如何优雅的解决多模块依赖问题。

1)安装全局包 lerna

1
$ npm install lerna -g

2)初始化 lerna 项目

1
2
3
$ mkdir test-lerna 
$ cd test-lerna
$ lerna init

会生成如下目录:

1
2
3
4
|-- test-lerna
|-- packages/
|-- lerna.json
|-- package.json

所有子包都放到 packages/ 目录下面统一管理。

3)补充目录结构

在 test-lerna/packages 目录下新建 test1 和 test2 两个子模块,分别创建 package.json 文件。

npm多模块依赖图1

修改 test2 子模块的 package.json,添加配置依赖 test1 子模块。

1
2
3
4
5
6
7
8
9
10
11
{
"name": "test2",
"version": "1.0.0",
// ....
"dependencies": {
"test1": "^1.0.0"
},
"keywords": [],
"author": "",
"license": "ISC"
}

在 /test-lerna 目录下执行 $ lerna bootstrap 安装依赖包,注意这里不是 $ npm install,因为 test2 依赖了 test1,去 test2 目录下可以看到出现了 node_modules 目录,可以看到 test1 目录被加载进来了(这里的 test1 其实相当于是个快捷方式而已),但到目前为止我们压根没有将 test1 提交到 npm 远程仓库。

4)添加文件内容

test-lerna/packages/test1/index.js

1
2
3
4
5
function sum (a, b) {
return Number(a) + Number(b);
}

module.exports = sum;

test-lerna/packages/test1/test.js,运行该脚本确保 test1 内容是正确的。

1
2
3
var sum = require('./index');

console.log(sum(1, 2));

test-lerna/packages/test2/index.js,引入 test1 包并调用它的方法,测试运行完全 ok。

1
2
3
var sum = require('test1');

console.log(sum(1, 2));

5)修改 test1 包

修改 test-lerna/packages/test1/index.js 文件,添加一段打印信息。

1
2
3
4
5
6
function sum (a, b) {
console.log('xixihaha');
return Number(a) + Number(b);
}

module.exports = sum;

重新执行 test2 包下的 index.js,发现也能打印出 xixihaha,都不需要发布 test1 包,test1 修改,及时生效。

6)总结

使用 lerna,不需要频繁的提交底层依赖包,就可看到底层依赖包的修改效果,其机制是创建底层依赖包的快捷方式(linux中叫做软链接)放到集成项目的 node_modules 目录下,由于是快捷方式,当底层依赖包有修改时,快捷方式内的内容也会相应修改。

当然,lerna 要求有依赖关系的包都必须放到 pacakges 目录下,如果受不了这种强制要求,可以跳转方案三。

三、方案二:npm 私有仓库

Npm 公共仓库对于同一个包的同一个版本是不允许重复发布的,这是出于安全方面考虑。

dk 用 react 1.0.0 包运行的程序目前跑的很稳定,如果 Npm 公共仓库能上传相同版本的包,指不定 react 哪个马虎的工作人员提交了个有 bug 的代码,并同时 publish 了 react 1.0.0 这个包,dk 这边的程序就会报错,又无从查起,命名包的版本都一样,为什么昨天晚上跑的还正常,今天跑起来就会报错呢?

有的公司会搭建供内部使用的 Npm 私有仓库,常用的开源软件是 Nexus,感兴趣的可以自行学习一波。私有仓库通过设置可以允许上传同版本的 npm 包。

mail-goods 版本为 1.0.0,现在修改了代码,可以重复 publish mail-goods 1.0.0 这个包,mail-integration 只需要强制更新(包的版本不变,包内容变化)一下 mail-goods 包的内容即可 npm install mail-goods -f

这样做可以解决底层依赖包版本更新太快的问题,但没法解决每次修改底层包都要 publish > install 的繁琐操作。

四、方案三:npm install

npm install <folder> 这里的 folder 是底层依赖包的绝对路径,也就是有盘符的路径,如:’D://work/test1’ 这种。

这种方案底层机制和方案一 lerna 很像,也是创建快捷方式(linux中叫做软链接)放到集成项目的 node_modules 下,较 lerna 灵活的一点是不必按照 lerna 要求有依赖关系的包必须放到 pacakges 目录下,而是可以是任意目录。

dk 实际使用 npm install <folder> 发现有几个需要注意的事项,列举如下:

  • 底层依赖包和继承项目包可能有共同的依赖,如都用到了 react,此时可能会报一个 react 的错误,错误原因是检查有多个版本的 react 包,解决方法为定义别名指定使用集成项目下的 react 依赖(webpack 和 roadhog 构建工具可以设置别名,其它构建工具不清楚)
1
2
3
4
alias: {
// ....
react: path.join(__dirname, 'node_modules', 'react')
},
  • npm install <folder> --no-save,加上 --no-save 不会修改 package.json 和 package-lock.json 文件,你可以分别测试一下不加参数和加参数安装之后 package.json 和 package-lock.json 里的区别。
首页
友链
归档
dkvirus
动态
RSS