Node.js学习笔记(一)基础介绍

来源:互联网 发布:mac arp 嗅探工具dmg 编辑:程序博客网 时间:2024/05/01 09:54

什么是Node.js

官网介绍:

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 Node.js 的包管理器 npm,是全球最大的开源库生态系统。

Node.js不是一个语言,也不是一个库,更不是一个框架。只是一个运行环境,也就是平台。在Node.js这个平台上,我们可以使用JavaScript 来编写程序,实现相应的功能。

使用Node.js 可以轻松地进行服务器端应用开发,PHP、Python、Ruby能做的事情,Node.js几乎都能做,而且可以做得更好。那么既然已经有了PHP等后台语言,为什么还需要Node.js?这里我们要知道一个理念——任何一个有点小规模的产品,都不会只使用一门技术或一种语言,结合多种语言来实现,然后在不同的场景、需求上,使用相应的语言和技术去实现。目的就是为了提高产品的性能。每一张语言都有自己的优缺点,我们要利用好它们的优点。

我们在考虑是否应该使用这门语言的时候,就是要搞清楚它的优缺点。时效性要求比较高的应用,Node.js是最佳的。

Node.js 初体验

官网下载安装好Node.js,接下来我们来初步感受一下。

Hello world

打开cmd窗口,输入node命令,就可以进入Node.js的运行环境,在这里任何的JavaScript代码都可以编写并执行,除了BOM 和 DOM 的内容。如果输入了 BOM 和 DOM 的一些内容,在 Node 平台就会出错。但是,node 平台也提供了一个全局对象——console

顺便说一句,像这种 cmd 的窗口,有一个专有名词 REPL

  • R: read,读取,等待用户的输入
  • E: eval,执行代码,输入代码完成之后,按回车键,可以执行代码
  • P: print,打印,输出结果
  • L: loop,循环,重复这个过程
执行js文件

上述的命令窗口模式操作node,平时很少会那样用。我们还是用编辑器编写js代码,然后会通过cmd窗口来执行这个文件。

过程也很简单,不需要进入node的REPL环境,打开cmd 窗口,输入 “node + 文件名(完整路径)” 执行即可。

搭建服务器

下面我们用Node.js来搭建HTTP服务器,

第一步:编写server.js文件如下:

<!--创建一个HTTP服务器-->// 载入http核心模块const http = require("http");// 创建一个server对象const server = http.createServer((req,res)=>{   // 通过res对象,输出一些内容   res.writeHead(200,{"Content-Type":"text/html;charset=utf-8"});   res.write("<h1>http服务器</h1>");   res.write("<p>使用Node.js创建一个http服务器</p>");   res.end();});// 开启server的监听server.listen(3000,()=>{    console.log("http server is listening in port 3000...");});

第二步:打开cmd窗口,执行server.js文件

第三步:在浏览器中,输入 localhost:3000 访问,即可看到结果页面。

模块机制

在Node.js中,所谓的模块,其实就是一个文件。一般而言,就是js文件/json文件。一个文件就是一个模块,模块是Node.js应用程序的基本组成部分。

模块分类

简单划分,可以将Node.js中的模块分成两大类:

  • 系统模块(核心模块)
  • 用户模块

其中,系统模块是Node.js自带的模块,比如http、fs、net、url、path等,可以直接使用。核心模块,Node.js中是内置好的。

用户模块不是Node.js本身的,又可以分为两种:

  • 第三方模块,一些比较通用的,但是Node.js自身没有提供的,这一类数量很庞大
  • 自定义模块,通常是在当前项目中,需要根据需求自己编写的js代码

加载模块

如何在Node.js中加载模块呢?

根据模块类型的不同,加载的方式略有不同。相同的是,都会使用 require 函数。格式:require(模块路径);

模块路径的写法,根据模块类型的不同,写法也不同:

  • 核心模块和第三方模块的写法一样,只需要写上模块名即可。
  • 自定义模块,需要使用相对路径来引入,必须使用 ./ 或者 ../ 开头。
(1)核心模块的载入

核心模块是Node.js自带的,本身就具备的,直接载入就可以使用。

(2)第三方模块的载入

首先,需要保证有一个第三方模块,需要先安装第三方模块,使用npm 命令安装即可。

npm install 模块名。

本地安装的时候,需要先进入对应的目录,使用命令来安装。

虽然,加载模块的写法和核心模块一样,但是原理不太一样。如果是第三方模块,它最加载的时候,一定会在当前目录下,寻找 node_modules文件夹,在里面找对应的这模块,如果找到,就直接使用。如果没有找到,则会到父级目录中找node_modules文件夹,找是否有该模块,重复这个过程,直到根目录。如果到根目录,也没有找到,就会报错。

(3)加载自定义模块

先创建一个模块,其实就是一个js文件:mymodule.js

然后,使用require引入,const myModule = require('./mymodule.js');

如果没有使用相对路径,会报错。

还有几个细节需要注意一下:

  • 模块就是文件,一般就是js文件,js文件是有后缀的,后缀是可以省略的,核心模块和第三方模块必须省略,自定义模块比较随意,两种都行。
  • 如果没有写后缀,require 方法在加载的时候,当文件不带后缀,Node.js会依照 目录 ->.js-> .json-> .node 的顺序进行查找,如果所有模块路径都找不到该文件,则抛出异常。

自定义模块的实现

默认情况下,任何一个模块,被载入时,得到的是一个空对象,就是 module.exports;我们可以直接在js文件中使用module.exports

在Node.js中,还有一个对象exports,它实际上是module.exports的一个引用,相当于exports = module.exports

如果使用的是module.exports,那么直接赋值即可,如果使用的是exports,不能直接赋值,原因很简单嘛,又涉及到了对象数值的传递了。所以小结一下:

任何一个模块(js文件)被 require 时,返回的是 module.exports 对象,默认为空。Node.js为了方便,提供了一个exports对象,指向module.exports,相当于执行了exports=module.exports。如果需要提供对外接口,需要给module.exports赋值为一个新的对象,或者使用exports.属性名=值的形式。

为了保险起见,我们可以写成:exports = module.exports = {}

Node.js进行Web开发的核心

Server 对象

作用:用于创建服务器对象,提供HTTP服务,在Node.js中,Server对象充当了HTTP服务器的角色。它提供了一个监听端口的底层套接字和接收请求,然后发送响应给客户端链接的处理程序。

创建对象:http.createServer();

核心方法:listen;启动监听,启动 http 服务,提供给用户来访问,有一个关键参数port,指定监听的端口

重要事件:request:接收请求时触发,传递两个参数,IncommingMessage 对象和 ServerResponse 对象;
listening:调用listen 时触发,同理也可以作为listen的回调函数来进行绑定。

实际上,用户输入url打开网页就是一个请求事件,如果我们需要响应用户的请求,那么必须先注册该事件。需要对server绑定一个request事件,在Node.js中,绑定事件使用 on 来完成。为了简化代码的书写,Node.js将注册request事件,使用回调函数的方式来实现:

const server = http.createServer();server.on('request',(req,res)=>{...});简化成const server = http.createServer((req,res)=>{...});

ServerResponse 对象

在刚才的 createServer 的回调函数中,使用了 res 对象,这个对象实际上就是 ServerResponse 对象,服务器端的响应对象。

作用:用来写http响应,包括响应状态行、响应头信息和响应正文。

创建:是自动创建的,作为回调函数的参数,自动创建好的一个对象。在createServer方法中的回调函数中,作为第二个参数来出现的。

重要属性和方法:

  • writeHead用于写 响应状态行和响应头。只能调用一次
  • write负责写 响应正文。可以调用多次
  • end,响应完毕,只能调用一次,必须要调用。
  • setHeader,也可以通过这个方法设置头信息
  • getHeader,用来获取头信息的。

IncommingMessage 对象

作用:要理解其作用,需要看看HTTP请求和响应的过程:浏览器到服务器端的请求过程,作为服务器端需要获取来自客户端的信息;服务器端到浏览器端的响应过程,作为浏览器端也需要获取来自服务器的一些信息。需要有一个对象,来表示这个信息,这个对象就是 incomingMessage对象。简单来说,它的作用就是获取对方(浏览器端或者服务器端)的一些信息。

如何创建:不需要手动创建,也是作为回调函数的参数出现的。站在服务器端的角度来看,这个incomingMessage 就是 createSever回调函数中的req参数。

重要的属性和方法:

  • httpVersion:请求/响应的HTTP版本
  • headers:请求/响应头信息
  • rawHeaders:原始请求/响应头信息
  • method:请求方式,仅对Server获得到的请求(request)有效
  • url:请求的url字符串,仅对Server获得到的请求(request)有效
  • statusCode:响应状态码,仅对从ClientRequest获得响应(response)有效

重要事件:

  • data:数据传递时触发
  • end:没有更多数据提供时触发

由Readable Stream 提供了一下接口,然后 IncomingMessage实现了这些接口

ClientRequest 对象

什么是ClientRequest对象呢?就是客户端请求对象,表示一个已经产生且正在进项中的HTTP请求。

使用Node创建Web客户端,需要引入http模块。

如何创建:有两种方法,都是http对象的方法

  • request
  • get

    const cr = http.request();const cr = http.get();

核心方法:

  • write:把一个正文写入请求;
  • end:完成请求,也可以写入数据,end方法,完成请求的动作,针对request方法创建的对象而言。

对于get请求,请求正文是空的,不用写,此时,不需要write方法;对于post请求,请求正文是需要写的,此时,需要写write方法。

重要事件:response,需要注册一个响应事件,当发出请求后,需要监听来自服务器端的响应。

在响应时的监听器中,有一个重要的对象 IncommingMessage 对象,这个对象有两个重要的事件。

  • data,数据传递时触发,
  • end,没有更多数据提供时触发

使用这两个事件就可以获取来自服务器端的响应内容。代码如下:

const http = require('http');<!--创建一个ClientRequest对象-->const cr = http.request({host:'www.baidu.com'});//注册response事件cr.on('response',(res)=>{    let str = '';    //注册data事件    res.on('data',(chunk)=>{        str += chunk;    });    res.on('end',()=>{        console.log(str);    });});//发出请求cr.end();

针对上述过程,我们还可以使用get方法来简化,其中,get方法,会自动发出请求,不需要调用end方法。

const http = require('http');http.get('http://www.baidu.com',(res)=>{    let str = "";    res.on('data',(chunk)=>{        str += chunk;    });    res.on('end',()=>{        console.log(str);    });});

利用这个可以搞一下爬虫,爬一下网络资源。

简单应用

URL路由

URL:Uniform Resource Locator。统一资源定位器。还有一个就是URI(统一资源标识符),其中的I是指identifier。URL是基于URI的。在互联网中,任何一个资源(html、css、js、img、动画、视频、音频、word)需要保证它的唯一性。可以给每一个资源指定一个唯一的URL。简单来说,URL就是我们常说的网址。

在web当中,用户输入不同的URL,服务器就接收到这个信息,需要处理这个信息,根据不同的请求,返回相应的内容。这个过程就是URL路由。我们可以通过req对象(IncomingMessage对象)中的url属性,来获取相关的信息,并进行处理。

Node.js提供了一个url模块,可以解析url,得到更为详细的信息。url.parse(url)得到一个对象,里边包含整个url中的各种信息。

代码片段:

浏览器输入 localhost:3000/user?username=admin&pwd=123路由处理 let realUrl = "http://"+ req.headers.host + req.url;        let urlObj = url.parse(realUrl);//得到查询字符串query : 'username=admin&pwd=123'

解析查询字符串

打开一个网页,很多情况下路径名不发生变化,改变查询字符串,就会显示不同内容;附加的一些信息,需要根据这些信息,显示不同的内容给用户。

格式:?键1=值1&键2=值2&键n=值n

使用url.parse方法解析url之后,可以拿到查询字符串

Node.js提供了querystring 模块,其中有 parse 方法,可以将字符串解析成对象;这样就可以直接通过属性来获取相对应的值。

代码片段:

浏览器输入 localhost:3000/user?username=admin&pwd=123const querystring = require('querystring');let realUrl = "http://"+ req.headers.host + req.url;let urlObj = url.parse(realUrl);//得到查询字符串query : 'username=admin&pwd=123'let qObj = querystring.parse(urlObj.query);//得到{username: 'admin', pwd: '123'}        

如何载入静态页面

在Node.js中,提供一个 web 服务器,有三种方式:

  • 手动实现:结合 fs 文件操作;
  • 第三方库实现:http-server
  • 框架实现:express

看一下手动实现:

第一步:准备一个静态的HTML页面index.html;

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Document</title>    <link rel="stylesheet" href="index.css"></head><body>    <h1>载入静态页面</h1>    <p>可以结合fs操作,实现载入静态页面</p>    <img src="xx.jpg" alt=""></body></html>

第二步:编码,创建一个 web 服务器,当用户输入url,需要显示静态页面;在这个过程中,由于图片和CSS都是独立的文件,对于它们,其实浏览器分别发送了单独的请求,而不是像之前那样直接去读取。所以,我们要在服务器端对这些静态文件进行处理,并通过res返回给浏览器端。

const http = require('http');const fs = require('fs');const url = require('url');http.createServer( (req,res) => {    let realUrl = "http://" + req.headers.host + req.url;    let urlObj = url.parse(realUrl);switch(urlObj.pathname) {    case "/" :  //首页    case "/index":        //使用fs读取html文件,然后输出        fs.readFile('index.html','utf8',(err,data) => {            if (err) throw err;            //读取成功,写入响应中            res.writeHead(200,{"content-type":"text/html;charset=utf-8"});            res.end(data);        });        break;    default:        //res.writeHead(200,{"content-type":"text/html;charset=utf-8"});        //res.end('页面走丢了');        //将所有其他的资源,在这里做处理        if (fs.existsSync(__dirname + urlObj.pathname)) {            fs.readFile(__dirname + urlObj.pathname,(err,data) => {                if (err) throw err;                    res.statusCode = 200;                    res.end(data);            });        }    }}).listen(3000,() => {    console.log('listening in port 3000...');});

这里使用了一个魔术常量 __dirname ,表示当前代码执行的路径。

POST请求及响应

典型的动态交互,表单处理。通常就是post请求。看一下Node.js是如何实现post请求和回应的。

第一步,准备一个表单,注意:要写获取到表单数据,必须要为input添加name属性,否则提交到服务器中的数据无法确定哪个是哪个,如下:

<form action="/signin" method="post" >    <ul>        <li>            <label for="">用户名:</label>            <input type="text" name="username">        </li>        <li>            <label for="">密码:</label>            <input type="password" name="password">        </li>        <li>            <label for=""></label>            <input type="submit" value="登录">        </li>    </ul></form>

第二步,创建一个web server,载入这个表单;此时,会需要使用 IncommingMessage 对象的两个事件 data 和 end,

switch (urlObj.pathname) {    case  '/login' : //登录页面        fs.readFile('login.html','utf8',(err,data)=>{            if (err) throw err;            res.writeHead(200, {"content-type" : "text/html"});            res.end(data);        });        break;    case '/signin':        //console.log('提交到这儿了');        //需要接受用户填写的信息,利用IncomingMesaage对象的data和end事件        let str = "";        req.on('data', (chunk)=> {            str += chunk;        } );        req.on('end',()=>{            //表示提交完毕,str就是提交的数据了            console.log(str); //username=admin&password=1234            //利用querystring对象的parse方法进行解析            let userObj = querystring.parse(str);            res.writeHead(200,{"content-type":"text/html;charset=utf-8"});            res.write(`<p>你输入的用户名是 ${userObj.username}</p>`);            res.write(`<p>你输入的密码是 ${userObj.password}</p>`);            res.end();        });        break;    default:        break;    }}).listen(3005, () => {    console.log('listening in port 3005');});

需要注册两个事件,data和end。数据提交的时候,并不是一次提交完毕,而是一块一块的提交。每提交一块,就触发一次,需要不停的累加。直到end触发了,说明提交完成了。

小结

以上是Node.js的基础介绍,接下来的学习更多的是不同模块的应用,和框架的学习了(express)。


0 0
原创粉丝点击