1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > node.js Stream(流) 和 EJS 模板引擎——0822

node.js Stream(流) 和 EJS 模板引擎——0822

时间:2021-11-26 10:38:26

相关推荐

node.js Stream(流) 和 EJS 模板引擎——0822

一、node.js 中的 Stream(流)

1、什么是 Stream ?

Stream是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http服务器发起请求的request对象就是一个 Stream,还有stdout(标准输出)。

Node.js,Stream 有四种流类型

Readable- 可读操作。

Writable- 可写操作。

Duplex- 可读可写操作.

Transform- 操作被写入数据,然后读出结果。

所有的Stream对象都是EventEmitter的实例。常用的事件有:

data- 当有数据可读时触发。

end- 没有更多的数据可读时触发。

error- 在接收和写入过程中发生错误时触发。

finish- 所有数据已被写入到底层系统时触发。

2、从流中读取数据

步骤:

创建读取流——设置读取流的编码——处理流事件(data,end,error)。

流的事件是Event.Emit驱动的。

const fs = require('fs');let Data = "";//创建读取流let streamRead = fs.createReadStream('./stu.txt');//设置流的编码streamRead.setEncoding('utf-8');//处理流事件 --> data end errorstreamRead.on('data', (chunk) => {Data += chunk;});streamRead.on('end', () => {console.log(Data);});streamRead.on('error', (err) => {console.log(err);})

3、写入流

写入流 可以写在读取流end()事件里边。

步骤:

创建一个可以写入的流——使用指定的编码写入数据——标记文件末尾(结束写入流)——处理流的事件(finisherror)。

const fs = require('fs');//创建一个可以写入的流 写入到指定的文件中let writeStream = fs.createWriteStream('./data.txt');//使用指定的编码格式写入流writeStream.write("写入流的数据", "utf-8", (err) => {if (err)console.log("写入失败!");});//标记文件末尾 --结束流writeStream.end();//处理流事件writeStream.on('finish', () => {console.log("写入完成!");})

查看 data.txt 文件的内容:

cat data.txt

4、管道流

什么是管道流?

管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。(会覆盖)

步骤:

创建一个可读流——创建一个可写流——管道读写操作

let readStm = fs.createReadStream('./stu.txt');let writeStm = fs.createWriteStream('./data.txt');//管道读写操作//读取stu.txt文件的内容 并将内容写到data.txt文件中readStm.pipe(writeStm);

5、链式流

什么是链式流?

链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。

比如,用管道和链式来压缩和解压文件。

let readStream = fs.createReadStream('./stu.txt');//压缩 ./stu.txt 文件为 ./stu.txt.zip 文件readStream.pipe(zip.createGzip()).pipe(fs.createWriteStream('./stu.txt.zip'));

二、web服务器静态文件托管

使用http模块、url模块、fs模块、path模块、自定义的extName模块创建一个静态的web服务器:

静态web服务器的目录结构:

代码如下:

server.js 代码:

const http = require('http');const fs = require('fs');const url = require('url');//path内置模块 可以取出路径名的后缀名称const path = require('path');//引入自定义模块 解析后缀名称为对应的文件格式let extname = require('extName');let port = 8000;let hostname = 'localhost';const server = http.createServer((req, res) => {let pathname = url.parse(req.url, true).pathname;if (url != '/favicon.ico') {if (pathname == "/") {pathname = "/index.html";}//这里要解析后缀名 根据后缀名取对应的文件格式 比如html对应"text/html" 用来设置请求头//先取出路径名的后缀名称let exname = path.extname(pathname);//使用后缀命名解析模块 解析后缀名//使用promise传递// extname(exname).then((lastname) => {//res.setHeader("Content-Type", `${lastname};charset=utf8`);//fs.readFile("./static" + pathname, { flag: "r+" }, (err, str) => {// if (err)// throw err;// res.end(str)//})// })//使用callback传递extname(exname, (lastname) => {res.setHeader("Content-Type", `${lastname};charset=utf8`);fs.readFile("./static" + pathname, {flag: "r+" }, (err, str) => {if (err)throw err;res.end(str)})})}});server.listen(port, hostname, () => {console.log(`服务器运行在:http://${hostname}:${port}/`);})

extName.js 代码如下:

let fs = require('fs');//使用promise传递// module.exports = (name) => {//let promise = new Promise((resolve, reject) => {// fs.readFile(__dirname + "/mime.json", (err, str) => {// if (err)// throw err;// let namelist = JSON.parse(str.toString());// resolve(namelist[name]);// });//});//return promise;// }//使用callback 传递module.exports = (name,callback) => {fs.readFile(__dirname + "/mime.json", (err, str) => {if (err)throw err;let namelist = JSON.parse(str.toString());callback(namelist[name]);});}

关于 extName 自定义模块:

(1)mime.json 是后缀名对应文件格式的json

(2)在extName.js 文件里,暴露出一个方法(解析后缀名),该方法返回一个promise对象,通过读取 mime.json 文件之后,根据后缀名取到对应的文件格式,resolve出去;

(3)传递namelist[name]可以使用promise,也可以使用callback,或者其他异步方式。

三、事件驱动 EventEmitter

Node.js的三大特点:单线程、非阻塞式IO(异步)、事件驱动.

处理异步请求,外边获取请求到的数据的方式:

回调函数、使用node.js内置的events模块进行数据的广播和监听(事件驱动EventEmitter)。

(1)回调函数获取异步请求到的数据:

//1.使用回调函数来获取数据function getMime(callback){fs.readFile("mime.json",function (err,res){if(err) throw err;callback(res.toString());});}console.log(1);//这样就可以在外部获取异步的数据 也可以暴露出去别的文件使用module.exports=getMime(function (data){console.log(data);});

(2)EventEmitter

什么是Node.js EventEmitter?

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。

Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。

EventEmitter 类:

events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发事件监听器功能的封装。

使用 EventEmitter:

// 引入 events 模块var events = require('events');// 创建 eventEmitter 对象var eventEmitter = new events.EventEmitter();

对于每个事件,EventEmitter支持 若干个事件监听器。

当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。

下边是一个通过EventEmitter 进行数据的广播和监听的例子:

var fs=require("fs");var event=require("events");//实例化事件的对象var EventEmitter=new event.EventEmitter();//使用events模块 获取异步的数据//在请求完数据之后进行广播fs.readFile("mime.json",function (err,res){if(err) throw err;EventEmitter.emit("mime",res.toString());});//在外部进行监听广播EventEmitter.on("mime",function (data){console.log(data);});

注意:

先监听(on) ,后分发(emit)(分发到事件队列里边)。

上边的on是先与emit执行的。

在不同的文件之间广播和监听使用都可以获取数据。

(3)EventEmitter 类的方法和属性

EventEmitter提供了多个属性,如onemiton函数用于绑定事件函数,emit属性用于触发一个事件。

下边是一些其它的属性:

方法:

addListener(event, listener)为指定事件添加一个监听器到监听器数组的尾部。

let fun1=()=>{console.log("1");}eventemitter.addListener("collection",fun1);eventemitter.emit("collection");

once(event, listener)为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。

removeListener(event, listener)移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。

removeAllListeners([event])移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。

setMaxListeners(n)默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。

listeners(event)返回指定事件的监听器数组。

emit(event, [arg1], [arg2], [...])按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

类方法:

listenerCount(emitter, event)返回指定事件的监听器数量。

eventemitter.listenerCount(eventName)

error 事件:

EventEmitter 定义了一个特殊的事件error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。

当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。

我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:

var events = require('events'); var emitter = new events.EventEmitter(); emitter.emit('error');

上边的代码 没有设置监听器,会报错。

四、web服务器静态文件托管+get、post路由传值

静态web服务器结构:

index.html 代码如下:

<form action="/login" method="POST">账号:<input type="text" name="id">密码:<input type="password" name="pwd"><button>登录</button></form>

server.js 代码如下:

const http = require('http');const url = require('url');let fs = require('fs');let port = 8000;let hostname = 'localhost';let server = http.createServer((req, res) => {let parse = url.parse(req.url, true);let path = parse.pathname;if (req.url != '/favicon.ico') {res.setHeader('Content-Type', 'text/html;charset=utf8');if (path == '/') {fs.readFile('./static/index.html', (err, str) => {if (err)throw err;else {res.end(str);}})}else if (path == '/login') {let info = "";if (req.method == 'GET') {info = parse.query.id + "---" + parse.query.pwd;res.end("结果:" + info);}else {req.on('data', (s) => {info += s;});req.on('end', () => {res.end("结果:" + info);})}}}});server.listen(port, hostname, () => {console.log(`服务器运行在http://${hostname}:${port}/`);})

如果将以上代码的路由部分封装起来:

目录结构:

router.js 代码如下:

暴露出一个对象,键名为路径

let fs = require('fs');module.exports = {'/': (req, res) => {fs.readFile('./static/index.html', (err, str) => {if (err)throw err;else {res.end(str);}})},'/login': (req, res) => {let info = "";if (req.method == 'GET') {info = parse.query.id + "---" + parse.query.pwd;res.end("结果:" + info);}else {req.on('data', (s) => {info += s;});req.on('end', () => {res.end("结果:" + info);})}}}

server.js 代码如下:

执行路径对应的函数,传参

const http = require('http');const url = require('url');let router = require('./router');let port = 8000;let hostname = 'localhost';let server = http.createServer((req, res) => {let parse = url.parse(req.url, true);let path = parse.pathname;if (req.url != '/favicon.ico') {res.setHeader('Content-Type', 'text/html;charset=utf8');router[path](req, res);}});server.listen(port, hostname, () => {console.log(`服务器运行在http://${hostname}:${port}/`);})

五、ejs 模板引擎

——从后台渲染数据到页面

ejs官网:/#install

需要掌握标签含义、用法、include、缓存(lru-cache);了解options参数的使用。

安装:

cnpm install --save-dev ejs

引入和使用:

let ejs = require('ejs'),people = ['geddy', 'neil', 'alex'],html = ejs.render('<%= people.join(", "); %>', {people: people});

下边是需要掌握的 ejs 基本用法:

1、标签含义

<%‘脚本’ 标签,用于流程控制,无输出。

<%_删除其前面的空格符

<%=输出数据到模板(输出是转义 HTML 标签)

<%-输出非转义的数据到模板

<%#注释标签,不执行、不输出内容

<%%输出字符串'<%'

%>一般结束标签

-%>删除紧随其后的换行符

_%>将结束标签后面的空格符删除

2、用法

let template = pile(str, options);template(data);// => 输出渲染后的 HTML 字符串

ejs.render(str, data, options);// => 输出渲染后的 HTML 字符串

ejs.renderFile(filename, data, options, function(err, str){// str => 输出渲染后的 HTML 字符串});

完整的用法示例:

server.js 代码:

const http = require('http');const ejs = require('ejs');let url = require('url');let port = 8000;let hostname = 'localhost';const server = http.createServer((req, res) => {//路径解析let parse = url.parse(req.url, true);let pathname = parse.pathname;if (req.url != '/favicon.ico') {res.setHeader('Content-Type', 'text/html;charset=utf8');if (pathname == '/') {let menu = ["首页", "导航"];//用法一// let html = ejs.render("<div><%=people%></div>", { people: "张翠花"});// res.end(html);//用法二ejs.renderFile(__dirname + '/view/index.ejs', {menu: menu }, (err, str) => {if (err)throw err;res.end(str);});//用法三// let template = pile("<div><%=people%></div>");// let html = template({ people: "张翠花" });// res.end(html);}}});server.listen(port, hostname, () => {console.log(`服务器运行在:http://${hostname}:${port}/`);})

index.html 代码:

<%for(var i=0;i<menu.length;i++){%><div><%=menu[i]%></div><%}%>

3、include

通过include指令将相对于模板路径中的模板片段包含进来。

可以传值,服务端传给页面,页面再传给该模板片段。

如,在页面中插入模板片段 “title.ejs”:

<%-include("./module/title.ejs",{menu:menu})%>

4、缓存(lru-cache)

EJS 附带了一个基本的进程内缓存,用于缓在渲染模板过程中所生成的临时 JavaScript 函数。

使用方式:

通过 Node 的lru-cache库加入 LRU 缓存:

先下载lru-cache,接下来:

let ejs = require('ejs'),LRU = require('lru-cache');ejs.cache = new LRU(100); // 具有 100 条内容限制的 LRU 缓存

如果要清除 EJS 缓存,调用ejs.clearCache即可。如果你正在使用的是 LRU 缓存并且需要设置不同的限额,则只需要将ejs.cache重置为 一个新的 LRU 实例即可。

六、模拟express 封装路由模块

server.js 文件—— 模拟 express 路由模块的使用:

let http = require('http');let app = require('./app');let ejs = require('ejs');let server = http.createServer(app);server.listen(8000, () => {console.log('http://localhost:8000');})//模拟express的路由app.get("/", (req, res) => {ejs.renderFile(__dirname+"/view/index.ejs", (err, str) => {res.send(str);});})app.get("/login", (req, res) => {console.log(req.query);res.send('登录');})app.post("/login",(req,res)=>{console.log(req.body);res.send("denglu")})

app.js 文件——模拟 express 封装路由模块:

let obj = this;obj._get = {};//存储get请求的回调函数obj._post = {};//存储post请求的回调函数let url = require("url")let app = (req, res) => {res.send = (str) => {res.writeHead(200, {"Content-Type": "text/html;charset=utf8" });res.end(str);}let urlparse = url.parse(req.url, true);let pathname = urlparse.pathname;let method = req.method.toLowerCase();if (pathname != "/favicon.ico") {//检测该路由存在if (obj["_" + method][pathname]) {//判断路由是get还是postif (method == "get") {req.query = urlparse.query;obj["_" + method][pathname](req, res);}else {let info = "/?";req.on("data", (s) => {info += s;})req.on("end", () => {req.body = url.parse(info, true).query;obj["_" + method][pathname](req, res);})}}//该路由不存在 直接endelse {res.send("无该路由")}}}app.get = (string, callback) => {obj._get[string] = callback;};app.post = (string, callback) => {obj._post[string] = callback;};module.exports = app;

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。