Skip to content

Latest commit

 

History

History
553 lines (391 loc) · 15 KB

2. node.js基础.md

File metadata and controls

553 lines (391 loc) · 15 KB

Node.js基础

Node.js模块系统

什么是 Node.js 模块系统?

  • 核心模块

Node.js 标准的API中提供的模块,如 fs、http、net 等,这些都是由 Node.js 官方提供的模块,编译成了二进制代码,可以直接通过require获取核心模块,例如:

var fs = require('fs')
  • 文件模块

存储为单独的文件(或文件夹)的模块,可能是JavaScript、JSON 或编译好的C/C++代码。

exports 和 require

Node.js 使用CommonJS规范,提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口。

1.创建个hello.js文件代码如下
exports.world = function() {
	console.log('Hello World')
}
2.创建文件test.js引入模块 hello.js
var hello = require('./hello')
hello.world()
3.执行test.js文件输出 ‘Hello World’

有时候只是想把一个对象封装到模块中第一步在hello.js封装对象

function Hello() {
    var name
    this.setName = function(thyName) {
        name = thyName
    }
    this.sayHello = function() {
        console.log('Hello ' + name)
    }
}
module.exports = Hello

这样就可以直接获得这个对象了

var Hello = require('./hello')
hello = new Hello()
hello.setName('w3cschool')
hello.sayHello()

在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports



querystring对象

  • querystring.parse()

    将 URL 查询字符串 str 解析为键值对的集合

    var querystring = require("querystring")
    querystring.parse('foo=bar&abc=xyz&abc=123')
    
    /*
    {
    	foo: 'bar',
    	abc: ['xyz', '123']
    }
    */
  • querystring.stringify()

    将一个对象序列化成一个字符串,与 querystring.parse() 相对

    var querystring = require("querystring")
    querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' })
    // 返回 'foo=bar&baz=qux&baz=quux&corge='
    
    querystring.stringify({ foo: 'bar', baz: 'qux' }, ';', ':')
    // 返回 'foo:bar;baz:qux'
  • querystring.escape()

    以针对URL查询字符串的特定要求进行了优化的方式对给定执行URL百分比编码

    var querystring = require("querystring")
    
    var str = "name=小明"
    var result = querystring.escape(str)
    console.log(result)
    // 结果:name%3D%E5%B0%8F%E6%98%8E
  • querystring.unescape()

    对给定的URL百分比编码字符进行解码

    var querystring = require("querystring")
    
    var str = "name%3D%E5%B0%8F%E6%98%8E"
    var res = querystring.unescape(str)
    console.log(res)
    // 结果:name=小明


events模块

事件驱动

event 模块是 Node.js 系统中一个十分重要的模块,我们可以使用该模块实现事件的绑定和触发,因为 Node.js 是单线程异步的。

JavaScript 是单线程语言,JavaScript 一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。

所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。

下图说明了浏览器的主要线程:

img

比如浏览器为网络请求这样的异步任务单独开了一个线程,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器

setTimeout(function (){
    console.log('aaa')
}, 100)
console.log('bbb')

执行这段代码的时候,浏览器异步执行计时操作,当100ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。

所以结果是先打印bbb,然后再打印aaa

events 主要方法

  • on():添加事件(事件队列尾部添加)
  • once():添加只能触发一次便失效的事件(事件队列尾部添加)
  • prependListener():添加事件(添加到事件队列头部)
  • prependOnceListener():添加只能触发一次便失效的事件(添加到事件队列头部)
  • emit():触发事件
  • removeListener():删除某个事件
语法
on(eventName, listener[, arg1][, arg2]...
// eventName:注册事件名字
// listener:事件处理函数
// arg1,arg2:往事件处理函数中传入的参数

同一事件可以绑定多次,触发时按照事件队列顺序执行

on 和 once 是往事件队列尾部添加,prependListener 和 prependOnceListener 是往事件队列头部添加,这便形成了同一事件的执行顺序。

const Event = require('events')

const event= new Event()

event.on('test', function () {
    console.log('I am coming01')
})

event.on('test', function () {
    console.log('I am coming02')
})

event.prependOnceListener('test', function () {
    //只执行一次,优先执行    
    console.log('I am coming03')
})

console.log('执行第一次')
event.emit('test')
console.log('执行第二次')
event.emit('test')

/*
执行第一次
I am coming03
I am coming01
I am coming02
执行第二次
I am coming01
I am coming02
*/


http模块

http服务器

传统的HTPP服务器会由Aphche、Nginx、IIS之类的软件来担任,但是 Node.js 并不需要,Node.js 提供了http模块,自身就可以用来构建服务器,而且http模块是由C++实现的,性能可靠。

//获取http模块
var http = require("http")
//获取http.Server对象
var server = new http.Server()
//创建服务器,并监听3000端口
server.on("request",function(req,res) {
    res.writeHead(200,{
        "content-type":"text/plain"
    })
    res.write("Hello w3cschool")
    res.end()
}).listen(3000)

打开浏览器,输入localhost:3000我们就可以看到屏幕上的Hello w3cschool了,这表明这个最简单的Node.js服务器已经搭建成功了。

http客户端

Node.js 中的http模块中封装了一个http服务器和一个简易的http客户端,http.Server是一个基于事件的http服务器,http.request 和http.get 则是一个http客户端工具,用于向http服务器发起请求。

http.request
http.request(options,callback)

options是一个类似关联数组的对象,表示请求的参数,callback作为回调函数,需要传递一个参数,为http.ClientResponse的实例,http.request返回一个http.ClientRequest的实例。

options常用的参数有host、port(默认为80)、method(默认为GET)、path(请求的相对于根的路径,默认是“/”,其中querystring应该包含在其中,例如/search?query=byvoid)、headers(请求头内容)

/**
 *c.js
 *HTTP客户端,发送HTTP请求
 *控制台输出返回的响应内容
 */
var http = require("http")

var options = {
    host: "localhost",
    port: 3000
}
var req = http.request(options, function(res) {
    res.on("data",function(chunk) {
        console.log(chunk.toString("utf-8"))
    })
    res.on("end",function() {
        console.log("----client request!----")
    })
})

req.on("error",function(err) {
    console.log(err.message)
})
req.end()

/*
Hello w3cschool
----client request!----
*/
http.get
http.get(options,callback)

这个方法是 http.request 方法的简化版,唯一的区别是http.get自动将请求方法设为了GET请求,同时不需要手动调用req.end(),但是需要记住的是,如果我们使用http.request方法时没有调用end方法,服务器将不会收到信息。因为http.get和http.request方法都是放回一个http.ClientRequest对象。



url模块(路由)

url模块parse

url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
  • urlString 要解析的 URL 字符串。
  • parseQueryString 如果为 true,则 query 属性总会通过 querystring 模块的 parse() 方法生成一个对象。 如果为 false,则返回的 URL 对象上的 query 属性会是一个未解析、未解码的字符串。 默认为 false。
  • slashesDenoteHost 如果为 true,则 // 之后至下一个 / 之前的字符串会被解析作为 host。 例如,//foo/bar 会被解析为 {host: 'foo', pathname: '/bar'} 而不是 {pathname: '//foo/bar'}。 默认为 false。 url.parse() 方法会解析一个 URL 字符串并返回一个 URL 对象。
var url = require("url")

var myurl = "https://www.w3cschool.cn/minicourse/play/orbls?cp=9936&gid=0"
parsedUrl = url.parse(myurl)
console.log(parsedUrl)

输出为 img

url模块format

url.format() 方法返回一个从 urlObject格式化后的 URL 字符串。
url.format(urlObject)
  • urlObject 是一个 URL 对象(就像 url.parse() 返回的对象)。 如果是一个字符串,则通过 url.parse() 转换为一个对象。

如果 urlObject 不是一个对象或字符串,则 url.format() 抛出 TypeError。

var url=require('url')
var obj1={ protocol: 'http:',      
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'www.w3cschool.cn',
  port: null,
  hostname: 'www.w3cschool.cn',
  hash: null,
  search: '?cp=9936&gid=0',
  query: 'cp=9936&gid=0',
  pathname: '/minicourse/play/orbls',
  path: '/minicourse/play/orbls?cp=9936&gid=0',
  href: 'https://www.w3cschool.cn/minicourse/play/orbls?cp=9936&gid=0'    
}
var url1=url.format(obj1)
console.log(url1)

输出:

img

实例:路由选择实现

处理不同的HTTP请求在我们的代码中是另外一个不同的部分,叫做“路由选择”。那么,我们接下来就创造一个叫做 路由 的模块吧。

新建路由文件router.js

 module.exports={
     login:function(req,res){
         res.write("我是login方法")
     },
     register:function(req,res){
         res.write("我是注册方法")
     }
 }

创建服务端server.js 并调用router.js

 var http = require('http')
 var url = require('url')
 var router = require('./router')
 
 http.createServer(function(request, response) {
     response.writeHead(200, {
         'Content-Type': 'text/html; charset=utf-8'
     })
     if(request.url!=="/favicon.ico"){
         var pathname = url.parse(request.url).pathname //得到请求的路径
         console.log(pathname)
         pathname = pathname.replace(/\//, '') //替换掉前面的/
         console.log(pathname)
         router[pathname](request,response)
         response.end('')
     }
 }).listen(8000)
 console.log('Server running at http://127.0.0.1:8000/')

运行结果:

img



GET / POST 请求

GET请求

由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数。

Node.js 中 url 模块中的 parse 函数提供了这个功能。

var http = require('http')
var url = require('url')
var util = require('util')

http.createServer(function(req, res){
	res.writeHead(200,{'Content-Type':'text/html'})
    res.write('<head><meta charset="utf-8"/></head>')
	res.write(req.url)
	res.write("<br/>")
    var params = url.parse(req.url, true).query
    res.write("name:" + params.name)
    res.write("<br/>")
    res.write("url:" + params.url)
    res.end()
}).listen(3000)
POST 请求

post请求,这些参数不是通过url传递的,是包含在request请求体中。请求体里的流需要以事件形式进行接收.

例如:

var http = require('http')
var querystring = require('querystring')
 
var postHTML = 
  '<html><head><meta charset="utf-8"><title> Node.js实例</title></head>' +
  '<body>' +
  '<form method="post">' +
  '姓名:<input name="name"><br>' +
  '住址: <input name="address"><br>' +
  '<input type="submit">' +
  '</form>' +
  '</body></html>'
 
http.createServer(function (req, res) {
  var  post_1 = ""
  req.on('data', function (chunk) {
    post_1 += chunk  
    //通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中   
  })
  req.on('end', function () {
    result = querystring.parse(post_1)
    res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'})
    if(result.name && result.address) { 
        res.write("名字:" + result.name)
        res.write("<br>")
        res.write("住址:" + result.address)
    } else {  
        res.write(postHTML)
    }
    res.end()
  })
}).listen(3000)


函数

Node.js 函数

在JavaScript中,一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。

function say(word) {
  console.log(word)
}

function execute(someFunction, value) {
  someFunction(value)
}

execute(say, "Hello")

以上代码中,我们把 say 函数作为execute函数的第一个变量进行了传递。这里传递的不是 say 的返回值,而是 say 本身!

这样一来, say 就变成了execute 中的本地变量 someFunction ,execute可以通过调用 someFunction() (带括号的形式)来使用 say 函数。

匿名函数

可以直接在另一个函数的括号中定义和传递这个函数:

function execute(someFunction, value) {
  someFunction(value)
}

execute(function(word){
    console.log(word)
}, "Hello")

回调函数

Node.js 异步编程的直接体现就是回调。

异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。

回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。

例如:我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。

创建一个文件 input.txt

Hello, welcome to w3cschool

创建 test.js 文件

var fs = require("fs")

fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err)
    console.log(data.toString())
})

console.log("程序执行结束!")

/*
Hello, welcome to w3cschool
程序执行结束!
*/