Node.js 标准的API中提供的模块,如 fs、http、net 等,这些都是由 Node.js 官方提供的模块,编译成了二进制代码,可以直接通过require获取核心模块,例如:
var fs = require('fs')
存储为单独的文件(或文件夹)的模块,可能是JavaScript、JSON 或编译好的C/C++代码。
Node.js 使用CommonJS规范,提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口。
exports.world = function() {
console.log('Hello World')
}
var hello = require('./hello')
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
-
将 URL 查询字符串 str 解析为键值对的集合
var querystring = require("querystring") querystring.parse('foo=bar&abc=xyz&abc=123') /* { foo: 'bar', abc: ['xyz', '123'] } */
-
将一个对象序列化成一个字符串,与 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'
-
以针对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
-
对给定的URL百分比编码字符进行解码
var querystring = require("querystring") var str = "name%3D%E5%B0%8F%E6%98%8E" var res = querystring.unescape(str) console.log(res) // 结果:name=小明
event 模块是 Node.js 系统中一个十分重要的模块,我们可以使用该模块实现事件的绑定和触发,因为 Node.js 是单线程异步的。
JavaScript 是单线程语言,JavaScript 一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。
所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。
下图说明了浏览器的主要线程:
比如浏览器为网络请求这样的异步任务单独开了一个线程,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器
setTimeout(function (){
console.log('aaa')
}, 100)
console.log('bbb')
执行这段代码的时候,浏览器异步执行计时操作,当100ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。
所以结果是先打印bbb,然后再打印aaa
- 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
*/
传统的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服务器已经搭建成功了。
Node.js 中的http模块中封装了一个http服务器和一个简易的http客户端,http.Server是一个基于事件的http服务器,http.request 和http.get 则是一个http客户端工具,用于向http服务器发起请求。
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(options,callback)
这个方法是 http.request 方法的简化版,唯一的区别是http.get自动将请求方法设为了GET请求,同时不需要手动调用req.end(),但是需要记住的是,如果我们使用http.request方法时没有调用end方法,服务器将不会收到信息。因为http.get和http.request方法都是放回一个http.ClientRequest对象。
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)
- 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)
输出:
处理不同的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/')
运行结果:
由于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请求,这些参数不是通过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)
在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
程序执行结束!
*/