近几年Node.js大火特火,以至于所有程序员都想学它。不过也总有我这样懒的迟迟未动。那些一样节奏过慢的小伙伴们,今天我们一起来入个门吧。
一、Node.js简介
Node.js是什么呢,Node.js和浏览器都是js的运行环境。区别在于,Node.js打破了js运行在浏览器端的限制,可以让js运行在服务端。
那么它有哪些优点呢?一是事件驱动,二是非阻塞I/O模型,三是单线程,四是拥有世界最大的开源生态系统NPM。
安装方法有两种,一种是官网下载之后配置环境变量,然后通过REPL模式使用;第二种是使用nvm命令安装使用。不熟悉的小伙伴可以自行搜索。
二、Node全局对象
先了解一下,js的全局对象有四种:
第一种是浏览器专属的,如window、alert等;第二种是Node专属的,如process / Buffer / __dirname / __filename;第三种是二者共有但实现方式不同的,如console / setTimeout /SetInterval等;第四种是二者共有并且属于ECMAScript语言定义的部分,如Date / String / Promise等。
可见,Node不存在document和window,我们来了解一下Node的几个全局对象吧~
1. process
它用来管理当前Node.js的进程状态,提供与操作系统的简单接口。它的常用属性有:进程编号pid, 系统环境变量env, 命令行执行此脚本时的输入参数argv, 当前操作系统的平台platform等。
2. Buffer
这个全局对象可以让js轻松处理二进制数据流
3. __filename和__dirname
当前所运行node脚本的文件路径和所在目录路径。
我们来使用这几个全局对象看一下node的一大特点吧:
setTimeout(() => { console.log('Hello World!');}, 3000);
console.log('当前进程 ID', process.pid);
console.log('当前脚本路径', __filename);
const time = new Date();console.log('当前时间', time.toLocaleString());
执行上述代码会发现,在定时器等待的3秒内,程序并没有阻塞,而是继续向下执行。这就是 Node.js 的异步非阻塞!
Node.js 能够在等待的同时继续处理新的请求,大大提高了系统的吞吐率。
三、模块机制
敲黑板啦~~~
这是入门Node的关键。
1. 模块机制的引入原因
为什么要引入模块机制呢?因为之前js没有模块化机制。导入js模块的方式是使用一系列script标签,这种方式存在一些明显的问题,比如容易产生命名冲突,js文件之间不能互相访问,导入的script标签不能轻易修改或删除等。针对js模块化机制的缺失,提出了AMD和CommonJS两大规范。AMD在浏览器中使用普遍,CommonJS规范致力于提供统一的接口API,Node.js实现的就是这一模块标准。
2. 是什么
什么是Node模块呢?Node模块分为核心模块(Node内置)和文件模块。文件模块是用户编写的或npm安装的模块,它可以是一个单独的文件(.js / .node / .json)或目录。是目录时,这个模块的入口就是其中package.json文件里main字段指向的文件,或者是其中的index(.js / .node / .json)文件。
3. 模块机制的实现
接下来,我们根据node的三个全局对象require / exports / module来分析一下模块机制是怎么实现的。
(1)reqiure导入模块
require('模块名称')或require('模块的相对路径')
为什么这样导入就能找到对应模块呢?以前使用的时候一直有这个疑问。实际上每个模块都有一个路径搜索列表module.paths,等下我们会讲到。
(2)exports导出模块
将模块中的函数添加到exports对象中,就可以在外面调用这个函数/方法了。
const myModule = require('./myModule');
myModule.add(1, 2);
上面的add函数还可以用解构赋值的方式获取:
const { add } = require('./myModule');
(3)module
module对象有id / path / filename / exports / parent / children / loaded / paths等字段。
A. id——模块的唯一标识符
B. path和filename——模块所在路径和文件名
C.exports——模块导出的内容,即上文说的exports对象是指向module.exports的应用。比如上文导出了add函数,则exports字段里有add函数。
D.parent和children——记录模块之间的导入关系,如main.js中require里myModule.js,main就是myModule.js的parent。
E.loaded——模块是否被加载
F.paths——搜索文件模块的路径列表
(4)module.exports
请记住这句话:exports对象本质上是module.exports的引用。两种导出方式:
第一种:exports.add=add;就是module.exports.add=add;
第二种:module.exports=add; 即把add赋值给module.exports对象
因此,在require时,第一种导出方式要访问add属性,第二种可以直接使用add函数。
const myModule = require('myModule');
myModule.add(1,3); // 第一种
const add = require('myModule');
add(1,3); // 第二种
四、命令行开发:接受输入参数
Node.js还提供了Grunt / Gulp / Webpack等命令行工具。
1. 通过process.argv读取命令行参数
上文提到过process是node的一个全局对象,它有一个argv属性能获取命令行参数的数组。
如果创建一个args.js文件,写入代码console.log(process.argv);
再运行命令 node args.js --time 5 --message "Hello world!"
这个数组的第0个元素是node的实际路径,第1个元素是创建的js文件的路径,后面的是输入的所有参数。
2. npm
(1)npm -v 查看版本
(2)npm init 初始化npm项目,创建package.json文件
(3)npm install xxx 安装npm包(安装之后,package.json文件里会多一个dependencies字段记录项目的直接依赖。denpendencies字段中还可以指定版本号)
(4)package-lock文件:锁定全局依赖的精确版本号
3. npm scripts
(1)预定义脚本:运行npm <scriptName>,如test / start / install / publish等
(2)自定义脚本:运行npm run <scriptName>,如npm run custom
五、监听exit事件
Node中的事件都是通过events核心模块中的EventEmitter这个类实现的。EventEmitter包括on(监听事件发生)和emit(触发新事件)两个方法。
const EventEmitter = require('events').EventEmitter;
const emitter = new EventEmitter();
// 监听 connect 事件,注册回调函数
emitter.on('connect', function (username) {
console.log(username + '已连接');
});
// 触发 connect 事件,并且加上一个参数(即上面的 username)
emitter.emit('connect', '一只图雀');
六、基础API
我们来说三个常用的模块——path、fs和http。使用这些模块之前,都要先引用。引用方式以path为例,如:
const path = require('path');
1. path:路径,用来处理处理文件路径和目录路径,使用方法上代码:
path.basename('/foo/bar/baz/asdf/quee.html'); // 文件名
path.dirname('/foo/bar/baz/asdf/quee'); // 目录名
path.extname('index.html'); // extname后缀名
path.format({ // 对象格式化为字符串路径
dir: 'C:\\path\\dir',
base: 'file.txt'
});
path.parse('C:\\path\dir\\file.txt'); // 字符串路径格式化为对象
path.join('/foo', 'bar', 'baz/asdf', 'quee', '..')
2. fs:文件系统,用于与文件系统进行交互。常用的API有writeFile、readFile、mkdir、opendir、readdir、rmdir等。
可以传3个参数,分别是path、data、callback。来个栗子:
fs.writeFile(path.join(__dirname, 'message.txt'), '您好呀', function(err){
if(err) throw err;
console.log('写入成功!')
})
3. http:发送请求,常用的API如:http.createServer();
七、搭建静态服务器
搭建静态服务器一般需要四步:加载模块--->创建http服务--->监听请求事件--->监听接口, 开启服务。
const http = require('http');
const server = http.createServer();
server.on('request', function(req, res){
res.setHeader('Content-Type','text/html;charset=utf-8'); // 解决乱码
res.write('<h1>hello world!</h1>');
res.end();
});
server.listen(8081, function(){
console.log('http://localhost:8081');
});
让我们来练习一下,根据用户的不同请求,响应现有的html文件:
const http = require('http');
const fs = require('fs');
const path = require('path');
http.createServer(function(req,res){
if(req.url==='/' || req.url==='/index'){
fs.readFile(path.join(__dirname, 'htmls', 'index.html'), function(){
if(err) throw err;
res.end(data);
})
}else if(req.url === '/resourcestyle.css'){
fs.readFile(path.join(__dirname, 'htmls','resource','style.css'),
function(){
if(err) throw err;
res.end(data);
}
)
}else{
fs.readFile(path.join(__dirname, 'htmls','404.html'), function(){
if(err) throw err;
res.end(data);
})
}
}).listen(8080, function(){
console.log('服务已启动,请访问:http://localhost:8080/');
});
好啦,Node.js入门笔记先写到这里了。