- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
- Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。
- Node使用包管理器NPM。
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
运行环境, 不是一门语言,不是一个框架。只是可以做为JavaScript代码运行的一个环境。 javascript
而这个运行环境主要是由V8提供的。html
建立了一个callstack。vue
function main(){ func1(); } function func1(){ func2(); } function func2(){ console.log(1); } main();
除去V8,Node中另一个比较重要的组成就是 libuv。java
What? libuv
是什么鬼?node
先说说,关于Node的另一句话:webpack
Node is designed to build scalable network applications.
这句话的底气在哪儿,就是Node自己采用的 事件驱动,非阻塞I/O模型。c++
在 并发模型构建网络的应用中,每一个链接都会生成一个新线程,每一个新线程可能须要 2MB 的配套内存。在一个拥有 8 GB RAM 的系统上,理论上最大的并发链接数量是 4,000 个用户。随着您的客户群的增加,若是但愿您的 Web 应用程序支持更多用户,那么,您必须添加更多服务器。因此在传统的后台开发中,整个 Web 应用程序架构(包括流量、处理器速度和内存速度)中的瓶颈是:服务器可以处理的并发链接的最大数量。这个不一样的架构承载的并发数量是不一致的。并且,多个线程存在的话,也会衍生出线程切换的问题,这也会占用必定资源。 git
并发存在的两个问题:github
Node的解决这个问题思路:web
市场上已经有一些使用一样处理思路的框架。
具体是怎么作的?
在程序运行时,V8 会把I/O操做等耗时较多操做及相关回调一并交给libuv去处理。而V8继续执行后面的代码。等到I/O操做完成后,libuv会将回调方法放到事件队列中。
const fs = require('fs'); const readFile = (file) => { fs.readFile(file, (err, data) => { if(!err) console.log(data); }); } console.log('program start ......'); readFile('file.json'); console.log('readFile has put the I/O '); console.log('program end!!!!!');
负责异步程序调度的工做就是libuv
作的事情。
Libuv is a multi-platform support library with a focus on asynchronous I/O.
在上述程序中,当遇到文件读取操做时,V8会把js接口转成C++接口 同时把回调方法一并交给libvu。此时libuv接管这个文件读取任务。当文件读取完成后,libuv就会把这个事件的回调函数扔到事件队列里。当下一次检查事件队列时,就会执行该回调函数。
question: 既然这样,怎么理解node中的单线程?
再捋一捋Node, V8 和libuv的关系。
1) Node主要由V8 javascript引擎和libuv组成;
2) v8引擎主要负责解释执行js代码,碰到须要异步的操做会交给libuv处理;
3) libuv自己是独立的c语言库,能够直接使用c/c++来调用;
在浏览器端,JS是没有能力进行文件读取操做的。js最初的能力也就限制在表单校验上。而在Node中,JS能够操做本地文件,创建网络链接。这确定是Node干的好事!
再来讲说Node中其它一些组成部分
builtin modules是由C++代码写成各种模块,包含了crypto,zlib, file, net等基础功能。
除了builtin modules, 还有一个native modules。它们是用js编写的内建模块,提供给程序开发者使用。
builtin modules 和 native modules都属于核心模块。核心模块在Node源码编译的过程当中,编译进了二进制文件。因此在Node进程启动的时候,部分核心模块就已经被直接加载到了内存当中。
至此,Node的基本构成和运行原理已经讲完了。
补充一句:Node.js的单线程并非真正的单线程,只是开启了单个线程(能够定义为主线程)进行业务处理,同时开启了其余线程专门处理I/O。当一个指令到达主线程,主线程发现有I/O以后,直接把这个事件传给libuv处理。libuv会管理一个线程池,I/O操做就是由线程池里面的线程完成的。等I/O 操做完成,libuv,会把对应I/O操做的回调放到事件循环当中。在线程上,不会等待I/O 操做完成,继续执行后续的代码。这就是“单线程”、“异步I/O”。
IO.js
var fork = require('child_process').fork; var fs = require('fs'); console.log('start......'); // blocking // console.log(fs.readFileSync('test.json', 'utf-8')); // non blocking var childProcess = fork('another-thread.js'); childProcess.on('message', function(data){ console.log(data); }) console.log('end !!!');
another-child.js
var fs = require('fs'); process.send( fs.readFileSync('test.json', 'utf-8'));
Everything runs in parallel except your code! 在Node中)除了代码,一切都是并行的!
因为node中主任务的执行是以单线程的方式进行,若是程序出错致使崩溃,就会终止整个流程。为此,市场上有些Node进程管理工具,它们会维护Node程序的状态,当程序挂掉时,会自动重启。好比咱们使用的pm2
。
对于node没有的一些模块(native modules),能够引入外部模块。这些外部模块一般是其它开发者贡献的。
那么问题来了,对于数量众多的模块中,如何快速找到本身想要的并可以快速的引进到本身的项目当中。
这就是npm帮咱们作的工做。
Use npm to install, share, and distribute code; manage dependencies in your projects; and share & receive feedback with others.
NPM是JavaScript包的管理器。
npm consists of three distinct components:
Use the website
to discover packages, set up profiles, and manage other aspects of your npm experience. For example, you can set up Orgs (organizations) to manage access to public or private packages.
The CLI
runs from a terminal. This is how most developers interact with npm.
The registry
is a large public database of JavaScript software and the meta-information surrounding it.
npm默认随node一块儿安装,在Node安装完成后,npm已经安装。
去npm官网,按关键词查找。
npm install [module name]
:普通安装方式,包安装完成后,会在当前目录生成一个node_modules
目录。这是一个存放外部js模块的地方,经过npm安装的包都放在node_modules
下。npm install -g [module name]
:全局安装,模块被安装在node安装路径下的 node_modules
中。npm install [folder path]
:能够指定npm 安装某个目录folder path
下的的文件,前提是这个目录下包含package.json
文件。npm install [module name]@[version]
: 安装包的时候,指定对应的版本号。
npm install --save-prod [module name]
: 在本地安装包,并将安装信息写入 package.json
文件中的dependencies
中, 不写--save-prod 或者只写 --save 默认跟 --save-prod同样。npm install --save-dev [module name]
:在本地安装包,并将安装信息写入 package.json
文件中的devDependencies
中
dependencies
:在生产环境中须要用到的依赖devDependencies
:在开发、测试环境中用到的依赖npm update [module name]
: 更新本地模块npm uninstall [module name]
: 卸载模块package.json
是一个node和npm都会自动读取的配置文件,它里面是个标准的JSON格式字符串。
对于NPM而言, package.json
作了如下工做:
对于你的项目而言,package.json
定义了一些基础信息,好比项目名称,版本等等。
pakcage.json必须具备的两个字段: name
和 version
。这俩个字段有什么意义呢?
NPM 做为一个包管理平台,当有开发者提交(publish)模块时,必须提供一些基本信息便于管理。
npm run + 属性名
执行{ "name": "vue-todo", "version": "1.0.0", "description": "a simply todolist using vuejs", "scripts": { "start": "node server.js", "stop": "egg-scripts stop --title=egg-server-example", "dev": "egg-bin dev" }, "dependencies": { // 线上生产环境必须,固然开发环境也会用到 "babel-runtime": "^6.23.0", "vue": "^2.0.1", "vue-localstorage": "^0.1.1", "vuex": "^2.2.1" }, "devDependencies": { // 开发环境会用到的东东 "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.3", "webpack-hot-middleware": "^2.12.2", "webpack-merge": "^0.14.1" } }
版本格式:主版本号.次版本号.修订号, 例如 1.2.3
版本号递增规则以下:
在package.json定义版本规则的时候,能够这么作:
若是只打算接受补丁版本的更新(也就是最后一位的改变),就能够这么写:
若是接受小版本的更新(第二位的改变),就能够这么写:
若是能够接受大版本的更新(天然接受小版本和补丁版本的改变),就能够这么写:
在使用npm install --save
|| npm install --save-dev
安装的时候,写入pakcage.json中的依赖,默认接受小版本的更新,即在版本号前添加 '^'。
Node中的模块分为两类:
fs
、http
等)文件模块是在运行的时候,动态加载。须要进行路径分析,文件定位 和 编译执行。
Node加载模块时,优先从缓存中加载,若是缓存中不存在该模块,才会按照上述的三个步骤进行模块加载。
模块标识符在Node中,主要有如下几类:
fs
, http
等.
或 ..
开头的相对路径文件模块/
开头的绝对路径文件模块这几类模块的加载速度是依次下降的。
module.js
同浏览器中的window
同样,在Node中的全局变量都挂在global
下。
先说一下,经常使用到一些变量:
上面5个变量,貌似全局变量,但不是全局变量。他们都是模块系统下的东西。
question: 它们不在全局变量下,那它们为什么能够在模块中直接调用?
在Node中,引入模块分为三个步骤:
编译和执行是引入文件模块的最后一个阶段。
.js
文件 : Node 会对js源码进行一个首尾的封装。返回一个function,并将当前的环境的exports
,require
,module
,__dirname
,__filename
做为形参传递给这个function。包装后的代码:
(function(exports, require, module, __filename, __dirname){ // js文件中的源码 })
这就是为何 它们不在 全局变量下,却能够在模块当中使用的缘由。
.node
文件: 对于.node
文件, 实际上并不须要编译过程。由于.node
文件自己就是C/C++编译后的文件,它只有加载和执行过程。.json
文件: Node 会读取json文件内容,并将它赋予exports对象,直接传递给第三方调用。在NPM中的模块,基本属于Node中文件模块里的Javascript模块。
Node的模块系统参照CommonJS规范实现。
若是参数字符串不以./
或/
或../
开头,说明要加载的不是一个文件,而是一个默认提供的核心模块。
而后再寻找NPM模块(即第三方模块包,或本身写的模块包)
若是require当中的参数字符串以./(从当前目录出发)或../(从上一级目录出发)开头:表示按照相对路径,从当前文件所在的文件夹开始寻找要载入的模块文件。
process
对象是一个全局变量,它提供当前 Node.js 进程的有关信息,以及控制当前 Node.js 进程。
process.argv
: 包含命令行参数的数组。第一个元素会是'node',第二个元素将是.js文件的名称,接下来的参数依次是命令行参数process.execArgv
: 启动进程所需的 node 命令行参数。这些参数不会在 process.argv 里出现,而且不包含 node 执行文件的名字,或者任何在名字以后的参数。这些用来生成子进程,使之拥有和父进程有相同的参数process.env
: 获取当前系统环境信息的对象,输出内容是环境变量等内容,这个对象能够修改nextTick(callback)
: 将callback放到事件轮询队列首位,下一次事件轮询开始时,先执行callbackprocess.abort
:结束当前进程process.kill(pid)
:结束一个进程process.js
console.log(process.argv); console.log(process.execArgv);
node process.js # [ '/usr/local/bin/node', '/root/node-demo/process.js' ] # [] node process.js abc 234 cvb=cvb # [ '/usr/local/bin/node', # '/root/node-demo/process.js', # 'abc', # '234', # 'cvb=cvb' ] # [] node --harmony --use-openssl-ca process.js abc 234 cvb=cvb # [ '/usr/local/bin/node', # '/root/node-demo/process.js', # 'abc', # '234', # 'cvb=cvb' ] # [ '--harmony', '--use-openssl-ca' ]
file.js
questions require('../fs/file.js')
这里是异步仍是同步?
等等............
[深刻浅出Node.js]()