Node.js入门笔记——我们踩的不是坑,是技术盲区

近几年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等。

js全局对象.png

可见,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入门笔记先写到这里了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容