一、 stream
流
1. Node.js Stream中的四种流类型
Readable
:可读操作Writable
:可写操作Duplex
:可读可写操作Transform
:操作被写入数据,然后读出结果
2. Stream
对象的常用事件
注意: 所有
Stream
对象都是EventEmitter
的实例。
data
:当有数据可读时触发end
:没有更多的数据可读时触发error
:在接收和写入过程中发生错误时触发finish
:所有数据已被写入到底层系统时触发
3. 常见流操作
注意:若想将读出的所有内容写入文件中,由于事件操作具有异步性,故必须在读完后进行写入,故应在可读流的
end
事件触发后向文件写入
① 从流中读取数据
- 非一次性读取完文件中的所有数据
- 监听流的事件都是异步操作
步骤:
- 引入
fs
模块:const fs = require("fs")
- 创建可读流:
let streamRead= fs.createReadStream(读取文件路径)
- 设置流的编码:
streamRead.setEncoding("UTF-8")
- 处理流文件的事件:
on
①streamRead.on("data",(res)=>{})
:当有数据可读时触发。只要有数据可读就会触发,直到数据读取完成(当文件数据足够多时,会触发多次。res为本次读取的数据)
②streamRead.on("end",()=>{})
:读取数据完成时触发
② 向文件中写入数据
一次性将数据写入文件
监听流的事件都是异步操作
步骤
- 引入fs模块:
let fs = require("fs")
- 创建写入流:
let streamWrite=fs.createWriteStream(写入文件的路径)
- 使用
utf-8
编码写入数据:streamWrite.write(写入的数据,'UTF-8')
- 写入结束关闭流:
streamWrite.end()
- 处理流文件的事件:
on
①streamWrite.on("finish"()=>{})
:写入完成时触发
//从一个文件读出数据,再写到另一个文件
let streamData = fs.createReadStream("public/stu.txt");
let resData = "";
streamData.setEncoding("UTF-8");
streamData.on("data", (res) => {
//会一直执行,直到数据读取完成
console.log("有数据可读");
resData += res;
})
streamData.on("end", () => {
console.log("数据读取完成");
//创建写入流 必须在读完时才创建写入流,否则异步操作,resData为空
let writeStream = fs.createWriteStream("public/demo.txt");
//设置写入数据以及编码
writeStream.write(resData, "UTF-8")
//写入完成,结束流
writeStream.end();
writeStream.on("finish", () => {
//写入流完成时触发
console.log("数据写入完成")
})
})
streamData.on("error", () => {
console.log("数据读取有异常")
})
管道流
pipe
方法的底层实现了边读边写- 非一次性全部读取
操作步骤
- 引入
fs
模块:let fs = require("fs")
- 创建可读流:
let pipStreamR= fs.createReadStream(读取文件路径)
- 创建可写流:
let pipStreamW = fs.createWriteStream(写入文件路径)
- 实现将一个文件的所有数据写入另一个文件中:
pipStreamR.pipe(pipStreamW )
- 处理流的事件:
①pipStreamR.on("data",(res)=>{})
:有数据可读时触发
②pipStreamR.on("end",()=>{})
:读完成时触发
③pipStreamW.on("finish",()=>{})
:写完成时触发
二、EventEmitter
events
模块只提供一个对象:events.EventEmitter
(调用events.EventEmitter()
方法可以获取EventEmitter类)。EventEmitter
的核心就是事件触发与事件监听器功能的封装EventEmitter
对象如果在实例化时发生错误,会触发error
事件- 要先注册后发送(即on方法在emit之前执行)
1. 使用步骤:
- 引入
events
模块:let events = require('events')
- 创建
EventEmitter
对象:let eventEmitter = new events.EventEmitter()
- 使用(注册事件、给事件绑定监听器、移除事件的监听器…)
2. EventEmitter
对象方法
① eventEmitter.on(事件名称,监听器(函数))
- 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
- 当向eventEmitter对象发送事件时(调用
emit
方法绑定事件名称名称),会调用该事件的监听器(函数)
②eventEmitter.emit(事件名称, [监听器1], [监听器2], [...])
- 按监听器的顺序执行执行每个监听器(按照实参顺序)
- 如果事件有注册监听返回 true,否则返回 false。
- 如果只有一个实参,则按照注册顺序执行监听器
③ eventEmitter.addListener(事件名称,监听器(函数))
为指定事件添加一个监听器到监听器数组的尾部(即最后执行)
④ eventEmitter.once(事件名称,监听器(函数))
为指定事件注册一个单次监听器(该监听器只触发一次,触发完后移除)
⑤ eventEmitter.removeListener(事件名称,监听器名称(函数名称))
移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器
⑥ eventEmitter.removeAllListeners([事件名称])
移除所有事件的所有监听器(即没有参数);如果指定事件(即给定参数),则移除指定事件的所有监听器
⑦ eventEmitter.listeners(事件名称)
返回指定事件的监听数组(返回的是函数集合,可取每一个元素进行调用执行)
3. EventEmitter
类方法
events.emitter.listenerCount(eventName)
获取指定事件的监听器个数
4. 事件
三、ejs
模板语法
什么是ejs
- EJS 是一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。EJS 只是普通的 JavaScript 代码而已。(可将js代码嵌入到html中且这些js代码可被解析成html)
- EJS是一个JavaScript模板库,用来从JSON数据中生成HTML字符串。
1. 使用步骤:
- 安装
ejs
:npm install ejs --save-dev
- 使用
ejs
:let ejs = require("ejs")
- 使用:在
js
中通过ejs.render()
/ejs.renderFile()
方法可得到ejs
模板的内容,并通过res.end()
将其渲染到页面上去。
2. delimiter
可通过
ejs.delimiter=""
将默认的%
替换成等号右边的字符。在使用时,需要将所有%
(当然不包括想要输出的%)替换成右边的字符(即右边字符和原本的%
作用相同,用来解析js(不能右边字符和%
混用,替换后只能用右边的字符))
3.include(path,data)
- 在一个
ejs
文件中引入另一个ejs
文件,data
为向目标ejs
传送的数据。path
为被引入的ejs
文件目录- 返回是一个字符串,需要用
<%-%>
/<%=%>
将返回的内容解析,如果不加,则在页面无法解析返回的数据,且只能打印处include("...")
4. ejs渲染函数
① render(template,data,options)
返回一个字符串,即将模板解析过的内容,可渲染到页面上。
参数详解
template
:模板字符串(需要用转义字符引起来)data
:给模板字符串传送的数据options
(一般不用):
ejs.delimiter = '~';//将默认的%设置为~ 在使用时,必须全部使用替换后的字符
var html = ejs.render(`
<~for(var i=0;i<num;i++){~>
<h2><~=user.name~></h2>
<h2><~=user.age~></h2>
<~}~>`,{
user:{
name:"张园园",
age:22
},num:2
})
res.end(html)//html是解析后的html,可渲染到页面上
② renderFile(path,data,options,callback)
- 没有返回值
- path直接写
父文件夹/xxx.ejs
即可。
参数详解
path
:模板字符串所在文件(后缀为.ejs
,如果在express
中配置,后缀也可为html
)data
:向模板字符串传递的数据callback
:回调函数接收两个参数,一个为err
,一个为str
。str
为解析后的html
,可直接渲染到页面上
ejs.renderFile("view/index.ejs", {
student: data
}, (err, str) => {
console.log(str)
res.end(str);//渲染到页面上
});
4. ejs
语法
语法 | 解释 |
---|---|
<% |
脚本 标签,用于流程控制,无输出 |
<%_ |
删除其前面的空格符 |
<%= |
输出数据到模板(输出的为转义HTML标签) |
<%- |
输出非转义的数据到模板(所有数据都不转义(如果数据中有html标签,则不会将html标签解析成字符串,则是解析成html)) |
<%# |
注释标签。不执行、不输出内容 |
<%% |
输出字符串<% ( eg:<%%22222%> 输出模板字符串<%22222%> ) |
%> |
一般结束标签(<%str%>、<%= str %>、<%-str%>、<%#str%>、<%%str%>、<%_str%>) |
-%> |
删除紧随其后的换行符 |
_%> |
将结束标签后面的空格符删除 |
//服务端回调函数 .js
ejs.renderFile("view/index.ejs", {
student: data//data为引入data.json外部模块的名称
}, (err, str) => {
res.end(str);//res为回调函数的res参数。将str渲染到页面上
});
//login.ejs
<ul>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
<li>2</li>
</ul>
//index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<%for(let i=0;i<student.length;i++){%>
<li><%=student[i].name%></li>
<%}%>
</ul>
<%
var str = "<div></div>"
%>
<%#123%>
<%%if(user.name)%>
<%- str %>
<%=str%>
<%-include("./login.ejs")%>
</body>
</html>
//data.json
[
{
"name": "张三",
"age": 18
},
{
"name": "张三四",
"age": 18
},
{
"name": "李四",
"age": 18
},
{
"name": "王麻子",
"age": 18
},
{
"name":"<span>hello</span>"
}
]
5. 在.ejs
文件的javascript
中获取从服务端传来的JSON数据
let jsonData = "<%-JSON.stringify(数据变量)%>"
JSON.parse(jsonData);//得到传来的json数据
注意:
- 只能用单引号
''
或者反编译符将解析成字符串的数据的模板标签引起来。若用双引号""
,会报错(Uncaught SyntaxError: Unexpected identifier)- 应该用
<%-%>
将解析成字符串的数据包裹起来。否则会将json
数据中的""
进行转义。