一、写在前面
为了统一 Javascript 在浏览器之外的实现,CommonJS 诞生了,CommonJS 试图定义一套普通应用程序使用的 API,从而填补 JavaScript 标准库过于简单的不足。 CommonJS 规范包括了模块、包、系统、控制台、文件系统、单元测试等部分。
Node.js 是目前 CommonJS 规范最热门的一个实现,随着 CommonJS 规范的更新,Node.js 也在不断跟进 ,但 Node.js 并不完全遵循 CommonJS 规范。这是所有规范制定者都会遇到的尴尬局面,因为规范的制定总是滞后于技术的发展。
二、node模块化
模块(Module)和包(Package)是 Node.js 最重要的支柱,开发一个具有一定规模的程序不可能只用一个文件,通常需要把各个功能拆分、封装,然后组合起来,模块正是为了实现这种方式而诞生。
在浏览器 JavaScript 中,脚本模块的拆分和组合通常使用 HTML 的 script 标签来实现。Node.js 提供了 require 函数来调用其他模块,而且模块都是基于文件的,换句话说就是每个文件都是一个模块,机制十分简单。
模块类型
nodejs的模块可以分为两类,一类是核心模块,另一类是文件模块。
核心模块是 nodejs 官方提供的,node 进程启动时,部分核心模块就被直接加载进内存中, 所以这部分核心模块我们就可以直接通过require 进行引入。比如 http 模块、url 模块、fs 模块等。
const http = require('http'); // 内置模块直接引用
const host = '127.0.0.1';
const port = 3000;
const server = http.createServer((req,res) => {
res.statusCode = 200;
res.end('test1');
})
server.listen(port, host, () => {
console.log(`服务器运行在 http://${host}:${port}/`)
})
文件模块是在运行时动态加载,需要完整的路径分析、文件定位、编译执行过程、速度相比核心模块稍微慢一些, 但是用的非常多。文件模块的加载方式有两种,一种是按路径加载,另一种是查找node_modules
文件夹。
按路径加载模式
// custom_module文件夹下的tools文件
function calculate(num) {
return num*2
}
function getUrl(domain) {
return `${domain}/wanwan0306`
}
module.exports = {
calculate,
getUrl
}
// app.js
const http = require('http');
const { calculate, getUrl } = require('./custom_module/tools'); // 按路径引入
const host = '127.0.0.1';
const port = 3100;
const server = http.createServer((req,res) => {
const num = calculate(20);
const url = getUrl('https');
res.statusCode = 200;
res.end(`${num}-${url}-test2`);
})
server.listen(port, host, () => {
console.log(`服务器运行在 http://${host}:${port}/`)
})
node_modules文件夹加载模式
// node_modules文件夹下的tools文件
function calculate(num) {
return num*2
}
function getUrl(domain) {
return `${domain}/wanwan0306`
}
module.exports = {
calculate,
getUrl
}
// app.js
const http = require('http');
const { calculate, getUrl } = require('tools'); // node_modules
const host = '127.0.0.1';
const port = 3200;
const server = http.createServer((req,res) => {
const num = calculate(20);
const url = getUrl('https');
res.statusCode = 200;
res.end(`${num}-${url}-test3`);
})
server.listen(port, host, () => {
console.log(`服务器运行在 http://${host}:${port}/`)
})
加载规则
1.如果 require 参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件, 比如,require('/future/Demo/NodeJs/2.模块化/custom_module/tools.js')
将加载/future/Demo/NodeJs/2.模块化/custom_module/tools.js
;
2.如果 require 参数以“ ./ ”或“ ../ ”开头,那么则以相对路径的方式来查找模块,这种方式在应用中是最常见的, 例如前面的例子中我们用了require('./custom_module/tools')
来加载;
3.如果require参数不以“ ./ ”或“ ../ ”开头,而该模块又不是核心模块,那么就要通过查找 node_modules
来加载模块了(使用npm获取的包通常就是以这种方式加载的);
举例来说:脚本/future/Demo/NodeJs/2.模块化/app3.js
执行了require('tools.js')
命令,Node会依次搜索以下文件。
/usr/local/lib/node/tools.js
/future/Demo/NodeJs/2.模块化/node_modules/tools.js
/future/Demo/NodeJs/node_modules/tools.js
/future/node_modules/tools.js
/node_modules/tools.js
4.如果指定的模块文件没有后缀,Node 会尝试为文件名添加.js、.json、.node
后,再去搜索。 .js 件会以文本格式的JavaScript 脚本文件解析,.json 文件会以 JSON 格式的文本文件解析,.node 文件会以编译后的二进制文件解析;
注意:核心模块拥有最高的加载优先级,换言之如果有模块与其命名冲突,Node.js 总是会加载核心模块。
加载缓存
Node.js 模块不会被重复加载,这是因为 Node.js 通过文件名缓存所有加载过的文件模块,所以以后再访问到时就不会重新加载了,注意,Node.js 是根据实际文件名缓存的,而不是 require() 提供的参数缓存的,也就是说即使你分别通过require('tools')
和 require('./node_modules/tools')
加载两次,也不会重复加载,因为尽管两次参数不同,解析到的文件却是同一个。
包管理器:npm
npm 是随同 NodeJS 一起安装的包管理工具,能解决 NodeJS 代码部署上的很多问题,常见的使用场景有以下几种:
- 允许用户从 npm 服务器下载别人编写的第三方包到本地使用。
- 允许用户从 npm 服务器下载并安装别人编写的命令行程序到本地使用。
- 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
使用 npm 命令安装模块,使用使用语法如下
npm install express // 本地安装
npm install express -g // 全局安装
本地安装
- 将安装包放在
./node_modules
下(运行 npm 命令时所在的目录),如果没有node_modules
目录,会在当前执行 npm 命令的目录下生成node_modules
目录。 - 可以通过 require() 来引入本地安装的包。
全局安装
- 将安装包放在
/usr/local
下或者你node
的安装目录。 - 可以直接在命令行里使用。
package.json用于定义包的属性,接下来我们来创建下:
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (2.模块化) test //模块名
version: (1.0.0)
description: nodejs模块化demo //项目描述
entry point: (app1.js)
test command:
git repository:
keywords:
author: wanwan
license: (ISC)
About to write to /xx/package.json:
{
"name": "test",
"version": "1.0.0",
"description": "nodejs模块化demo",
"main": "app1.js",
"dependencies": {
"express": "^4.17.1"
},
"devDependencies": {},
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "wanwan",
"license": "ISC"
}
Is this OK? (yes) yes
npm常用指令
npm version 查看模块版本
npm help 查看某条命令的详细帮助
npm install 安装模块
npm uninstall 卸载模块
npm update 更新模块
npm outdated 检查模块是否已经过时
npm ls 查看安装的模块
npm init 在项目中引导创建一个package.json文件
四、参考资料
- Nodejs开发指南.pdf
- Node学习指南.pdf
- CommonJS规范 -- JavaScript 标准参考教程(alpha)
完整示例代码:https://github.com/wanwan0306/future/tree/master/Demo/NodeJs/2.模块化
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。