深刻浅出Node.js
一直想致力于写一篇关于广义讲解Node.js系统的文章,苦于时间有限,资源有限。这篇文章是在结合本身的学习心得以及与行业大佬共同探讨下争对于熟练掌握JS语言后的广义Node.js.至于为何叫做广义在后文会提到。但愿看到这篇文章后能够激发你们对Node.js的学习兴趣,这篇文章的初衷就是致力于帮助你们能够走进Node.js世界。
- 简介:
Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时创建的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度很是快,性能非 常好。
底层选择用c++和v8来实现的
注意:广义的Node.js是指不掌握以底层C++技术以及V8知识,利用Node.js运行在服务端的JS特性完成操做,不少小伙伴会产生疑惑Node.js是什么,简单的来讲:Node.js是解析器.
优点:javascript
- RESTful API
这是NodeJS最理想的应用场景,能够处理数万条链接,自己没有太多的逻辑,只须要请求API,组织数据进行返回便可。它本质上只是从某个数据库中查找一些值并将它们组成一个响应。因为响应是少许文本,入站请求也是少许的文本,所以流量不高,一台机器甚至也能够处理最繁忙的公司的API需求。前端
- 统一Web应用的UI层
目前MVC的架构,在某种意义上来讲,Web开发有两个UI层,一个是在浏览器里面咱们最终看到的,另外一个在server端,负责生成和拼接页面。
不讨论这种架构是好是坏,可是有另一种实践,面向服务的架构,更好的作先后端的依赖分离。若是全部的关键业务逻辑都封装成REST调用,就意味着在上层只须要考虑如何用这些REST接口构建具体的应用。那些后端程序员们根本不操心具体数据是如何从一个页面传递到另外一个页面的,他们也不用管用户数据更新是经过Ajax异步获取的仍是经过刷新页面。java
- 大量Ajax请求的应用
例如个性化应用,每一个用户看到的页面都不同,缓存失效,须要在页面加载的时候发起Ajax请求,NodeJS能响应大量的并发请求。
4.Javascript在nosql的应用
Javascript在nosql数据库中大量应用,使得数据存储和管理使用的都是javascript语句,与web应用有了自然的结合;好比mongoDB;
5.Javascripte运行从前台到后台node
一门语言从前台后台,减小了开发客户端和服务端时,所需的语言切换,使得数据交互效率提高
- 特色
1.单线程:
Nodejs跟Nginx同样都是单线程为基础的,这里的单线程指主线程为单线程,全部的阻塞的所有放入一个线程池中,而后主线程经过队列的方式跟线程池来协做。咱们写js部分不须要关心线程的问题,简单了解就能够了,主要由一堆callback回调构成的,而后主线程在循环过在适当场合调用。 c++
2.事件驱动
首先,解释下“事件驱动”这个概念。所谓事件驱动,是指在持续事务管理过程当中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。 Nodejs设计思想中以事件驱动为核心,事件驱动在于异步回调,他提供的大多数api都是基于事件的、异步的风格。而事件驱动的优点在于充分利用系统资源,执行代码无须阻塞等待某种操做完成,有限的资源用于其余任务。事件驱动机制是经过内部单线程高效率地维护事件循环队列来实现的,没有多线程的资源占用和上下文的切换。git
3.异步、非阻塞I/O
Nodejs提供的不少模块中都是异步执行的。好比,文件操做的函数。 一个异步I/O的大体流程:
1.发起I/O调用 :
①用户经过js代码调用nodejs的核心模块,将回调函数和参数传入核心模块
②将回调函数和参数封装成
2.执行回调:
①操做完成将结果储存到请求对象的result属性上,并发出完成通知。
②循环事件,若是有未完成的,就在进入对象请求I/O观察者队列,以后当作事件处理;程序员
- 缺点
1.不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:因为JavaScript单线程的缘由,若是有长时间运行的计算(好比大循环),将会致使CPU时间片不能释放,使得后续I/O没法发起;
2.只支持单核CPU,不能充分利用CPU
3.可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
4.开源组件库质量良莠不齐,更新快,向下不兼容github
- 安装
官网下载:web
1.下载地址:http://nodejs.cn/download/
2.根据本身的系统进行镜像的下载:sql
3.下载的为最新的node版本,当前下载的为10.8.0
- Nvm管理node
nvm能够方便的在同一台设备上进行多个node版本之间切换
1.先下载安装nvm,下载地址:https://github.com/coreybutle...,选择nvm-setup压缩文件,解压后安装;
2.安装过程当中出现,选择nvm的安装目录
3.选择node的安装目录
4.配置环境变量
5.查看nvm是否安装成功:
Nvm -v
6.安装nodejs
使用nvm install <version> [<arch>]命令下载须要的版本。arch参数表示系统位数,默认是64位,若是是32位操做系统,须要执行命令:nvm install 10.8.0 32,如:
Nvm install 10.8.0
7.使用下载的nodejs
执行nvm use <version> [<arch>] 命令开始使用特定版本。好比:nvm use 10.8.0或者nvm use 10.8.0 32
Nvm use 10.8.0
8.当有多个nodejs版本时,设置默认的node版本
nvm alias default v10.8.0
9.查看当前所安装的node版本
Nvm list
- 全局安装和局部安装
全局安装:
全局安装方式是键入命令:npm install gulp -g 或 npm install gulp --global,其中参数-g的含义是表明安装到全局环境里面,包安装在Node安装目录下的node_modules文件夹中,通常在 Users用户名AppDataRoaming 目录下,可使用npm root -g查看全局安装目录。
局部安装(本地安装)
本地安装方式是键入命令:npm install gulp 或 npm install gulp --save-dev等,其中参数--save-dev的含义是表明把你的安装包信息写入package.json文件的devDependencies字段中,包安装在指定项目的node_modules文件夹下。
局部安装的意义:
一、能够实现多个项目中使用不一样版本的包;
二、能够在不使用全局变量NODE_PATH的状况下,进行包的引入;
Node运行
终端运行和外部文件运行
- Nodejs的模块(commonjs规范)
(一)模块化
1.诞生背景:
全局变量的灾难:
函数命令的冲突:
对于公用方法的封装会出现不少命名冲突,尤为在多人开发的状况下
依赖关系的管理:
好比b.js依赖a.js,在文件引入的过程当中,就要先引入b.js
最先的时候在解决上述部分问题时的解决方案是:使用匿名的自执行函数
2.模块须要解决的问题:
- 如何安全的包装一个模块的代码?(不污染模块外的任何代码)
- 如何惟一标识一个模块?
- 如何优雅的把模块的API暴漏出去?(不能增长全局变量)
- 如何方便的使用所依赖的模块?
(二)Commonjs
1.规范:
1)模块的标识应遵循的规则(书写规范)
定义,标识,引用
2)定义全局函数require,经过传入模块标识来引入其余模块,执行的结果即为别的模块暴漏出来的API
3)若是被require函数引入的模块中也包含依赖,那么依次加载这些依赖
4)若是引入模块失败,那么require函数应该报一个异常
5)模块经过变量exports来向往暴漏API,exports只能是一个对象,暴漏的API须做为此对象的属性。
2.模块的简单使用:
//math.js
exports.add = function() {
var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum;
};
//increment.js
var add = require('math').add;
exports.increment = function(val) {
return add(val, 1);
};
//program.js
var inc = require('increment').increment;
var a = 1;
inc(a); // 2
3.模块的定义
1)全局有一个module变量,用来定义模块
2)经过module.declare方法来定义一个模块(通常不经过此方式进行模块的定义)
3)module.declare方法只接收一个参数,那就是模块的factory,次factory能够是函数也能够是对象,若是是对象,那么模块输出就是此对象。
4)模块的factory函数传入三个参数:require,exports,module,用来引入其余依赖和导出本模块API
5)若是factory函数最后明确写有return数据(js函数中不写return默认返回undefined),那么return的内容即为模块的输出。
不经常使用:
module.declare(function(require, exports, module) {
exports.foo = "bar";
});
module.declare(function(require)
{
return { foo: "bar" };
});
经常使用:
module.exports={}
4.Module.exports和exports的区别:
1)module.exports 初始值为一个空对象 {}
2)exports 是指向的 module.exports 的引用
3)require() 返回的是 module.exports 而不是 exports
4)关系为var exports = module.exports={};
如:
module.exports能够赋值一个对象
module.exports={}
exports不能够赋值一个对象,只能添加方法或者属性
exports.add=function(){
}
5.模块引用
require函数的基本功能是,读入并执行一个JavaScript文件,而后返回该模块的exports对象。当咱们用require()获取module时,Node会根据module.id找到对应的module,并返回module. exports,这样就实现了模块的输出。
require函数使用一个参数,参数值能够带有完整路径的模块的文件名,也能够为模块名。
假如,有三个文件:一个是a.js(存放路径:home/a.js),一个是b.js(存放路径:home/user/b.js), 一个是c.js(存放路径:home/user/c.js)。咱们在a.js文件中引用三个模块,实例代码以下:
var httpModule=require('HTTP');//用 “模块名”加载服务模块http
var b=require('./user/b');//用“相对路径”加载文件b.js
var b=require('../ home/user/c');//用“绝对路径”加载文件c.js
6. 模块标识
模块标识就是传递给require方法的参数,必须符合小驼峰命名的字符串,或者以.、..开头的相对路径,或者绝对路径,默认文件名后缀.js。在Node实现中,正是基于这样一个标识符进行模块查找的,若是没有发现指定模块会报错。
根据参数的不一样格式,require命令去不一样路径寻找模块文件。加载规则以下:
(1)若是参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。好比,require('/home/marco/foo.js')将加载/home/marco/foo.js。
(2)若是参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。好比,require('./circle')将加载当前脚本同一目录的circle.js。
(3)若是参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。
举例来讲,脚本/home/user/projects/foo.js执行了require('bar.js')命令,Node会依次搜索如下文件。
/usr/local/lib/node/bar.js
/home/user/projects/node_modules/bar.js
/home/user/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
这样设计的目的是,使得不一样的模块能够将所依赖的模块本地化。
(4)若是参数字符串不以“./“或”/“开头,并且是一个路径,好比require('example-module/path/to/file'),则将先找到example-module的位置,而后再以它为参数,找到后续路径。
(5)若是指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析。
(6)若是想获得require命令加载的确切文件名,使用require.resolve()方法。此方法会返回一个完整的路径,而且还会对文件的是否存在作检测
7.二进制模块
虽然通常咱们使用JS编写模块,但NodeJS也支持使用C/C++编写二进制模块。编译好的二进制模块除了文件扩展名是.node外,和JS模块的使用方式相同。虽然二进制模块能使用操做系统提供的全部功能,拥有无限的潜能,但对于前端同窗而言编写过于困难,而且难以跨平台使用,所以不在本教程的覆盖范围内。
(三)Node模块的分类:
1.内置模块(核心模块)
核心模块指的是那些被编译进Node的二进制模块,它们被预置在Node中,提供Node的基本功能,如fs、http、https等。核心模块使用C/C++实现,外部使用JS封装。要加载核心模块,直接在代码文件中使用require() 方法便可,参数为模块名称,Node将自动从核心模块文件夹中进行加载。
2.第三方模块
Node使用NPM (Node Package Manager) 安装第三方模块,NPM会将模块安装到应用根目录下的node_modules文件夹中,而后就能够像使用核心模块同样使用第三方模块了。在进行模块加载时,Node会先在核心模块文件夹中进行搜索,而后再到node_modules文件夹中进行搜索。
3.文件模块
上述两种方式都是从当前目录获取模块文件,实际上,能够将文件放在任何位置,而后在加载模块文件时加上路径便可。可使用以./ 开头的相对路径和以/ 或C: 之类的盘符开头的绝对路径。
4.文件夹模块
从文件夹中加载模块,Node首先会在该文件夹中搜索package.json文件。若是存在,Node便尝试解析它,并加载main属性指定的模块文件。若是package.json不存在,或者没有定义main属性,Node默认加载该文件夹下的index.js文件。 如从项目根目录下的 modules/hello 文件夹加载模块: var hello = require("./modules/hello");
package.json格式以下:
{ "name": "hello", "version": "1.0.0", "main": "./hello.js" }
此时,Node会去加载./modules/hello/hello.js 文件。
若是目录里没有 package.json 文件,则 Node.js 就会试图加载目录下的 index.js 或 index.node 文件。 例如,若是上面的例子中没有 package.json 文件,则 require("./modules/hello") 会试图加载:
./modules/hello /index.js
./modules/hello /index.node
- Npm与package.json详解
(一)npm简介:
世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package) (即,代码模块)。来自各大洲的开源软件开发者使用 npm 互相分享和借鉴。包的结构使您可以轻松跟踪依赖项和版本。
npm 是一个包管理器,它让 JavaScript 开发者分享、复用代码更方便(有点 maven 的感受哈)。 在程序开发中咱们经常须要依赖别人提供的框架,写 JS 也不例外。这些能够重复的框架代码被称做包(package)或者模块(module),一个包能够是一个文件夹里放着几个文件,同时有一个叫作 package.json 的文件。 一个网站里一般有几十甚至上百个 package,分散在各处,一般会将这些包按照各自的功能进行划分(相似咱们安卓开发中的划分子模块),可是若是重复造一些轮子,不如上传到一个公共平台,让更多的人一块儿使用、参与这个特定功能的模块。 而 npm 的做用就是让咱们发布、下载一些 JS 轮子更加方便。
(二)npm构成:
npm 由三个独立的部分组成:
网站:是开发者查找包(package)、设置参数以及管理 npm 使用体验的主要途径。
注册表(registry):是一个巨大的数据库,保存了每一个包(package)的信息
命令行工具 (CLI):经过命令行或终端运行。开发者经过 CLI 与 npm 打交道
(三)npm更新:
查看版本:nvm -V
更新版本:nvm install npm@latest -g
(四)npm更改全局目录:
查看npm全局目录:
npm root -g
修改全局包位置 :
npm config set prefix '目标目录'
查看修改结果
npm config get prefix或npm root -g
(五)package.json
A.做用:
1.做为一个描述文件,描述了你的项目依赖哪些包
2.容许咱们使用 “语义化版本规则”(后面介绍)指明你项目依赖包的版本
3.让你的构建更好地与其余开发者分享,便于重复使用
B.建立:
npm init 便可在当前目录建立一个 package.json 文件:
C.内容:
基本配置:
1.name:项目的名字
2.version:项目的版本
3.description:描述信息,有助于搜索
4.main: 入口文件,通常都是 index.js
5.scripts:支持的脚本,默认是一个空的 test
6.keywords:关键字,有助于在人们使用 npm search 搜索时发现你的项目
7.author:做者信息
8.license:默认是 MIT
9.bugs:当前项目的一些错误信息,若是有的话
注:
若是 package.json 中没有 description 信息,npm 使用项目中的 README.md 的第一行做为描述信息。这个描述信息有助于别人搜索你的项目,所以建议好好写 description 信息。
依赖包配置:
1.dependencies:在生产环境中须要用到的依赖
2.devDependencies:在开发、测试环境中用到的依赖
(六)npm的包版本规范和package.json的使用规范
npm版本规范:
若是一个项目打算与别人分享,应该从 1.0.0 版本开始。之后要升级版本应该遵循如下标准:
补丁版本:解决了 Bug 或者一些较小的更改,增长最后一位数字,好比 1.0.1
小版本:增长了新特性,同时不会影响以前的版本,增长中间一位数字,好比 1.1.0
大版本:大改版,没法兼容以前的,增长第一位数字,好比 2.0.0
Package.json的版本书写:
咱们能够在 package.json 文件中写明咱们能够接受这个包的更新程度(假设当前依赖的是 1.0.4 版本):
若是只打算接受补丁版本的更新(也就是最后一位的改变),就能够这么写:
1.0
1.0.x
~1.0.4
若是接受小版本的更新(第二位的改变),就能够这么写:
1
1.x
^1.0.4
若是能够接受大版本的更新(天然接受小版本和补丁版本的改变),就能够这么写:
x
(七)npm下载包
安装方式:
若是你只是想在当前项目里用 require() 加载使用,那你能够安装到本地 npm install 默认就是安装到本地的
若是你想要在命令行里直接使用,好比 grunt CLI,就须要安装到全局了
若是在你的项目里有 package.json 文件,运行 npm install 后它会查找文件中列出的依赖包,而后下载符合语义化版本规则的版本。 npm install 默认会安装 package.json 中 dependencies 和 devDependencies 里的全部模块。 若是想只安装 dependencies 中的内容,可使用 --production 字段:
npm install --production
1.本地安装:
1)安装指定版本:
$ npm install sax@latest :最新版本
$ npm install sax@0.1.1 :指定版本
$ npm install sax@" >=0.1.0 <0.2.0” :安装0.1.0到0.2.0版本
注:
有时下载会报错:npm install error saveError ENOENT: no such file or directory, 解决办法: - 在目录下执行 npm init 建立 package.json,输入初始化信息 - 而后再执行下载命令
2)安装参数 --save 和 --save -dev
添加依赖时咱们能够手动修改 package.json 文件,添加或者修改 dependencies devDependencies 中的内容便可。
另外一种更酷的方式是用命令行,在使用 npm install 时增长 --save 或者 --save -dev 后缀:
npm install --save 表示将这个包名及对应的版本添加到 package.json的 dependencies
npm install --save-dev 表示将这个包名及对应的版本添加到 package.json的 devDependencies
3)更新本地package
有时候咱们想知道依赖的包是否有新版本,可使用 npm outdated 查看,若是发现有的包有新版本,就可使用 npm update 更新它,或者直接 npm update 更新全部:
npm update 的工做过程是这样的:
先到远程仓库查询最新版本
而后对比本地版本,若是本地版本不存在,或者远程版本较新
查看 package.json 中对应的语义版本规则 (必定注意规则)
若是当前新版本符合语义规则,就更新,不然不更新
4)卸载本地 package
卸载一个本地 package 很简单,npm uninstall
2.全局安装
1)安装
npm install -g <packages>
2)权限处理(非windows处理)
在全局安装时可能会遇到 EACCES 权限问题,解决办法办法有以下 2种:
1.sudo npm install -g jshint,使用 sudo 简单粗暴,可是治标不治本
2.修改 npm 全局默认目录的权限 先获取 npm 全局目录:npm config get prefix,通常都是 /usr/local; 而后修改这个目录权限为当前用户:
sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
3)更新全局包
想知道哪些包须要更新,可使用 npm outdated -g --depth=0,而后使用 npm update -g 更新指定的包;
要更新全部全局包,可使用 npm update -g,能够发现对比本地的,只是多了个 -g。
4)卸载全局包
npm uninstall -g <package>
3.其余命令
Npm run: 运行 package.json 中 scripts 指定的脚本
npm install from github: 从github下载资源
npm install git://github.com/package/path.git ;
npm info:npm info 能够查看指定包的信息:
- Npm发布包:
1.注册:
npm网站地址:https://www.npmjs.com/
npm网站注册地址:https://www.npmjs.com/signup
2.命令行登陆
Windows直接cmd到命令行:
输入如下命令,会提示输入用户名、密码、邮箱,这些都是注册时填写过的。
npm login
3.建立项目
建立一个testxxxxx文件夹,cd到testxxxxx文件夹中,而后下载基础配置文件:
1
2 //输入如下命令,会提示配置包的相关信息,名称版本等等,都是包的基本配置信息
npm init
配置完毕开始写本身的包内代码:
建立一个index.js文件,文件内的代码以下,直接输出123456789
module.exports = 123456789;
4.发布:
开始命令行发布包,命令以下:npm publish testxxxxx
发布完毕,在npm网站上搜索,就能够搜索到本身刚刚发布的包了。
5.验证下载:
6.撤销发布
接下来讲明一下怎么撤销本身发布的版本。这只是一个测试的包,最好固然仍是撤销下来:
删除要用force强制删除。超过24小时就不能删除了。本身把握好时间。
npm --force unpublish testxxxxx
- Npm修改镜像源:
因为npm的源在国外,因此国内用户使用起来各类不方便。部分国内优秀的npm镜像资源,国内用户能够选择使用
1.淘宝npm镜像
搜索地址:http://npm.taobao.org/
registry地址:http://registry.npm.taobao.org/
2.cnpmjs镜像
搜索地址:http://cnpmjs.org/ registry
地址:http://r.cnpmjs.org/
3.如何使用
有不少方法来配置npm的registry地址,下面根据不一样情境列出几种比较经常使用的方法。以淘宝npm镜像举例:
1)临时使用
npm --registry https://registry.npm.taobao.org
2)持久使用(推荐使用)
npm config set registry https://registry.npm.taobao.org
配置后可经过下面方式来验证是否成功 npm config get registry
3)经过cnpm使用 (也可使用cnpm) (经常使用)
npm install -g cnpm --registry=https://registry.npm.taobao.org
4.恢复npm源镜像:
若是将npm的镜像地址改变后,在发布包时,应该将镜像改回:
npm config set registry https://registry.npmjs.org/
- 断点调试
(一)Node-inspector的浏览器调试
1.安装node-inspector运行环境
安装命令:npm install -g node-inspector
注意:a、参数-g 将node-inspector安装到系统环境变量中,能够在任何路径下执行,尽可能保留。
b、若是是Linux或Unix系统,须要使用root权限安装
2. 启动node-inspector
node-inspector启动后会生成一个服务,主要负责调试工具与nodejs程序之间的沟通工做,一个桥梁。
a、window:直接在任意路径下执行 node-inspector,进行守护
b、Linux || Unix:node-inspector &
将node-inspcetor做为后台服务,这样就不怕误操做,把窗口关掉了。出现进程PID,表示node-inspcetor已经成为后台进程,能够ctrl+c结束当前任务,node-inspcetor进程依然保持。若是想中止能够 kill -9 pid 杀掉node-inspcetor进程。
3.打开chrome,输入地址 http://127.0.0.1:8080/debug?port=5858
相信你们都看到有错误了,知道什么缘由吗? 恭喜你猜对了,咱们的NodeJS程序还没起来呢,目前先到这,如今须要回过头来看看咱们的NodeJS程序的变化。
4. 打开NodeJS的调试模式
node --debug app.js debugger的监听端口是5858,这个端口能够修改
5.再次打开chrome,刷新页面,chrome经过node-inspector服务链接到nodejs服务上了,并显示nodejs应用的入口文件内容。
总结:
一、node-inspector依赖nodejs的运行环境。
二、调试过程当中node-inspector的服务不要重启,只须要在重启nodejs应用后刷新一下chrome的页面便可。
三、严格的来讲node-inspector不是一个完整的调试工具,它须要一个可视化的调试界面来展现全部的调试信息,node-inspector是调试界面与nodejs之间的桥梁,是调试界面能与nodejs沟通。
- Vscode的debug
1.以下图进行点击按钮,弹出配置页面
2.添加node处理程序
3.配置程序名称和程序路径
4.配置完毕启动程序
5.打断点
结尾:主要的学习是配置环境,其实Node.js更多扮演的是一个中间层的做用,不少国内企业对Node.js的依赖并非很高,但愿感受对本身有帮助的小伙伴能够积极的点赞加关注!!