npm5中本地间模块引用的最好方式(附带引用方法总结)

news2024/10/7 3:12:39

引用其他的包

正常情况下在项目 package.json 所在的目录(一般也是项目根目录)运行npm install xxxx 命令之后,会从远程或者代理地址下载xxxx包到node_modules,然后在package.json生成对应的包名和版本

如果想要依赖本地自己开发的一个包或者更改别人的包放到本地维护需要怎么处理呢?

如果跟项目package.json平级的目录有一个文件夹 yuiopp,在yuiopp里面用 npm init -y 初始化了这个文件夹, 这个文件夹就会有一个package.json文件。

在这里插入图片描述
 在根目录 npm install yuiopp就会将这个文件夹下载到 node_modules下面,根目录下的 package.json 会生成对应的文件路径,如下图:
 
在这里插入图片描述

红色框对应的本地维护的包,蓝色框根目录下的package.json

注意:此时npm install 对应的文件夹名,install的是文件夹名称,install包名会从远程寻找,如果本地包名和文件夹名不一样,对应的package.json显示如下:

sdddddd 对应的包名称,yuiopp 对应的文件夹名称, 运行的命令是 npm install yuiopp

有些场景我们本地会维护很多包但是不希望都创建一个文件夹,希望放到统一的文件夹里面,比如 根目录新建一个lib文件夹,这个时候如果想要下载本地包,需要手动将依赖更改成对应的路径,运行npm i命令便可以
在这里插入图片描述

那如果我们引用的模块在当前模块的上层文件夹里呢?是和node的包查找机制一样在node_modules里查找吗?

肯定不是的。见下文(其实就是npm install 的时候使用 相对路径 …/…/等)


对于某些比较大的项目,我们可能会希望将其划分成多个模块:

  • module1

    • package.json

    • libs

      • file1.js
  • module2

    • package.json
    • index.js

假如我们要在 module2/index.js 中直接引用 file1.js,可能就会写成这样 require('../module1/libs/file1')。这样写并不好,因为:

  1. 有时候引用方的文件处在很深的路径下,那么引用地址就得写成 ../../../module1/libs/file1,体验很不好。
  2. 既然我们将项目划分成了多个模块,肯定是想要降低模块之间的偶合度。这样直接引用文件的做法相当于直接深入到了模块的内部,没有将模块之间隔离出来,低耦合更无从谈起了。

解决 2 号问题最好的做法是提供一个单独的文件作为模块的接口。

比如上文的文件结构,我们可以变成这样:

  • module1

    • package.json

    • index.js

    • libs

      • file1.js
  • module2

    • package.json
    • index.js

module1/index.js 中把我们希望在本模块中暴露出来的函数或类 export 出来,这样的话就相当于给模块提供了一个接口。其他模块只能通过 index.js 提供的内容来访问。假如以后需要重构 module1,将 file1.js 拆成两个文件,只需要改动一下 module1/index.js即可,不需要修改其他的模块。

现在我们的引用可以写成 require('../module1') 了,本文章的剩余内容就来讨论一下怎样解决 1 号问题——去掉前面的..

1. 使用 npm install ../module1

我们可以把 module1 作为依赖安装给 module2。这样的话,module2 在运行的时候就可以把 module1 当成一个普通的依赖,使用 require('module1') 来引入了。这个方法的问题在于要安装的 module1 必须在 package.json 文件中有 version 字段,否则无法安装成功。对于一个应用程序(而不是类库)而言,每次修改都需要更新版本还是太麻烦了。而且 npm5 的安装会直接创建一个软链接,会引发更多的问题(我下文要说)。

2. 直接修改 package.json 字段,增加依赖

这种方法也很有意思。我们可以直接在 module2/package.json 的 dependencies 中增加一个 "module1": "file:../module1" 。这样 npm5 在安装依赖的时候会自动创建相应的软链接,自动给 module1 安装依赖,而且不需要版本号。但这个特性跟 npm5 的 package-lock 结合起来就出问题了。

对于进行过这些修改的 module2,在安装依赖的时候会把 module1 的 package-lock 也合并进 module2/package-lock.json,再一次使用 npm install 的时候就会提示 enoent ENOENT: no such file or directory。我也不知道这是为什么。不过 npm5 已经准备放弃直接安装文件夹的功能而改用 npm link了,这些问题以后肯定也不会修复了。

3. 使用 install-local

这玩意跟解决方法 1 类似,只不过在 npm5 里面也能直接拷贝文件夹而不是创建软链接。这么做的话还是需要 version 字段,而且对于 module1 的改动不会直接反应到 module2 上,还需要像更新其他模块那样手动更新。

4. 使用 npm link

这个比较神奇。我们可以在 module1 目录下运行 npm link,然后在module2目录下运行npm link module1。这样的话,npm 就会自动在 module2 的 node_modules 目录下创建一个软链接,我们拥有了类似于方法 2 的效果,并且不会修改 package-lock。

但是它也不会修改 package.json。也就是说我们拿到项目源代码之后不能直接用 npm i 来安装所有依赖,还必须得加一步 npm link 才行,还是太麻烦了。

5. 使用 require-rewrite

这个方法解决了上述的所有问题。我们只需要配置一下 module2 的 package.json,加入 module1 的路径,这样就能使用require('module1')来引入模块了。

不过,这个方法又引入了两个新的问题:

  1. 引入的时候得多写一步。我们必须要在 require('module1') 前面写上 require('require-rewrite')(__dirname) 才能在本文件中引入此模块。
  2. 静态代码分析失效了。如果你用 webstorm 的话,它显然是没法通过重写过的路径来找到 module1 的实际路径的,那基于此的代码补全也就失效了。

6. 使用 postinstall 钩子手动创建软链接

我个人比较推荐这种做法。

我们可以在 module2 的 package.json 的 script 字段中新建一个:

"postinstall": "node -e "var s='../module1',d='./node_modules/module1',fs=require('fs'), r=require('path').resolve;fs.exists(d,function(e){e||fs.symlinkSync(r(s),r(d),'junction')});""

它的命令名称为 postinstall,使得这个命令会在 npm install 安装结束之后自动执行。它执行一段 node 脚本来自动创建链接,起到了跟 npm link 相似的效果。只要把其中的 module1 更换成你自己的模块路径即可。注意其中的 junction,这个参数只在 windows 下才有效果,*Unix 下这个参数是什么都没有关系。junction 代表创建目录链接。

其实这个解决办法还是有两个小问题,不过对于我而言还能接受。

不会自动安装依赖的依赖

在 module2 下执行 npm install 并不会自动给 module1 安装依赖。

在 root 权限下执行会有问题

默认情况下,在 root 权限下执行 npm install 会阻止 postinstall 脚本运行。我们可以使用普通权限来安装或者使用 npm install --unsafe-perm 来安装。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TypeScript 学习笔记(一):类型

文章目录 一、常见类型1. 数组2. 布尔3. 数值4. 字符串5. object6. null 和 undefined7. symbol7.1 作为属性名7.2 属性名遍历7.3 静态方法:Symbol.for()和 Symbol.keyFor()7.4 内置 symbol 值7.4.1 Symbol.hasInstance7.4.2 Symbol.isConcatSpreadable7.4.3 Symbol…

第四十五章Java 接口

Java 接口 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。 接口并不是类&#x…

DevOps(二)

CD 1. 平台选择2. 技术选型3. 阶段性目标4. 搭建示例4.1 环境准备(节点机)1. java版本升级2. 编译安装git3. docker安装4. docker-compose安装5. sonarqube安装6. harbor安装7. gitlab私服 4.2 示例一(手动)1. 创建项目2. 编码3. Dockerfile4. 拷贝pytho…

【linux】线程详解

线程 线程的概念 在官方书籍对于线程的概念: 1.在进程内部的执行流 2.线程比进程粒度更细,调度成本更低。 3.线程是CPU调度的最小单位。 线程(tcb):进程(pcb) n:1 进程和线程在执…

Java 动态规划 面试题 17.16. 按摩师

代码展示: class Solution {public int massage(int[] nums) {int nnums.length;if(n0){return 0;}//创建数组int f[]new int[n]; //f[i]表示接i位置的最长时间int g[]new int[n]; //g[i]表示不接i位置的最长时间//初始化f[0]nums[0];g[0]0;//填充数组for(int i1;i…

VectorCAST单元测试手动配置测试用例

一、单元测试 等待环境创建完成后,就可以开始单元测试。 二、生成测试用例 在 VectorCAST 中,一共有两种方法来生成测试用例,一种是手动生成测试用例,另外一种是自动 生成测试用例。 三、手动生成测试用例 在 VectorCAST 中&a…

《面试1v1》Redis分布式锁

🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结&#xf…

泛微E-Cology SQL注入漏洞复现(QVD-2023-15672)

0x01 产品简介 泛微协同管理应用平台e-cology是一套兼具企业信息门户、知识文档管理、工作流程管理、人力资源管理、客户关系管理、项目管理、财务管理、资产管理、供应链管理、数据中心功能的企业大型协同管理平台。 0x02 漏洞概述 由于泛微e-cology未对用户的输入进行有效的…

matlab学习指南(2):安装工具箱Toolbox的方法(详细图解)

🌅*🔹** φ(゜▽゜*)♪ **🔹*🌅 欢迎来到馒头侠的博客,该类目主要讲数学建模的知识,大家一起学习,联系最后的横幅! 喜欢的朋友可以关注下,私信下次更新不迷路&#xff0…

【项目 进程1】2.1 进程概述 2.2 进程状态转换

文章目录 2.1进程概述程序和进程**时间片****并行和并发****进程控制块(PCB)** 2.2进程状态转换**进程的状态** **进程相关命令****实时显示进程动态** 2.1进程概述 程序和进程 程序是包含一系列信息的文件,这些信息描述了如何在运行时创建一个进程: …

Linux的软链接与硬链接

Linux的软链接与硬链接 1,创建硬链接:2,创建软链接:3,软链接是什么4,软链接文件的权限5,硬链接是什么6,做个小实验 总结问题:为什么有软链接了(快捷方式&…

Centos7.9通过expect脚本批量修改H3C交换机配置

背景: 公司有几百台H3C二层交换机设备,当需要批量更改配置时非常的消耗工作量 解决: 通过一台Linux服务器,编写shell脚本,模拟Telnet至各台交换机,让一切变的很容易 1.首先在安装Telnet服务前需要检测centO…

Java基础(动力节点课程)

JavaSE基础——第一章初识Java JavaSE JavaEE JavaMEJavaSEJavaEEJavaME Java语言跨平台性垃圾回收机制Java的加载和执行JDK、JRE、JVM关系安装JDK以及配置PATHJDK目录说明 第一个Java程序javac命令和java命令的具体用法javac命令的用法:java命令的用法:…

Transformer网络学习记录——基于空间约束自注意力和Transformer的RGB-D显著性检测方法研究

基于图半监督学习和图卷积的目标分割与跟踪算法研究 (wanfangdata.com.cn) 只能说看不懂,记录是为了有耐心慢消化 原文: 网络整体为通用的编码器-解码器架构 ,总体上由骨干编码器、交互编码器、RGB 解码器、深度解码器组成。 具体来说&#…

ROCKSDB原理

按照读写的性质来分 分为读少写多和 写少读多 RocksDB适用于第一种。 磁盘中的数据结构 就地写和追加写 找到某一个页 然后将数据刷入到这一个页中. 这就导致了一个问题 就是追加写入的数据冗余 由于存在数据冗余 所以必须要对数据进行一定的处理才能保持查找性能 数据以块…

langchain系列:Model I/O模块之-Prompts

文章目录 Model I/O简介输入部分(Prompts)PromptTemplatefrom_template ChatPromptTemplate langchain是基于大语言模型而开发的一个框架,既然是基于大语言模型,自然最重要的就是先要介绍Model I/O模块。 Model I/O简介 Model I/O…

Helm3安装和使用

Helm3安装和使用 1、Helm简介 Helm 是 Kubernetes 上的包管理器,用来管理 Kubernetes 应用程序,Helm Charts 可帮助您定义,安装和升级 复杂的 Kubernetes 应用程序。Helm 把 Kubernetes 资源(比如deployments、services或ingress等) 打包到…

ProtoBuf的学习并且制作了一个网络通讯录项目

Linux环境下载安装ProtoBuf编译器 1. 安装依赖库 Ubuntu用户选择 sudo apt-get install autoconf automake libtool curl make g unzip -yCentos用户选择 sudo yum install -y autoconf automake libtool curl make gcc-c unzip2. 下载ProtoBuf编译器 Github地址&#xff…

kmalloc与vmalloc如何选择

kmalloc和vmalloc都是Linux内核中用于内存分配的函数,但它们适用于不同的内存分配场景。 kmalloc函数用于在内核空间中分配小块(通常小于一个页面大小)的连续内存区域,这些内存区域可以用于存储内核数据结构和缓冲区等。kmalloc内…

MySQL与Oracle的粗略对比

前言 首先先说自己的感受,我第一次使用Oracle是在我第一次的实习当中,包括我也在Leetcode中做了一些题目来练习,大家也可以做做,还是有收获的。 首先,我之前一直听说Oracle是要付费的,但其实它有免费版&am…