We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NodeJS本身不是开发语言,它是一个工具或者平台,在服务器端解释、运行Javascript。NodeJS利用Google V8来高效率地解释运行Javascript,而Javascript做的只是调用这些API而已。NodeJS里的libuv为开发者提供了异步编程的能力。
libuv 采用了 异步 (asynchronous), 事件驱动 (event-driven)的编程风格, 其主要任务是为开人员提供了一套事件循环和基于I/O(或其他活动)通知的回调函数, libuv 提供了一套核心的工具集, 例如定时器, 非阻塞网络编程的支持, 异步访问文件系统, 子进程以及其他功能.
NodeJS 把每个JavaScript文件封装成一个模块,一个模块其实就是函数,因为函数本来就是一个执行上下文,可以通过node demo.js得到,这个函数的参数
node demo.js
# demo.js console.log(arguments); # output: { '0': {}, '1': { [Function: require] resolve: { [Function: resolve] paths: [Function: paths] }, main: Module { id: '.', exports: {}, parent: null, filename: 'E:\\myWorks\\Workbench\\web\\modu.js', loaded: false, children: [], paths: [Array] }, extensions: { '.js': [Function], '.json': [Function], '.node': [Function] }, cache: { 'E:\myWorks\Workbench\web\modu.js': [Object] } }, '2': Module { id: '.', exports: {}, parent: null, filename: 'E:\\myWorks\\Workbench\\web\\modu.js', loaded: false, children: [], paths: [ 'E:\\myWorks\\Workbench\\web\\node_modules', 'E:\\myWorks\\Workbench\\node_modules', 'E:\\myWorks\\node_modules', 'E:\\node_modules' ] }, '3': 'E:\\myWorks\\Workbench\\web\\modu.js', '4': 'E:\\myWorks\\Workbench\\web' }
从上图可以看到,每个JS文件之所以可以访问module、exports、require()、__filename、__dirname,就是因为NodeJS把我们写的JS文件封装成一个模块,这个模块就是一个函数执行上下文,而函数的入参就有它们。
module
exports
require()
__filename
__dirname
我们还可以通过global对象访问全局对象。
NodeJS 里的模块分为两种:
npm
yarn
当 Node.js 直接运行一个文件时,require.main 会被设为它的 module。 这意味着可以通过 require.main === module 来判断一个文件是否被直接运行: 对于 foo.js 文件,如果通过 node foo.js 运行则为 true,但如果通过 require('./foo') 运行则为 false。
当没有以 '/'、'./' 或 '../' 开头来表示文件时,这个模块必须是一个核心模块或加载自 node_modules 目录。
如果按确切的文件名没有找到模块,则 Node.js 会尝试带上 .js、.json 或 .node 拓展名再加载。
但是文件名被解析成一个目录,如果目录下有package.json,入口文件将会是main字段指定的文件;如果目录下没有package.json,Node.js 就会试图加载目录下的 index.js 或 index.node 文件。
package.json
main
如果传递给 require() 的模块标识符不是一个核心模块,也没有以 '/' 、 '../' 或 './' 开头,则 Node.js 会从当前模块的父目录开始,尝试从它的 /node_modules 目录里加载模块。 Node.js 不会附加 node_modules 到一个已经以 node_modules 结尾的路径上。
如果 NODE_PATH 环境变量被设为一个以冒号分割的绝对路径列表,则当在其他地方找不到模块时 Node.js 会搜索这些路径。
如果给定的路径不存在,则 require() 会抛出一个 code 属性为 'MODULE_NOT_FOUND' 的 Error。
模块在第一次加载后会被缓存。 这也意味着(类似其他缓存机制)如果每次调用 require('foo') 都解析到同一文件,则返回相同的对象。
多次调用 require(foo) 不会导致模块的代码被执行多次。 这是一个重要的特性。 借助它, 可以返回“部分完成”的对象,从而允许加载依赖的依赖, 即使它们会导致循环依赖。
模块是基于其解析的文件名进行缓存的。 在不区分大小写的文件系统或操作系统中,被解析成不同的文件名可以指向同一文件,但缓存仍然会将它们视为不同的模块,并多次重新加载。
Node.js 有些模块会被编译成二进制。require()总是会优先加载核心模块。 例如,require('http') 始终返回内置的 HTTP 模块,即使有同名文件。
当循环调用 require() 时,一个模块可能在未完成执行时被返回。
NodeJS项目里可以通过NPM和package.json管理第三方包。
NPM
NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题。允许用户从NPM服务器下载别人编写的第三方包或命令行程序到本地使用,也允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
name - 包名。 version - 包的版本号。 description - 包的描述。 homepage - 包的官网 url 。 author - 包的作者姓名。 contributors - 包的其他贡献者姓名。 dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。 repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。 main - main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。 keywords - 关键字.
首先使用npm adduser指令创建NPM账户,或者使用npm login指令登录NPM账号,然后创建自己的库,package.json是必须的,用于描述模块,使用npm publish指令发布出去。
npm adduser
npm login
npm publish
下面是官方给出的NodeJS的事件循环示意图:
┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
事件循环可以说是NodeJS的核心,作为web服务器,可以把接收到的请求放到事件队列中去,并把阻塞的操作放到异步模块中,这样一来可以高效率使用cpu,提高用户响应。
setTimeout()、setInterval()属于timers阶段的。 setImmediate()属于check阶段。 process.nextTick()将 callback 添加到"next tick 队列",在micro-task-queue被调用之前执行。递归调用nextTick callbacks 会阻塞任何I/O操作,就像一个while(true); 循环一样。 Promise.then()属于micro-task-queue。
setTimeout()
setInterval()
setImmediate()
process.nextTick()
Promise.then()
NodeJS和浏览器的事件循环机制不一样。
浏览器环境下,microtask的任务队列是每个macrotask执行完之后执行。 而在Node.js中,microtask会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行microtask队列的任务。
[译] 你不知道的 Node 深入理解js事件循环机制(Node.js篇) The Node.js Event Loop, Timers, and process.nextTick() 【Node.js】理解事件循环机制 Node.js机制及原理理解初步 module - 模块
The text was updated successfully, but these errors were encountered:
No branches or pull requests
概述
NodeJS本身不是开发语言,它是一个工具或者平台,在服务器端解释、运行Javascript。NodeJS利用Google V8来高效率地解释运行Javascript,而Javascript做的只是调用这些API而已。NodeJS里的libuv为开发者提供了异步编程的能力。
1. 模块化的本质
NodeJS 把每个JavaScript文件封装成一个模块,一个模块其实就是函数,因为函数本来就是一个执行上下文,可以通过
node demo.js
得到,这个函数的参数从上图可以看到,每个JS文件之所以可以访问
module
、exports
、require()
、__filename
、__dirname
,就是因为NodeJS把我们写的JS文件封装成一个模块,这个模块就是一个函数执行上下文,而函数的入参就有它们。我们还可以通过global对象访问全局对象。
A. 模块的分类:
NodeJS 里的模块分为两种:
npm
和yarn
引入的其他人写好的模块)和自己编写的模块B. 访问主模块:
当 Node.js 直接运行一个文件时,require.main 会被设为它的 module。 这意味着可以通过 require.main === module 来判断一个文件是否被直接运行:
对于 foo.js 文件,如果通过 node foo.js 运行则为 true,但如果通过 require('./foo') 运行则为 false。
C. 模块解析:
1. 区别模块类型
当没有以 '/'、'./' 或 '../' 开头来表示文件时,这个模块必须是一个核心模块或加载自 node_modules 目录。
2. 填充后缀
如果按确切的文件名没有找到模块,则 Node.js 会尝试带上 .js、.json 或 .node 拓展名再加载。
但是文件名被解析成一个目录,如果目录下有
package.json
,入口文件将会是main
字段指定的文件;如果目录下没有package.json
,Node.js 就会试图加载目录下的 index.js 或 index.node 文件。3. 填充路径
如果传递给 require() 的模块标识符不是一个核心模块,也没有以 '/' 、 '../' 或 './' 开头,则 Node.js 会从当前模块的父目录开始,尝试从它的 /node_modules 目录里加载模块。 Node.js 不会附加 node_modules 到一个已经以 node_modules 结尾的路径上。
4. 全局目录
如果 NODE_PATH 环境变量被设为一个以冒号分割的绝对路径列表,则当在其他地方找不到模块时 Node.js 会搜索这些路径。
5. 查找失败
如果给定的路径不存在,则 require() 会抛出一个 code 属性为 'MODULE_NOT_FOUND' 的 Error。
D. 模块缓存:
模块在第一次加载后会被缓存。 这也意味着(类似其他缓存机制)如果每次调用 require('foo') 都解析到同一文件,则返回相同的对象。
多次调用 require(foo) 不会导致模块的代码被执行多次。 这是一个重要的特性。 借助它, 可以返回“部分完成”的对象,从而允许加载依赖的依赖, 即使它们会导致循环依赖。
模块是基于其解析的文件名进行缓存的。 在不区分大小写的文件系统或操作系统中,被解析成不同的文件名可以指向同一文件,但缓存仍然会将它们视为不同的模块,并多次重新加载。
E. 核心模块:
Node.js 有些模块会被编译成二进制。
require()
总是会优先加载核心模块。 例如,require('http') 始终返回内置的 HTTP 模块,即使有同名文件。F. 循环依赖:
当循环调用 require() 时,一个模块可能在未完成执行时被返回。
2. 包管理
NodeJS项目里可以通过
NPM
和package.json
管理第三方包。NPM
NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题。允许用户从NPM服务器下载别人编写的第三方包或命令行程序到本地使用,也允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
Package.json
发布自己的模块
首先使用
npm adduser
指令创建NPM账户,或者使用npm login
指令登录NPM账号,然后创建自己的库,package.json是必须的,用于描述模块,使用npm publish
指令发布出去。3. 事件循环
下面是官方给出的NodeJS的事件循环示意图:
事件循环可以说是NodeJS的核心,作为web服务器,可以把接收到的请求放到事件队列中去,并把阻塞的操作放到异步模块中,这样一来可以高效率使用cpu,提高用户响应。
1. 关键的API
setTimeout()
、setInterval()
属于timers阶段的。setImmediate()
属于check阶段。process.nextTick()
将 callback 添加到"next tick 队列",在micro-task-queue被调用之前执行。递归调用nextTick callbacks 会阻塞任何I/O操作,就像一个while(true); 循环一样。Promise.then()
属于micro-task-queue。2. 与浏览器事件循环机制的不同
NodeJS和浏览器的事件循环机制不一样。
浏览器环境下,microtask的任务队列是每个macrotask执行完之后执行。
而在Node.js中,microtask会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行microtask队列的任务。
3. 关键阶段
setImmediate()
产生的回调。参考
[译] 你不知道的 Node
深入理解js事件循环机制(Node.js篇)
The Node.js Event Loop, Timers, and process.nextTick()
【Node.js】理解事件循环机制
Node.js机制及原理理解初步
module - 模块
The text was updated successfully, but these errors were encountered: