主题
发布 npm 包与HTTP 协议(Day8 3/4)
本页对应 Day08_Node,共 6 段视频,约 1 小时 14 分。
学习目标
- 能使用 yarn 完成项目初始化、添加/删除依赖、运行脚本,并说出与 npm 对应命令的区别。
- 能解释 npx 的作用,并在不全局安装的情况下执行本地依赖包中的命令行工具或项目脚本。
- 能按检查 registry、登录、发布、更新版本、清缓存、安装验证的流程发布一个 npm 包。
- 能通过编写可执行脚本、配置 package.json 的 bin 字段,开发并发布可在全局调用的命令。
- 能区分 OSI 七层模型与 TCP/IP 四层模型,说明 HTTP、TCP/UDP、IP 分别所在层级及分层意义。
- 能解释 IP 地址与域名的对应关系,说明 hosts 文件与 DNS 服务器的解析作用,并诊断“能用 IP 访问但域名无法访问”的常见原因。
知识点
本节接着前面 npm、yarn、cnpm 与 npx 的铺垫,真正动手把本地写好的单位转换函数发布到 npm,并进一步把它做成全局命令。发布不是只敲一条 npm publish,而是“确认 registry → 初始化 → 登录 → 发布 → 安装验证 → 更新版本再发布”的完整流程。
发布前先确认 registry
为什么需要:发布包必须发到 npm 官方仓库;如果当前 registry 被改成淘宝镜像,淘宝镜像只被动同步官方包,不接受个人发布,npm publish 会失败。怎么想:先查当前镜像地址,再决定是否登录和发布。怎么做:运行:
bash
npm config get registry确认是官方地址后再继续;不是就改回官方 registry。易错点:转写稿里特意先查 registry,就是怕前面配过 cnpm/淘宝镜像导致发布不成功;不要一上来就 npm publish。
初始化并发布普通包
为什么需要:npm 只识别带 package.json 的包,没有它连包名、版本、入口都没有。怎么想:先把本地函数整理成可被 require 的模块,再初始化。怎么做:在包目录执行 npm init,入口写 index.js,包名不能和已有包重名。转写稿中包名类似 convert-byte,先发布 1.0.0:
bash
npm login
npm publish登录前在 npm 网站注册账号;命令行登录时可能要求邮箱验证码。发布成功后在网站搜索可能有延迟,不一定立刻搜到。易错点:包名重名发布不成功;npm publish 不要拼成 pub list;发布后在另一个项目里 npm install convert-byte 测试,不要在包目录里自测。
把包升级为全局命令
为什么需要:普通包只能被 require,想让命令行直接敲 bytec 3286 3,就要把包做成全局命令。怎么想:全局命令的本质是 package.json 的 bin 字段指向一个可执行脚本,脚本第一行写 shebang,让系统用 Node 执行。怎么做:新建 exec.js:
js
#!/usr/bin/env node
const convertByte = require('./index.js')
const bytes = process.argv[2]
const unit = process.argv[3]
console.log(convertByte(bytes, unit))package.json 增加:
json
{
"name": "convert-byte",
"version": "1.1.0",
"main": "index.js",
"bin": {
"bytec": "exec.js"
}
}先本地调试:node exec.js 3286 3。process.argv 是内置对象,索引 0 是 node 路径,1 是脚本路径,2、3 才是参数。易错点:bin 里直接写 exec.js,不要写 node exec.js;转写稿中就是因为写成 node exec.js,全局命令没有正确生成。shebang 必须放第一行。
版本更新、缓存与安装验证
为什么需要:npm 不允许同一版本重复发布;改了代码必须改 version。怎么想:普通包发布后再做全局命令,属于一次功能升级,要升版本。怎么做:转写稿中第一次改全局命令直接发布失败,因为版本还是 1.0.0;改成 1.1.0 才成功。后来修正 bin 写法,再升到 1.1.1 发布。测试新版本前清缓存:
bash
npm cache clean --force
npm install -g convert-byte
bytec 3286 3易错点:本地有缓存时可能装不到刚发布的新版本;全局命令要 npm install -g 后才会进入 PATH。转写稿中还发现包名与命令名不同,npx 没找到对应命令,最后用全局安装验证。发布全局命令后,若别人同步项目,仍要按项目依赖方式安装;全局命令适合本机命令行使用。
本节定位:HTTP 是应用层协议
本节笔记原文是:
HTTP(hypertext transport protocol)协议;中文叫 超文本传输协议,是一种基于TCP/IP的应用层通信协议,这个协议详细规定了
浏览器和 万维网服务器之间互相通信的规则。协议中主要规定了两个方面的内容:
- 客户端:用来向服务器发送数据,可以被称之为 请求报文
- 服务端:向客户端返回数据,可以被称之为 响应报文
本节笔记未附图片和代码,因此下面不虚构插图或代码,只把笔记中的概念和转写稿里的推导、调试过程讲清楚。
为什么需要:浏览器要向服务器要网页、图片、接口数据,服务器要把结果返回浏览器。双方如果没有统一规则,数据格式、发送顺序、接收方式都会乱。
怎么想:HTTP 不是孤立存在的,它建立在 TCP/IP 之上,位于应用层。转写稿用“寄包裹”类比:写信封有写信封的规则,运输有运输的规则,道路有道路的规则;每一层只管本层协议。
怎么做:学习时先抓住一句话:HTTP 规定浏览器与万维网服务器之间的通信规则,核心是“客户端发请求报文,服务端回响应报文”。
易错点:不要把 HTTP 说成传输层协议;HTTP 是基于 TCP/IP 的应用层通信协议。转写稿中口误的“绘画层”“表述层”,应纠正为“会话层”“表示层”。
网络分层:HTTP 在最高层
为什么需要:网络通信涉及网线、无线、IP 寻址、TCP/UDP 传输、浏览器应用等多个环节,不分层就无法分工。
怎么想:转写稿介绍了 OSI 七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层;实际常用的是 TCP/IP 四层模型,把 OSI 上三层合并为应用层,最下面两层合并为网络接口层。HTTP 在应用层,TCP/UDP 在传输层,IP 在网络层。
怎么做:作为前端或后端开发者,重点关心应用层的 HTTP;底层协议知道位置和职责即可。
易错点:不要因为 HTTP 很常用,就以为网络里只有 HTTP。转写稿明确说 HTTP 只是众多网络协议中的一种。
请求报文与响应报文:HTTP 规定的两类数据
为什么需要:客户端和服务器必须知道“谁先发、发什么、怎么回”。
怎么想:把一次访问拆成两个方向:浏览器向服务器发送的是请求报文;服务器向浏览器返回的是响应报文。笔记把这两方面作为 HTTP 的主要内容。
怎么做:记忆时用“客户端→请求报文,服务端→响应报文”对应。后续学习报文字段时,再展开请求行、请求头、响应状态等细节;本节先守住两类报文的划分。
易错点:不要把请求报文和响应报文的方向说反;不要把“客户端”只理解成某一种浏览器,笔记中的客户端是向服务器发送数据的一方。
从域名到服务器:IP、域名与 DNS 的铺垫
为什么需要:浏览器访问网站,本质上要找到服务器;服务器在网络中有 IP 地址。但 IP 不好记,所以需要域名。
怎么想:转写稿用 ping 演示:输入域名后能看到对应 IP,说明最终仍要靠 IP 找到服务器;域名只是更好记的名字。域名和 IP 的对应关系由 DNS 服务器保存和查询。
怎么做:Windows 下可用 ipconfig 查看本机 IP;用 ping 观察域名解析结果;本机 hosts 文件也可配置本地映射。转写稿中演示了打开 hosts,并说明它只影响本机。DNS 服务器地址通常在网络配置中自动获取。
易错点:hosts 不是全世界的域名表;DNS 配错时,浏览器输入域名可能打不开,但微信、QQ 等已建立连接或走其他方式的应用仍可能发消息。转写稿中“DS服务器”“DX服务器”应纠正为 DNS 服务器。
调试与排错:先分清是 HTTP 还是网络解析问题
为什么需要:页面打不开时,原因可能在域名解析、IP 连通性,也可能在 HTTP 请求响应。
怎么想:先用 ipconfig 看本机网络配置,再用 ping 看域名能否解析到 IP;如果域名解析异常,先查 DNS 和 hosts,不要一上来就怀疑 HTTP 报文。
怎么做:按“本机 IP → 域名解析 → 服务器连通 → HTTP 请求响应”的顺序排查。
易错点:不要把“浏览器打不开网页”直接等同于“HTTP 协议写错了”;转写稿的例子说明,DNS 地址错误也会造成浏览器访问失败。
课堂画面






代码示例
示例一:把工具函数做成一个 npm 包(project04-convert-byte)
js
// package.json —— 用 npm init 生成后确认这几项
{
"name": "convert-byte", // 包名,全网唯一,不能和别人重名
"version": "1.0.0", // 每次发布前必须改,否则 publish 失败
"description": "字节单位转换",
"main": "index.js", // 入口文件,别人 require 的就是它
"scripts": {},
"author": "",
"license": "ISC"
}js
// index.js —— CommonJS 方式暴露(require 使用)
function convertByte(bytes, unit) {
// unit:1 -> KB,2 -> MB,3 -> GB
return bytes / Math.pow(1024, unit);
}
module.exports = convertByte;js
// index.esm.js —— ES6 模块方式暴露(import 使用)
export default function convertByte(bytes, unit) {
return bytes / Math.pow(1024, unit);
}要点:
npm init时包名默认取目录名(会变成project04-convert-byte),实际中是手动改成convert-byte的,名字必须全小写、且全网不重名。main指向入口文件,没写对别人require就会拿不到东西。- 同一份功能准备 CJS 和 ESM 两套暴露方式,是为了让
require和import的人都能用(转写稿:“你喜欢用 CommonJS 就 require,喜欢 ES6 就 import”)。 - 易错点:
package.json都没有就npm publish,等于没东西可发;发布前先npm config get registry确认是官方源,如果被换成淘宝镜像,包是发不上去的(淘宝镜像只单向同步官方)。
示例二:在另一个项目里安装并使用自己发的包(project01)
js
// 先在 project01 目录下执行:npm install convert-byte
// 安装成功后它会出现在 package.json 的 dependencies 里
const convertByte = require('convert-byte'); // 引入刚发布的包
const bytes = 1024 ** 3 * 3.21; // 课堂演示用的那个字节数
// 转成 MB(单位 2)后四舍五入 —— 结果约 3286
console.log(Math.round(convertByte(bytes, 2)) + 'MB');
// 转成 GB(单位 3)后保留两位小数 —— 结果 3.21GB
console.log(convertByte(bytes, 3).toFixed(2) + 'GB');要点:
- 开发目录和使用目录要分清:包在
project04-convert-byte里开发,在project01里安装测试,别在包自己的目录里require自己。 Math.round()对结果“有点残忍”(3286 那种大整数),课堂最后改成.toFixed(2)保留两位小数,输出3.21GB更友好。- 易错点:刚发布完就立刻
npm install可能装到缓存里的旧版本,需要先npm cache clean --force强制清缓存再装。 - 易错点:包名和别人重名是发不出去的,就像 App Store 不允许重名应用一样。
示例三:给包加一条全局命令(exec.js + bin 配置)
js
// exec.js —— 全局命令真正执行的脚本
#!/usr/bin/env node // 固定第一行,写了它才能在命令行里被执行
const convertByte = require('./index.js'); // 引用包内部的模块
// process.argv 是内置对象,不用 require
// [ node 程序路径, 脚本路径, 参数1, 参数2 ] —— 共 4 个元素
const bytes = process.argv[2]; // 要转换的字节数
const unit = process.argv[3]; // 转换单位:1 KB / 2 MB / 3 GB
console.log(convertByte(bytes, unit) + (unit == 1 ? 'KB' : unit == 2 ? 'MB' : 'GB'));json
// package.json 里增加 bin 字段(与 name、version 同级)
{
"name": "convert-byte",
"version": "1.1.1",
"main": "index.js",
"bin": {
"bytec": "exec.js"
}
}bash
# 改完代码要重新发布,版本号必须先改(1.1.0 -> 1.1.1)
npm publish
# 清缓存后再全局安装,否则装到的还是老版本
npm cache clean --force
npm i convert-byte -g
# 安装完就多了一条和 node、npm、yarn 平级的命令
bytec 3286 3要点:
bin的值只能写文件名(如exec.js),实际中写成node exec.js就导致命令没被生成出来 —— 这是最容易踩的坑。exec.js第一行#!/usr/bin/env node是固定写法,没有它,命令敲下去不会执行脚本内容。process.argv前两个元素是 node 路径和脚本路径,真正传进来的参数从索引 2 开始取,索引 1 取会拿到脚本路径。- 发布过 1.0.0 之后再
npm publish必须改version,不改会直接报错(“版本号也得改呀”)。 - 包名(
convert-byte)和命令名(bytec)不一致时,npx找不到对应命令,只能npm i -g全局安装后使用 —— 想让npx自动兜底下载,命令名要和包名保持一致。
常见错误
- 现象:
npm publish报权限/仓库错误;原因:当前 npm 镜像仍指向淘宝等非官方源;修法:发布前执行npm config get registry,切回官方源后再登录、发布。 - 现象:修改代码后再次
npm publish失败;原因:package.json版本号未更新;修法:按语义化版本修改version后再发布。 - 现象:全局命令安装后无法识别,或
npx找不到命令;原因:bin字段配置错误,如命令名与包名混淆、脚本路径写错、误加node;修法:核对bin的命令名与脚本相对路径,发布前本地验证。 - 现象:刚发布新版本,安装端仍拿到旧版本;原因:npm 本地缓存未更新;修法:执行强制清缓存命令后重新安装/更新。
- 现象:包名发布失败;原因:npm 上已存在同名包;修法:更换唯一包名,发布前先查询名称是否可用。
- 现象:本地依赖中的命令行工具直接运行失败;原因:该工具未全局安装,也未通过
npx或脚本调用;修法:用npx执行,或写入package.jsonscripts 后通过npm run调用。
练习
- 写出发布一个 npm 包前需要检查的 4 项内容,并说明每项检查的目的。
- 某包已在
package.json中配置了bin,但安装后命令不可用,请列出至少 3 个排查点。 - 模拟一次“修改包代码→发布新版本→在使用项目中更新验证”的完整命令流程。
- 在项目本地安装了一个命令行工具,请分别用
npx和package.jsonscripts 两种方式调用它。 - 浏览器无法打开网页但 QQ/微信可发消息,请写出判断思路和验证命令/配置项。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | yarn命令 | 05:38 |
| 2 | npx命令 | 08:59 |
| 3 | 发布包 | 12:40 |
| 4 | 发布全局命令 | 20:30 |
| 5 | 网络协议介绍 | 12:22 |
| 6 | IP地址和域名 | 13:52 |
小结
- 发布 npm 包的核心前置条件是:包结构完整、
package.json正确、镜像源正确、账号已登录。 - 版本号是发布更新的硬门槛,代码变了但版本没变,发布不会成功。
- 开发全局命令的关键是“脚本文件 +
bin配置 + 发布安装验证”,任何一环出错都会导致命令不可用。 npx解决的是本地依赖中命令行工具的执行问题,不是 npm 的替代品。- yarn、cnpm、npm 可基于同一
package.json切换使用,但发布相关操作必须关注源地址。 - 缓存会导致“新版本已发布但本地安装不到”的假象,更新验证时要考虑清缓存。
- HTTP 属于应用层协议,前端开发重点掌握应用层规则,底层协议只需建立分层常识。
- 域名访问依赖 DNS 解析;能发消息但浏览器打不开网页时,应优先排查 DNS 配置。