资讯专栏INFORMATION COLUMN

了解可执行的NPM包

MingjunYang / 513人阅读

摘要:在源码中也可以看到,在执行之前动态的引入了这些解释器模块。因为认为如果你要使用,那么一定会有对应的依赖,这个模块就是与同级的依赖,也就是说可以放心的进行,大致这样的结构的位置在这里执行脚本以及一个相反的栗子

NPMNode.js的包管理工具,随着Node.js的出现,以及前端开发开始使用gulpwebpackrollup以及其他各种优秀的编译打包工具(大多数采用Node.js来实现),大家都开始接触到一些Node.js,发现了使用NPM来管理一些第三方模块会很方便。
大家搬砖的模式也是从之前的去插件官网下载XXX.min.js改为了npm install XXX,然后在项目中require或者import

当然,NPM上边不仅仅存在一些用来打包、引用的第三方模块,还有很多优秀的工具(包括部分打包工具),他们与上边提到的模块的区别在于,使用npm install XXX以后,是可以直接运行的。

常见的那些包

可以回想一下,webpack官网中是否有过这样的字样:

> npm install webpack -g

> webpack
当然,现在是不推荐使用全局安装模式的,具体原因会在下边提到

以及非全局的安装使用步骤:

> npm install webpack

然后编辑你的package.json文件:

{
  "scripts": {
+    "webpack": "webpack"
  }
}

再使用npm run就可以调用了:

> npm run webpack
以上非全局的方案是比较推荐的做法

不过还可以顺带一提的是在NPM 5.x更新的一个新的工具,叫做npx,_并不打算细说它,但它确实是一个很方便的小工具,在webpack官网中也提到了简单的使用方法_

就像上边所提到的修改package.json,添加scripts然后再执行的方式,可以很简单的使用npx webpack来完成相同的效果,不必再去修改额外的文件。_(当然,npx可以做更多的事情,在这里先认为它是./node_modules/webpack/bin/webpack.js的简写就好了)_

包括其他常用的一些,像ncreate-react-appvue-cli这些工具,都会直接提供一个命令让你可以进行操作。

自己造一个简易的工具

最近面试的时候,有同学的回答让人哭笑不得:

Q:你们前端开发完成后是怎样打包的呢?
A:npm run build

[黑人问号脸.png]。经过再三确认后,该同学表示并没有研究过具体是什么,只知道执行完这个命令以后就可以了。
我本以为这仅仅是网上的一个段子,但没想到真的被我碰到了。_也不知道是好事儿还是坏事儿。。_

从我个人的角度考虑,还是建议了解下你所使用的工具。_至少看下scripts里边究竟写的是什么咯 :)_
P.S. npm scripts中不仅仅可以执行NPM模块,普通的shell命令都是支持的

创建工程

首先的第一步,就是你需要有一个文件夹来存放你的NPM包,因为是一个简单的示例,所以不会真实的进行上传,会使用npm ln来代替npm publish + npm install

随便创建一个文件夹即可,文件夹的名字也并不会产生太大的影响。
然后需要创建一个package.json文件,可以通过npm init来快速的生成,我个人更喜欢添加-y标识来跳过一些非必填的字段。

> mkdir test-util
> cd test-util
> npm init -y
创建执行文件

因为我们这个模块就是用来执行使用的,所以有没有入口文件实际上是没有必要的,我们仅仅需要创建对应的执行文件即可,需要注意的一点是:__与普通的JS文件区别在于头部一定要写上#!/usr/bin/env node__

#!/usr/bin/env node

// index.js
console.log("first util")
注册执行命令

然后就是修改package.json来告诉NPM我们的执行文件在哪:

{
+  "bin": "./index.js"
}

在只有一个bin,且要注册的命令与package.json中的name字段相同时,则可以写成上边那种形式,如果要注册多个可执行命令,那么就可以写成一个k/v结构的参数:

{
  "bin": {
    "command1": "./command1.js",
    "command2": "./command2.js"
  }
}
调用时就是 command1 | command2
模拟执行

接下来我们去找另一个文件夹模拟安装NPM模块,再执行npm ln就可以了,再执行对应的命令以后你应该会看到上边的log输出了:

> cd .. && mkdir fake-repo && cd fake-repo
> npm ln ../test-util

> test-util       # global
first util
> npx test-util   # local
first util

这样一个最简易的可执行包就创建完成了。

npm ln 为 npm link 的简写  
npm ln <模块路径> 相当于 cd <模块路径> && npm ln + npm ln <模块名>
要注意是 模块名__,而非文件夹名, __模块名package.json中所填写的name字段
global 与 local 的区别

因为npm link执行的特性,会将global+local的依赖都进行安装,所以在使用上不太好体现出两者的差异,所以我们决定将代码直接拷贝到node_modules下:

> npm unlink --no-save test-util      # 仅移除 local 的依赖
> cp -R ../test-util ./node_modules/
> npm rebuild

因为绕过了NPM的安装步骤,一定要记得npm rebuild来让NPM知道我们的包注册了bin

这时候我们修改脚本文件,在脚本中添加当前执行目录的输出

#!/usr/bin/env node

- console.log("first util")
+ console.log(process.execPath) // 返回JS文件上层文件夹的完整路径

这时再次执行两种命令,就可以看到区别了。

之所以要提到globallocal,是因为在开发的过程中可能会不经意的在这里踩坑。
比如说我们在开发Node项目时,经常会用到nodemon来帮助在开发期间监听文件变化并自动重启。
为了使用方便,很可能会将预定的一个启动命令放到npm scripts中去,类似这样的:

{
  "script": {
    "start": "nodemon ./server.js"
  }
}
两者混用会带来的问题

这样的项目在你本地使用是完全没有问题的,但是如果有其他的同事需要运行你的这个项目,在第一步执行npm start时就会出异常,因为他本地可能并没有安装nodemon

以及这样的做法很可能会导致一些其它包引用的问题。
比如说,webpack实际上是支持多种语言编写config配置文件的,就拿TypeScript举例吧,最近也一直在用这个。

> webpack --config webpack.config.ts
这样的命令是完全有效的,webpack 会使用 ts 的解释器去执行对应的配置文件

因为webpack不仅仅支持这一种解释器,有很多种,类似CoffeeScript也是支持的。
所以webpack肯定不能够将各种语言的解释器依赖都放到自身的依赖模块中去,而是会根据传入config的文件后缀名来动态的判断应该添加哪些解释器,这些在webpack的源码中很容易找到:

获取配置文件后缀

获取对应的解释器并引入模块注册

根据webpack动态获取解释器的模块interpret来看,.ts类型的文件会引入这些模块:["ts-node/register", "typescript-node/register", "typescript-register", "typescript-require"],但是在webpack的依赖中你是找不到这些的。

在源码中也可以看到,webpack在执行config之前动态的引入了这些解释器模块。

这里也可以稍微提一下Node中引入全局模块的一些事儿,我们都知道,通过npm install安装的模块,都可以通过require("XXX")来直接引用,如果一些第三方模块需要引入某些其他的模块,那么这个模块也需要存在于它所处目录下的node_modules文件夹中才能够正确的引入。

首先有一点大家应该都知道的,目前版本的NPM,不会再有黑洞那样深的node_modules了,而是会将依赖平铺放在node_modules文件夹下。比如说你引入的模块AA的内部引用了模块B,那么你也可以直接引用模块B,因为AB都存在于node_modules下。

还是拿我们刚才做的那个小工具来实验,我们在fake-repo中添加express的依赖,然后在test-util中添加koa的依赖,并在test-util/index.jsrequire上述的两个模块。

你会发现,npx test-util运行正确,而test-util却直接报错了,提示express不存在。

我们可以通过NPM的一个命令来解释这个原因:

> npm root
/node_modules
> npm root -g
/node_modules

这样输出两个路径应该就能看的比较明白了,koa模块是没有问题的,因为都是存在于这些路径下的node_modules,而express则只存在于/node_modules/test-util/node_modules下,全局调用下,require是找不到express的。

# global 下的结构
.
├── /usr/local/lib/node_modules   # npm root 的位置
│   ├── koa
│   └── test-util                 # 执行脚本所处的位置
└──                    # 本地的项目
    ├── node_modules
    │   └── express
    └── .

# local 下的结构
└──                    # 本地的项目
    ├── node_modules              # npm root 的位置
    │   ├── koa
    │   ├── test-util             # 执行脚本所处的位置
    │   └── express
    └── .

所以这也从侧面说明了为什么webpack可以直接在自己的文件中引用并不存在于自己模块下的依赖。

因为webpack认为如果你要使用TypeScript,那么一定会有对应的依赖,这个模块就是与webpack同级的依赖,也就是说webpack可以放心的进行require,大致这样的结构:

├── node_modules    # npm root 的位置
│   ├── webpack
│   └── typescript
└── .               # 在这里执行脚本

以及一个相反的栗子

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98735.html

相关文章

  • 2018 年了,你还是只会 npm install 吗

    摘要:无需手动拷贝文件或者创建软链接到目录,有更优雅的解决方案。这是因为识别协议的,得知这个包需要直接从文件系统中获取,会自动创建软链接到中,完成安装过程。 nodejs 社区乃至 Web 前端工程化领域发展到今天,作为 node 自带的包管理工具的 npm 已经成为每个前端开发者必备的工具。但是现实状况是,我们很多人对这个nodejs基础设施的使用和了解还停留在: 会用 npm insta...

    libxd 评论0 收藏0
  • node入门基础

    摘要:入门是一门基于的后台编程语言,由于其解析引擎为引擎,性能比较强大,再加上与前端语言关系更为密切的先天优势,使其在众多后台编程语言中脱颖而出。那么线程呢,线程,有时被称为轻量级进程,,是程序执行流的最小单元。 node入门 nodejs是一门基于JavaScript的后台编程语言,由于其解析引擎为V8引擎,性能比较强大,再加上与前端语言关系更为密切的先天优势,使其在众多后台编程语言中脱颖...

    wujl596 评论0 收藏0
  • 手把手教你写命令行工具

    摘要:命令行工具,即。我们在写命令行工具的时候,需要指定一个可执行文件。或者四调试我们全局安装一个包后,可以全局调用这个命令行工具。 命令行工具,即 Cli(command-line interface)。是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。在学习这篇教程之前,你需要先了解NodeJs,NPM和一些常用的...

    DevYK 评论0 收藏0
  • [译]npm入门指南

    摘要:博客地址这篇文章是我在众成翻译翻译的一篇文章,一篇的入门指南,原文链接的出现使得用写服务端应用成为可能。你可以看到,这个过程也安装了其他的模块,它们都是的所依赖的模块。但是,得到的输出信息会很冗长,我们可以加上来精简一下输出。 github 博客地址: https://github.com/zengxiaota... 这篇文章是我在 众成翻译 翻译的一篇文章,一篇 npm 的入门指南,...

    mrcode 评论0 收藏0

发表评论

0条评论

MingjunYang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<