一、HelloWorld
1.1 安装nodemon工具
在编写调试Node.js项目,修改代码后,需要频繁的手动close掉,然后再重新启动,非常繁琐。现在,我们可以使用nodemon这个工具,它的作用是监听代码文件的变动,当代码改变之后,自动重启。
安装命令:
npm i nodemon -g
安装完成后,在任意目录下新建js文件,文件内容如下:
console.log('hello node.js');
console.log('hello java');
console.log('hello python');
执行命令:nodemon 文件路径,执行结果如下所示:
以后修改了js文件后,不需要每次重新运行node命令,提高开发效率。
二、模块使用
Nodejs内置一些模块,这些模块是nodejs本身提供,我们只需要在js文件中导入它们即可使用。下面简单介绍几个比较常用的模块:File System、OS、HTTP模块等。
资料地址:http://nodejs.cn/api/modules.html
2.1 文件模块
在 Node.js 模块系统中,每个文件都被视为一个独立的模块。如果要使用模块中导出的函数,那么就需要先导入模块。
下面使用fs模块读取文件的内容:
第一步:导入fs模块;
const fs = require('fs')
第二步:调用模块提供的函数;
// 以同步方式读文件
const data = fs.readFileSync('./config.js');
console.log('文件内容:', data);
// 以异步方式读文件
fs.readFile('./config.js', (data) => {
console.log('文件内容:', data);
});
nodejs几乎所有的api都提供了同步和异步的版本。在实际开发中建议使用异步方式。但是有时我们需要对多个异步操作的执行顺序进行控制,这时我们可以把异步代码同步化。
让异步代码同步化的几种方式:
方式一:使用Promise;
const {promisify} = require('util')
// 将fs.readFile函数转换成Promise
const readFile = promisify(fs.readFile)
readFile('./config.js').then(data => console.log(data))
在nodejs的10.0以后版本,可以使用fs模块提供了promises对象,该对象也提供了readFile方法实现同步的读文件操作。
const {promises} = require('fs')
promises.readFile('./config.js').then(data => console.log(data.toString()))
方式二:使用generator函数;
function readFile(path) {
return new Promise((resolve, reject) => {
const data = fs.readFile(path, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data);
}
});
})
}
function* test() {
yield readFile('./config.js');
yield readFile('./config2.js');
}
const tt = test();
tt.next().value.then(res => {
console.log('config.js文件内容:', res.toString());
})
tt.next().value.then(res => {
console.log('config2.js文件内容:', res.toString());
})
方式三:使用async和await;
function readFile(path) {
return new Promise((resolve, reject) => {
const data = fs.readFile(path, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data);
}
});
})
}
async function test() {
const p1 = await readFile('./config.js');
console.log('config.js文件内容:', p1.toString())
const p2 = await readFile('./config2.js');
console.log('config2.js文件内容:', p1.toString())
}
test();
上面程序会先读去config.js文件内容,读完后再读取config2.js文件内容。
2.2 Buffer模块
Buffer代表缓冲器,用于在 TCP 流、文件系统操作、以及其他上下文中与八位字节流进行交互。
Buffer实例类似于从 0 到 255 之间的整数数组,它是一个8字节大小的数组,可存储二进制数据,但是以十六进制格式展示出来。Buffer 的大小在创建时确定,且无法更改。
Buffer 类在全局作用域中,因此无需使用 require(‘buffer’)。
创建Buffer的不同方式:
方式一:使用Buffer.alloc方法创建一个固定长度的Buffer;
const buf = Buffer.alloc(10)
方式二:把数组转换成Buffer;
const buf = Buffer.from([11, 22, 33])
方式三:把字符串转换成Buffer;
const buf = Buffer.from('hello world', 'utf-8')
Buffer提供了toString方法,用于读取Buffer中的数据。
buf.toString('utf-8')
合并多个Buffer:
const buf1 = Buffer.from('hello ')
const buf2 = Buffer.from('world ')
const buf3 = Buffer.concat([buf1, buf2])
console.log(buf3.toString());
2.3 http模块
Node.js 中的 HTTP 接口旨在支持传统上难以使用的协议的许多特性。 特别是,大块的、可能块编码的消息。 接口永远不会缓冲整个请求或响应,用户能够流式传输数据。
http头消息:
{
'content-length': '123',
'content-type': 'text/plain',
'connection': 'keep-alive',
'host': 'mysite.com',
'accept': '*/*'
}
为了支持所有可能的 HTTP 应用程序,Node.js 的 HTTP API 都非常底层。 它仅进行流处理和消息解析。 它将消息解析为消息头和消息主体,但不会解析具体的消息头或消息主体。
要使用 HTTP 服务器和客户端,必须 require(‘http’)。
下面我们使用http模块开发一个简单版的web服务器。
第一步:创建一个js文件,导入http模块以及其他相关模块;
const http = require('http')
const fs = require('fs')
const path = require('path')
第二步:调用http接口的createServer方法,该方法用于创建一个Http服务端;
const server = http.createServer((req, res) => {
console.log('url: ', req.url); // 输出 /
console.log('method: ', req.method); // 输出 get
// 向客户端发送响应消息
res.write('hello ');
res.write('world ');
res.write('nodejs ');
res.end('java');
})
第三步:调用listen方法监听某个端口;
server.listen(3000)
res.end()方法用于结束请求,否则客户端会一直处于等待状态。
2.4 流
流(stream)是 Node.js 中处理流式数据的抽象接口。 stream 模块用于构建实现了流接口的对象。
Nodejs提供四种不同类型的流:
- writable - 可写入数据的流(例如 fs.createWriteStream())。
- Readable - 可读取数据的流(例如 fs.createReadStream())。
- Duplex - 可读又可写的流(例如 net.Socket)。
- Transform - 在读写过程中可以修改或转换数据的 Duplex 流(例如 zlib.createDeflate())
下面使用stream来完成文件的读写操作:
const fs = require('fs')
const inStream = fs.createReadStream('./config.js')
const outStream = fs.createWriteStream('./config2.js')
inStream.pipe(outStream)
在服务端向客户端输出图片:
if (req.headers.accept.indexOf('image/*') !== -1 && method === 'GET') {
fs.createReadStream(path.resolve('.' + url)).pipe(res)
}
上面程序代码中的响应对象res其实也是一个输出流。如果可以在pipe方法中直接把res作为参数传入。
三、express使用
Express 是一个保持最小规模的灵活的 Node.js Web 应用程序开发框架,为 Web 和移动应用程序提供一组强大的功能。
express主要解决nodejs开发中的两个主要问题:路由和请求流程控制。
3.1 如何使用express
第一步:安装express;
npm i express
第二步:新建一个js文件,导入express模块;
const express = require('express')
第三步:调用express函数,该函数返回一个对象;
const app = express()
第四步:调用对象方法处理请求;
第五步:调用对象的listen方法,指定要监听的端口;
下面演示express工具的简单用法:
const express = require('express')
const fs = require('fs')
const path = require('path')
// 创建express对象
const app = express()
app.get('/', (req, res) => {
fs.readFile(path.resolve('./index.html'), (err, data) => {
if (err) {
res.statusCode = 500;
res.end('server err!')
} else {
res.statusCode = 200 // 请求成功
res.setHeader('content-type', 'text/html;charset=utf-8')
res.end(data)
}
})
})
app.listen(3000)
3.2 实现简单版的express
第一步:新建一个js文件,导入需要用到的模块;
const http = require('http')
const url = require('url')
第二步:定义一个数组变量,用于存储所有路由;
let routers = [] // 路由列表
第三步:创建一个主类,定义get方法;
get(path, handler) {
// 保存路由
routers.push({path, method: 'get', handler})
}
第四步:定义listen方法;
listen(port) {
http.createServer((req, res) => {
// url.parse方法可以把req.url的各个部分分解出来
let {pathname} = url.parse(req.url, true)
// 判断该pathname在路由列表中是否存在
// 如果存在,在执行对应的handler函数
routers.forEach(v => {
if (v.path === pathname) {
v.handler(req, res)
return
}
})
}).listen(port)
}
第五步:定义导出;
module.exports = function() {
return new Application()
}