2

image

一、写在前面

为了统一 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官网https://www.npmjs.cn/

npm 是随同 NodeJS 一起安装的包管理工具,能解决 NodeJS 代码部署上的很多问题,常见的使用场景有以下几种:

  1. 允许用户从 npm 服务器下载别人编写的第三方包到本地使用。
  2. 允许用户从 npm 服务器下载并安装别人编写的命令行程序到本地使用。
  3. 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

使用 npm 命令安装模块,使用使用语法如下

npm install express      // 本地安装
npm install express -g   // 全局安装

本地安装

  1. 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
  2. 可以通过 require() 来引入本地安装的包。

全局安装

  1. 将安装包放在 /usr/local 下或者你 node 的安装目录。
  2. 可以直接在命令行里使用。

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文件

四、参考资料

  1. Nodejs开发指南.pdf
  2. Node学习指南.pdf
  3. CommonJS规范 -- JavaScript 标准参考教程(alpha)

完整示例代码:https://github.com/wanwan0306/future/tree/master/Demo/NodeJs/2.模块化


婉婉
294 声望14 粉丝

爱吃肉肉的小姐姐~