其他章节请看:

前端学习 node 快速入门 系列

模块(module)

模块的导入

核心模块

初步认识 node 这篇文章中,我们在读文件的例子中用到了 require('fs'),在写最简单的服务器的例子中用到了 require('http'),除了 fs 和 http,node 提供了很多核心模块,例如:path(路径)、os(操作系统)、events(事件)、url 等等。

如果我们需要使用核心模块的功能,就使用 require(模块名) 方法进行引入。

第三方模块

npm 一文中,我们知道了如何用 npm 下载包。如果我们需要使用第三方的模块,也可以像引入核心模块那样。请看示例:

// 首先得下载包。后续不再提醒
$ npm install underscore let _ = require('underscore') console.log(_.VERSION)
console.log(typeof _.clone)
console.log(typeof _.find) /*
输出:
1.12.0
function
function
*/

:由于核心模块和第三方模块(npm 下载的包)都是通过模块名加载,所以它们的名字不会相同。也就是说 node 不允许第三方模块占用 node 核心模块的名字。

require() 方法加载第三方模块有这么一套规则

  1. 找到执行文件所属目录的 node_modules 文件夹
  2. 找到 node_modules/第三方模块名/package.json
  3. 找到 main 字段指向的入口文件
    • 加载入口文件
  4. 找不到 package.json 或找不到 main 字段,又或者找不到 main 指向的文件,就加载 index.js
  5. 以上都失败,则去上一级找 node_modules 目录,找不到又去上一级,上一级,直到根目录都没有找到,就报错处理

我们用 underscore 这个第三方模块验证一下上面的规则。请看示例:

首先准备环境,导入 underscore。

// 进入项目(D:\实验楼\node-study\test2)

// 快速生成 package.json
$ npm init -y
// 下载 underscore
$ npm install underscore // 创建 test2/index.js
let _ = require('underscore')
console.log(_.VERSION) // 运行
$ node index
1.12.0
function

现在模块已经正常导入。

我们首先验证一下:加载的是否是 main 字段指向的文件。具体步骤请看:

// 修改 package.json 中的 main 字段。目录是:test2\node_modules\underscore\package.json
{
"main": "underscore.js",
// 改为
"main": "a.js",
} // 新建 a.js 文件。目录是:test2\node_modules\underscore\a.js
exports.name = 'ph' // 修改 index.js 内容如下
let _ = require('underscore')
console.log(_) $ node index
{ name: 'ph' }

重置 package.json 中 main 字段,将 underscore 模块的主入口文件改为 a.js,最终我们拿到的 underscore 确实是我们返回的 { name: 'ph' }

接着将 a.js 改为 index.js,执行 node index,输出的还是 { name: 'ph' }。说明 require() 找不到 main 指向的入口文件 a.js,于是就去找 index.js。

最后将 node_modules 文件夹剪切至 D 盘根目录中,执行 node index 仍旧输出 { name: 'ph' }

自定义模块

在 node 中,每个文件都被视为一个单独的模块

浏览器可以通过 script 标签执行多个 js 文件,但 node 只能执行一个文件,不过我们可以通过 require() 方法加载其他 js 文件,js 文件又加载其他 js 文件,如此循环,最终就形成了一个大大的模块。请看示例:

// index.js 内容
let m = require('./a') // {1}
console.log(m.sum(1,2)) // a.js 内容
let sum = (v1, v2) => v1 + v2
module.exports.sum = sum // 运行
$ node index
3

index.js 中加载了 a.js。相当于运行了 2 个文件。

如果将 require('./a') 改成 require('a')(行{1}),运行则会报错:Error: Cannot find module 'a'。因为 require('a') 会直接去核心模块和第三方模块中找,结果又没有找到对应的 a 模块。只有传入 require(id) 方法中的 id 以 '/'、'./'或'../'开头,才会从自定义模块中找。

通常 '/' 很少会用到。请看示例:

// D:\1.js
console.log('hello') // D:\实验楼\node-study\index.js
require('/1') $ node index
hello

运行 require('/1') 会到文件的根目录(D 盘)中找 1.js。如果别人用你的项目,还得在根目录下也存一份同样的 1.js 文件吗?这是很困难的。

到现在,我们已经知道 require() 方法能导入模块,也能执行模块。其实它还有一个重要特性:优先从缓存中加载,重复导入也只会执行一次。

// 1.js
var a = 1
let two = require('./2')
let twoCopy = require('./2')
console.log(a)
console.log(`two a = ${two.a}`)
console.log(`twoCopy a = ${twoCopy.a}`) // 2.js
var a = 2
console.log(`我是 2.js`) // {1}
exports.a = a; // 运行
$ node 1
我是 2.js
1
two a = 2
twoCopy a = 2

在 1.js 中导入了两次 2.js,但只输出了一次 我是 2.js(行{1})。

请接着看:

// index.js
require('./a')
require('./b') // a.js
require('./b') // b.js
console.log('hello') $ node index
hello

模块 b.js 同样被导入了两次,但也只执行了一次。

模块的导出

每个文件都有一个 module 的变量。就像 require() 方法,无需加载就可以直接使用。请看示例:

// index.js
console.log(typeof require)
console.log(module) // 运行
$ node index
function
Module {
id: '.',
...
exports: {}, // {1}
...
}

我们看到 module 里面有一个 exports 的对象(行{1})。

如果我们的模块需要对外提供接口,就可以使用 module.exports。请看示例:

// index.js
let m = require('./1')
console.log(`name = ${m.name} age = ${m.age}`) // 1.js
let name = 'ph'
let age = 'age'
module.exports.name = name // {1} $ node index
name = ph age = undefined

模块 1.js 只导出了 name 属性,所以 index.js 只能读取到 name ,而读不到 age。

module.exports 还提供了一个快捷方式:直接使用 exports。例如将 module.exports.name = name(行{1}) 改成 exports.name = name 效果也是一样的。

:由于 exports 是 module.exports 的引用,就像任何变量一样,如果将新值分配给 exports,则它不再绑定到 module.exports。请看示例:

// index.js
let m = require('./1')
console.log(m) // 1.js
module.exports = 'ph' // {1} $ node index
ph

如果将 module.exports = 'ph'(行{1}) 换成 exports = 'ph',输出结果是:{},说明导出失败。

模块的作用域

node 中没有全局作用域,只有模块作用域。内部访问不了外部,外部访问不了内部。请看示例:

// 1.js
var a = 1
var a = 2
console.log(a)

执行 $ node 1 输出 2。稍微改一下:

// 1.js
var a = 1
require('./2')
console.log(a) // 2.js
var a = 2

再次执行 $ node 1,这次输出的是 1。说明 2.js 中的 变量 a 没能影响到 1.js 中的变量 a。继续:

// 1.js
require('./2')
console.log(a) // 2.js
var a = 1

再次运行 $ node 1,输出报错信息 ReferenceError: a is not defined,说明 1.js 不能访问 2.js 中的变量。这就是外部访问不了内部。

其他章节请看:

前端学习 node 快速入门 系列

前端学习 node 快速入门 系列 —— 模块(module)的更多相关文章

  1. 前端学习 node 快速入门 系列 —— 初步认识 node

    其他章节请看: 前端学习 node 快速入门 系列 初步认识 node node 是什么 node(或者称node.js)是 javaScript(以下简称js) 运行时的一个环境.不是一门语言. 以 ...

  2. 前端学习 node 快速入门 系列 —— npm

    其他章节请看: 前端学习 node 快速入门 系列 npm npm 是什么 npm 是 node 的包管理器,绝大多数 javascript 相关的包都放在 npm 上. 所谓包,就是别人提供出来供他 ...

  3. 前端学习 node 快速入门 系列 —— 报名系统 - [express]

    其他章节请看: 前端学习 node 快速入门 系列 报名系统 - [express] 最简单的报名系统: 只有两个页面 人员信息列表页:展示已报名的人员信息列表.里面有一个报名按钮,点击按钮则会跳转到 ...

  4. 前端学习 node 快速入门 系列 —— 简易版 Apache

    其他章节请看: 前端学习 node 快速入门 系列 简易版 Apache 我们用 node 来实现一个简易版的 Apache:提供静态资源访问的能力. 实现 直接上代码. - demo - stati ...

  5. 前端学习 node 快速入门 系列 —— 服务端渲染

    其他章节请看: 前端学习 node 快速入门 系列 服务端渲染 在简易版 Apache一文中,我们用 node 做了一个简单的服务器,能提供静态资源访问的能力. 对于真正的网站,页面中的数据应该来自服 ...

  6. vue 快速入门 系列 —— vue loader 上

    其他章节请看: vue 快速入门 系列 vue loader 上 通过前面"webpack 系列"的学习,我们知道如何用 webpack 实现一个不成熟的脚手架,比如提供开发环境和 ...

  7. vue 快速入门 系列 —— vue-cli 下

    其他章节请看: vue 快速入门 系列 Vue CLI 4.x 下 在 vue loader 一文中我们已经学会从零搭建一个简单的,用于单文件组件开发的脚手架:本篇,我们将全面学习 vue-cli 这 ...

  8. webpack 快速入门 系列 - 自定义 wepack 上

    其他章节请看: webpack 快速入门 系列 自定义 wepack 上 通过"初步认识webpack"和"实战一"这 2 篇文章,我们已经学习了 webpac ...

  9. vue 快速入门 系列 —— vue loader 下

    其他章节请看: vue 快速入门 系列 vue loader 下 CSS Modules CSS Modules 是一个流行的,用于模块化和组合 CSS 的系统.vue-loader 提供了与 CSS ...

随机推荐

  1. ssh原理及加密传输

    1.ssh??(保证过程中是加密的,即安全的)ssh 是 Secure Shell 的缩写,是一个建立在应用层上的安全远程管理协议.ssh 是目前较为可靠的传输协议,专为远程登录会话和其他网络服务提供 ...

  2. cdn jsdelivr + github releases 以wordpress sakura主题manifest为例

    1 创建github repository 在本地创建文件,这里为文件夹 /manifest 在github创建库wordpresscdn,上传 /manifest到库中 2 github relea ...

  3. 使用MCSManager搭建Minecraft服务器

    目录 一.准备工作 1.MCSManager Windows环境下安装 Linux安装 2.Minecraft服务端 3.Java 二.配置 1.登录面板 2.上传服务端 3.服务端的配置 三.开启服 ...

  4. Linux shell create file methods

    Linux shell create file methods touch, cat, echo, EOF touch $ touch file.py $ touch file1.txt file2. ...

  5. Nestjs mongodb

    nestjs 文档 mongoose 文档 使用"@meanie/mongoose-to-json"转换查询后返回的json数据 将"_id"转为"i ...

  6. echarts手机端,数据多时可以滚动

    <div id="container" style="height: 400px"></div> <script type=&qu ...

  7. 新年狂欢高倍币,1万枚VAST拿到手软

    新一年NGK推出了新币VAST,成为了当前最瞩目的一颗星.有人认为VAST有价无市,有人认为VAST价值巨大,潜力无限.总之,众说风云! 选择数字货币,当然是挑选有价值的货币,从内外价值入手. 一.币 ...

  8. 调整是为了更好的上涨,牛市下的SPC空投来了!

    2021年刚过没几天,比特币就开启了牛市的旅程,BTC涨到4万美元,ETH涨到1300多美元,BGV也涨到了621.05美元,牛市已然来袭. 虽然从近两日,比特币带领着主流币进行了一波调整,但是只涨不 ...

  9. NGK公链大事件盘点——回顾过去,展望未来!

    NGK公链构想广阔,愿景宏大,2020年10月NGK正式上线,同时NGK全球发布会正式启动,建立区块链生态体系. 早在这之前,NGK就经过了紧锣密鼓的数年缜密搭建. 2018年6月NGK底层系统技术原 ...

  10. PAA房产,一家有温度的房产公司

    PAUL ADAMS ARCHITECT房产(以下简称PAA,公司编号:07635831)对每一个客户从心出,为他们选择优质房源,为他们缔造家的温暖.PAA房产,是一家有温度的房产公司. PAA房产( ...