了解可执行的NPM包
NPM
是Node.js
的包管理工具,随着Node.js
的出现,以及前端开发开始使用gulp
、webpack
、rollup
以及其他各种优秀的编译打包工具(大多数采用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
的简写就好了)
包括其他常用的一些,像n
、create-react-app
、vue-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文件上层文件夹的完整路径
这时再次执行两种命令,就可以看到区别了。
之所以要提到global
与local
,是因为在开发的过程中可能会不经意的在这里踩坑。
比如说我们在开发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
文件夹下。比如说你引入的模块A
,A
的内部引用了模块B
,那么你也可以直接引用模块B
,因为A
和B
都存在于node_modules
下。
还是拿我们刚才做的那个小工具来实验,我们在fake-repo
中添加express
的依赖,然后在test-util
中添加koa
的依赖,并在test-util/index.js
中require
上述的两个模块。
你会发现,npx test-util
运行正确,而test-util
却直接报错了,提示express
不存在。
我们可以通过NPM
的一个命令来解释这个原因:
> npm root
<current>/node_modules
> npm root -g
<global>/node_modules
这样输出两个路径应该就能看的比较明白了,koa
模块是没有问题的,因为都是存在于这些路径下的node_modules
,而express
则只存在于<current>/node_modules/test-util/node_modules
下,全局调用下,require
是找不到express
的。
# global 下的结构
.
├── /usr/local/lib/node_modules # npm root 的位置
│ ├── koa
│ └── test-util # 执行脚本所处的位置
└── <workspace> # 本地的项目
├── node_modules
│ └── express
└── .
# local 下的结构
└── <workspace> # 本地的项目
├── node_modules # npm root 的位置
│ ├── koa
│ ├── test-util # 执行脚本所处的位置
│ └── express
└── .
所以这也从侧面说明了为什么webpack
可以直接在自己的文件中引用并不存在于自己模块下的依赖。
因为webpack
认为如果你要使用TypeScript
,那么一定会有对应的依赖,这个模块就是与webpack
同级的依赖,也就是说webpack
可以放心的进行require
,大致这样的结构:
├── node_modules # npm root 的位置
│ ├── webpack
│ └── typescript
└── . # 在这里执行脚本
以及一个相反的栗子
了解可执行的NPM包的更多相关文章
- npm包与gem包--在线&离线安装
目录 NPM 在线 离线 GEM 在线 离线 NPM NPM,即为Node的包管理工具,官网为 https://www.npmjs.com/,我们可以在站内搜索所需要的NPM包,了解相关的使用规则 安 ...
- 记npm包开发全过程
概述 为什么开发npm包? 如何开发? 如何写单元测试? package.json 如何发布模块? 如何使用? 为什么开发npm模块? NPM的全称是Node Package Manager,是一个N ...
- node.js零基础详细教程(3):npm包管理、git github的使用
第三章 建议学习时间4小时 课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑 ...
- 如何自己写一个公用的NPM包
以markdown-clear,创建过程为例,讲解整个NPM包创建和发布流程 1 如何创建一个包 1.1 创建并使用一个工程 在GitHub上新建一个仓库,其名markdown-clear clone ...
- 【转】npm包管理器那些事
原文链接:http://www.cnblogs.com/shuoer/p/7782125.html npm包管理器那些事! 今天和朋友针对npm包全局安装和本地项目安装这个梗展开的激烈的讨论,故此做一 ...
- 【vue系列之三】从一个vue-pdf-shower,说说vue组件和npm包
前言 从去年年初开始,自己便下决心要写一个vue系列的博客,但时至今日,才写系列的第三篇博客,想来甚是惭愧. 但是慢归慢,每一篇都要保证质量,以及要写出自己的心路历程,防止自己工作中填的坑再让读者走一 ...
- 如何发布第一个属于自己的npm包 到公网
发布前的准备 1. 注册一个npm账号 前往NPM官网进行注册 2. 创建一个简单的包 在本地创建一个项目文件夹 mannymu_demo (名字自己取,不要和NPM上已有的包名重复冲突就好)然后通过 ...
- 如何发布一个npm包(基于vue)
前言:工作的时候总是使用别人的npm包,然而我有时心底会好奇自己如何发布一个npm包呢,什么时候自己的包能够被很多人喜欢并使用呢...今天我终于迈出了第一步. 前提:会使用 npm,有 vue 基础, ...
- 编写一个供浏览器端使用的NPM包
此文已由作者吴维伟授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 在编写程序时,总会有一些代码是我们不愿意一遍又一遍重复地去写的,比如一些UI或交互相似组件,或是一些相似的流 ...
随机推荐
- spring静态代理和动态代理
本节要点: Java静态代理 Jdk动态代理 1 面向对象设计思想遇到的问题 在传统OOP编程里以对象为核心,并通过对象之间的协作来形成一个完整的软件功能,由于对象可以继承,因此我们可以把具有相同功能 ...
- a标签的使用
a标签可以用来跳转页面请求路径,也可以用来绑定事件. 在绑定事件的时候,我需要控制a标签,不让他进行跳转. 这时候我要做的就是限制他,不让他跳转. 只需要在href属性中加入javascript:vo ...
- EOS之记事本智能合约
EOS记事本智能合约 本次分享的内容是一个记事本合约,调用合约中的写入动作可以将文本和作者保存到数据库中,通过记事本合约来学习EOS智能合约数据存储当中的主键自增. 合约动作 写入动作 记事本合约必须 ...
- Windows单机最大TCP连接数的问题
本文和大家分享一下Windows下单机最大TCP连接数,因为在做Socket 编程时,我们经常会要问,单机最多可以建立多少个 TCP 连接,本文将介绍如何调整系统参数来调整单机的最大TCP连接数. W ...
- 如何修改discuz论坛的图像地址
今天帮别人修改discuz论坛,遇到一个问题,就是图像显示不出来,按F12键后,发现是自己的图像路径设置有问题,于是就要去修改这个设置路径了.有两种方法: 一,直接修改配置文件,打开config/co ...
- LeetCode算法题-Binary Tree Paths(Java实现-3种解法)
这是悦乐书的第199次更新,第206篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第62题(顺位题号是257).给定二叉树,返回所有根到叶路径.例如: 输入: 1 / \ ...
- 【算法】LeetCode算法题-Search Insert Position
这是悦乐书的第152次更新,第154篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第11题(顺位题号是35).给定排序数组和目标值,如果找到目标,则返回索引. 如果没有, ...
- Linux 小知识翻译 - 「邮件服务器」
这次聊聊「邮件服务器」. 邮件服务器上通常会运行2个服务端软件,「SMTP服务器」和「POP服务器或者IMAP服务器」. 这2个东西,也许使用邮件客户端的人立马就明白了.因为设置邮件客户端的时候,需要 ...
- CISCO 动态路由(OSPF)
OSPF(开放式最短路径优先):是一个内部网关协议(Interior Gateway Protocol,简称IGP),用于在单一自治系统(autonomous system,AS)内决策路由.是对链路 ...
- DBUtils温习1
1.简介 Commons DBUtIls是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的简单封装,学习成本极低,但是使用DBUtils却极大的简化了dao层的开发,少些了很多的jdb ...