一、node是什么?
官方解释
-
node.js is a JavaScript runtime built on chrome's V8 JavaScript engine
-
JavaScript-提供灵活的语法 ; V8-为JavaScript运行提供足够高的效率和实现
-
Node.js 是单进程单线程应用程序,但是因为V8引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高
-
编译语言的运行环境(编译器+操作系统的底层支持)
-
node=c++代码编写+ECMAScript语法规范
个人理解
-
JavaScript可以在后端运行的一个环境
-
Node.js开发的目的就是为了用JavaScript编写Web服务器程序。
-
JavaScript实际上已经统治了浏览器端的脚本,其优势就是有世界上数量最多的前端开发人员。
-
如果已经掌握了JavaScript前端开发,再学习一下如何将JavaScript应用在后端开发,就是名副其实的全栈了。
node 特点
在Node上运行的JavaScript相比其他后端开发语言有何优势
-
最大的优势是借助JavaScript天生的事件驱动机制加V8高性能引擎,使编写高性能Web服务轻而易举。
-
其次,JavaScript语言本身是完善的函数式语言,在前端开发时,开发人员往往写得比较随意,让人感觉JavaScript就是个“玩具语言”。但是,在Node环境下,通过模块化的JavaScript代码,加上函数式编程,并且无需考虑浏览器兼容性问题,直接使用最新的ECMAScript 6标准,可以完全满足工程上的需求。
Node 自带了交互式解释器REPL
同步操作异步操作
同步操作的好处是代码简单,缺点是程序将等待IO操作,在等待时间内,无法响应其它任何事件。而异步读取不用等待IO操作,但代码较麻烦。
1、组成
- 引入 required 模块:我们可以使用 require 指令来载入 Node.js 模块。
- 创建服务器:服务器可以监听客户端的请求,类似于 Apache 、Nginx 等 HTTP 服务器。
- 接收请求与响应请求 服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。
2、内部机制?
- node中的IO:node程序在Libuv支持下与系统磁盘和网络交互的过程
- node没有提供多线程的支持,用户编写的代码只能运行在当前线程中
- 开发者无法在一个独立进程中添加新的线程,但是可以派生出多个进程来达到并行完成工作的目的
- node中的并发:node虽然运行在单线程中,但任然支持高并发,就是依靠事件循环实现的
二、node模块化系统
为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统;Node.js 提供了 exports 和 require 两个对象
- exports 是模块公开的接口
- require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
1、javascript模块规范
1.1、commonJS
- commonJS将每个文件都看作一个模块,模块内部的定义的变量都是私有的,无法被其他模块使用
- 模块加载是同步的,受限于宽带速度。
- 代表:nodeJs
1.2、AMD
- Asynchronous Module Definition 异步模块定义,他采用异步方式加载模块,模块的加载不影响他后面语句的进行。
- 代表:RequireJs
2、基础模块fs
nodejs官方推荐在使用fs模块读取文件时使用绝对路径,而不是相对路径值得注意的路径问题
let PUBLIC_PATH = path.resolve(__dirname, '../public')
fs模块同时提供同步和异步方法
读取文件
读取文本文件
sample.txt文件必须在当前目录下
且文件编码为utf-8
'use strict';
var fs = require('fs');
fs.readFile('sample.txt', 'utf-8', function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
复制代码
读取二进制文件
-
当读取二进制文件时,不传入文件编码时,回调函数的data参数将返回一个Buffer对象。
-
在Node.js中,Buffer对象就是一个包含零个或任意个字节的数组(注意和Array不同)。
-
Buffer对象可以和String作转换,例如,把一个Buffer对象转换成String:
'use strict';
var fs = require('fs');
fs.readFile('sample.png', function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
console.log(data.length + ' bytes');
}
});
复制代码
同步读取文件
同步读取的函数和异步函数相比,多了一个Sync后缀,并且不接收回调函数,函数直接返回结果
如果同步读取文件发生错误,则需要用try...catch捕获该错误:
'use strict';
var fs = require('fs');
var data = fs.readFileSync('sample.txt', 'utf-8');
console.log(data);
复制代码
try {
var data = fs.readFileSync('sample.txt', 'utf-8');
console.log(data);
} catch (err) {
// 出错了
}
复制代码
写文件
往文本文件中写入数据
'use strict';
var fs = require('fs');
var data = 'Hello, Node.js';
fs.writeFile('output.txt', data, function (err) {
if (err) {
console.log(err);
} else {
console.log('ok.');
}
});
复制代码
同步写入
'use strict';
var fs = require('fs');
var data = 'Hello, Node.js';
fs.writeFileSync('output.txt', data);
复制代码
获取文件信息stat
'use strict';
var fs = require('fs');
fs.stat('sample.txt', function (err, stat) {
if (err) {
console.log(err);
} else {
// 是否是文件:
console.log('isFile: ' + stat.isFile());
// 是否是目录:
console.log('isDirectory: ' + stat.isDirectory());
if (stat.isFile()) {
// 文件大小:
console.log('size: ' + stat.size);
// 创建时间, Date对象:
console.log('birth time: ' + stat.birthtime);
// 修改时间, Date对象:
console.log('modified time: ' + stat.mtime);
}
}
});
复制代码
运行结果
isFile: true
isDirectory: false
size: 181
birth time: Fri Dec 11 2015 09:43:41 GMT+0800 (CST)
modified time: Fri Dec 11 2015 12:09:00 GMT+0800 (CST)
复制代码
3、基础模块stream
- 流的特点是数据是有序的,而且必须依次读取,或者依次写入,不能像Array那样随机定位
- 在Node.js中,流也是一个对象,我们只需要响应流的事件就可以了
- data事件表示流的数据已经可以读取了
- end事件表示这个流已经到末尾了,没有数据可以读取了
- error事件表示出错了。
从文件读取数据时,可以打开一个文件流,然后从文件流中不断地读取数据
所有可以读取数据的流都继承自stream.Readable
data事件可能会有多次,每次传递的chunk是流的一部分数据
'use strict';
var fs = require('fs');
// 打开一个流:
var rs = fs.createReadStream('sample.txt', 'utf-8');
rs.on('data', function (chunk) {
console.log('DATA:')
console.log(chunk);
});
rs.on('end', function () {
console.log('END');
});
rs.on('error', function (err) {
console.log('ERROR: ' + err);
});
复制代码
向文件写入数据时,只需要把数据不断地往文件流中写进去就可以了
所有可以写入的流都继承自stream.Writable
'use strict';
var fs = require('fs');
var ws1 = fs.createWriteStream('output1.txt', 'utf-8');
ws1.write('使用Stream写入文本数据...\n');
ws1.write('END.');
ws1.end();
var ws2 = fs.createWriteStream('output2.txt');
ws2.write(new Buffer('使用Stream写入二进制数据...\n', 'utf-8'));
ws2.write(new Buffer('END.', 'utf-8'));
ws2.end();
复制代码
pipe
就像可以把两个水管串成一个更长的水管一样,两个流也可以串起来
一个Readable流和一个Writable流串起来后,所有的数据自动从Readable流进入Writable流,这种操作叫pipe
在Node.js中,Readable流有一个pipe()方法,k可以实现文件复制的类似操作
- 默认情况下,当Readable流的数据读取完毕,end事件触发后,将自动关闭Writable流
- 如果我们不希望自动关闭Writable流,需要传入参数:
readable.pipe(writable, { end: false });
'use strict';
var fs = require('fs');
var rs = fs.createReadStream('sample.txt');
var ws = fs.createWriteStream('copied.txt');
rs.pipe(ws);
复制代码
4、基础模块http
- 应用程序并不直接和HTTP协议打交道,而是操作http模块提供的request和response对象。
- request对象封装了HTTP请求,我们调用request对象的属性和方法就可以拿到所有HTTP请求的信息;
- response对象封装了HTTP响应,我们操作response对象的方法,就可以把HTTP响应返回给浏览器。
'use strict';
// 导入http模块:
var http = require('http');
// 创建http server,并传入回调函数:
var server = http.createServer(function (request, response) {
// 回调函数接收request和response对象,
// 获得HTTP请求的method和url:
console.log(request.method + ': ' + request.url);
// 将HTTP响应200写入response, 同时设置Content-Type: text/html:
response.writeHead(200, {'Content-Type': 'text/html'});
// 将HTTP响应的HTML内容写入response:
response.end('<h1>Hello world!</h1>');
});
// 让服务器监听8080端口:
server.listen(8080);
console.log('Server is running at http://127.0.0.1:8080/');
复制代码
引入path模块和URL模块
URL模块
'use strict';
var url = require('url');
console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash'));
复制代码
输出结果
Url {
protocol: 'http:',
slashes: true,
auth: 'user:pass',
host: 'host.com:8080',
port: '8080',
hostname: 'host.com',
hash: '#hash',
search: '?query=string',
query: 'query=string',
pathname: '/path/to/file',
path: '/path/to/file?query=string',
href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' }
复制代码
path模块
'use strict';
var path = require('path');
// 解析当前目录:
var workDir = path.resolve('.'); // '/Users/michael'
// 组合完整的文件路径:当前目录+'pub'+'index.html':
var filePath = path.join(workDir, 'pub', 'index.html');
// '/Users/michael/pub/index.html'
复制代码
实现一个文件服务器
'use strict';
var
fs = require('fs'),
url = require('url'),
path = require('path'),
http = require('http');
// 从命令行参数获取root目录,默认是当前目录:
var root = path.resolve(process.argv[2] || '.');
console.log('Static root dir: ' + root);
// 创建服务器:
var server = http.createServer(function (request, response) {
// 获得URL的path,类似 '/css/bootstrap.css':
var pathname = url.parse(request.url).pathname;
// 获得对应的本地文件路径,类似 '/srv/www/css/bootstrap.css':
var filepath = path.join(root, pathname);
// 获取文件状态:
fs.stat(filepath, function (err, stats) {
if (!err && stats.isFile()) {
// 没有出错并且文件存在:
console.log('200 ' + request.url);
// 发送200响应:
response.writeHead(200);
// 将文件流导向response:
fs.createReadStream(filepath).pipe(response);
} else {
// 出错了或者文件不存在:
console.log('404 ' + request.url);
// 发送404响应:
response.writeHead(404);
response.end('404 Not Found');
}
});
});
server.listen(8080);
console.log('Server is running at http://127.0.0.1:8080/');
复制代码
5、npm理解
定义:是什么
- npm其实是Node.js的包管理工具(package manager)
意义: 为啥我们需要一个包管理工具呢?
因为我们在Node.js上开发时,会用到很多别人写的JavaScript代码。 如果我们要使用别人写的某个包,每次都根据名称搜索一下官方网站,下载代码,解压,再使用,非常繁琐。 于是一个集中管理的工具应运而生:大家都把自己开发的模块打包后放到npm官网上,如果要使用,直接通过npm安装就可以直接用,不用管代码存在哪,应该从哪下载。
使用严格模式
如果在JavaScript文件开头写上'usestrict';那么Node在执行该JavaScript时将使用严格模式。
但是,在服务器环境下,如果有很多JavaScript文件,每个文件都写上'usestrict'很麻烦。
我们可以给Nodejs传递一个参数,让Node直接为所有js文件开启严格模式: node --use_strict calc.js
怎样高效开发node程序
- 使用文本编辑器来开发Node程序,最大的缺点是效率太低,运行Node程序还需要在命令行单独敲命令。如果还需要调试程序,就更加麻烦了。所以我们需要一个IDE集成开发环境,让我们能在一个环境里编码、运行、调试,这样就可以大大提升开发效率。
对比
- Java的集成开发环境有Eclipse,Intellij idea等,
- C#的集成开发环境有Visual Studio,
- Node.js的集成开发环境:Visual Studio Code
- 优点:启动速度快,执行简单,调试方便
- 优点:可以跨平台Windows、Mac和Linux通用