【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)

https://www.cnblogs.com/cnb-yuchen/p/18031964

出自【进步*于辰的博客

1、NPM

推荐一篇博文《NPM概述及使用简介》(转发)。

我暂未整理相关阐述,大家可查阅这篇文章。

2、Buffer

推荐一篇博文《02-Node.js—Buffer(缓冲器)》(转发)。

参考笔记三,P49.1。

Buffer是一种类似数组的对象,用于表示固定长度的字节序列,其本质是一段内存空间,且空间由c++申请,每个元素占一个字节。

创建:

  1. Buffer.alloc(size):创建长度为 size 的字节序列;
  2. buffer.allocUnsafe(size):同上,区别是在分配内存时不会清除旧数据(指曾使用过仍保留数据、但目前未使用的内存空间);
  3. Buffer.from(xx):xx 可以是数组、字符串或 Buffer。

说明:

1、由于每个元素占一个字节,故alloc(size)allocUnsafe(size)创建的字节序列共包含 size 个字节。

示例:

var buf = Buffer.alloc(10)
// 打印buf:<Buffer 00 00 00 00 00 00 00 00 00 00>

规定以16进制的格式进行显示,00(16进制)是0000 0000(二进制),共10个元素。

2from(xx)创建的字节序列所占字节数由 xx 决定。

示例1。(xx是数组)

var arr = [2, 0, 2, 3]
var buf = Buffer.from(arr)
// 打印buf:<Buffer 02 00 02 03>

数字占一个字节,故长度为4

2(数字,十进制)是02(16进制)。

示例2.。(xx是字符串)

var buf = Buffer.from('2023')
// 打印buf:<Buffer 32 30 32 33>

为何buf[0]32?因为此时的2不是数字,而是字符。

'2'的 ASCLL 码是50,转换成16进制就是32

示例3。(xx是字符串)

var buf = Buffer.from('汉字')
// 打印buf:<Buffer e6 b1 89 e5 ad 97>

是不是有点懵?因为Buffer采用utf-8编码,一个汉字占3个字节,故用三个元素表示一个汉字。

改一下。

var buf = Buffer.from('汉字')
buf[0] = 97 + 7// 'h'的ASCLL码
buf[1] = 97
buf[2] = 97 + 13
console.log(buf.toString())// 打印:han字

toString()会将每个元素都转换成对应的字符,这样是不是一目了然了。

再补充一点。

var buf = Buffer.from('汉字')
buf[0] = 97 + 7 + 256// ------------------A
buf[1] = 97
buf[2] = 97 + 13
console.log(buf.toString())// 打印:han字

97 + 7'h'的 ASCLL 码,再+ 256已经不是'h',为何最后还是'h'

因为Buffer规定,一个字符占一个字节。换言之,只会用一个字节来表示字符,如果字符对应的 ASCLL 码超出一个字节(8位)的表示范围(255),超出的部分会被丢弃。

256对应的二进制是1 00000000,即需要两个字节,则第一个字节舍去,剩下0000 0000,为0(十进制)。

示例4。(xx 是Buffer

var buf1 = Buffer.from([2, 0, 2, 3])
var buf2 = Buffer.from(buf1)
// 打印buf:<Buffer 02 00 02 03>

与示例1相同。

3、fs模块

推荐一篇博文《03-Node.js—fs模块》(转发)。

参考笔记三,P50、P51。

fs模块是Node.js的内置模块,负责与文件系统的交互。

3.1 读文件

函数:

  1. 异步读取:readFile(path[, options], (err, content) => {})
  2. 同步读取:var content = readFileSync(path[, options])
  3. 流式读取:(1)创建读取流:createReadStream(path[, options]);(2)通过'data''end'事件读取。

注:options是读取配置,如:'utf-8',否则读取结果为二进制序列。

3.2 写文件

函数:

  1. 异步写入:writeFile(path, data[, options], err => {})
  2. 同步写入:writeFileSync(path, data[, options]),返回undefinied
  3. 流式写入:(1)创建写入流:createWriteStream(path[, options]);(2)写入:write(data)
  4. 附加写入:appendFile() / appendFileSync(),参数同上。

注:options是写入配置,如:{flag: 'a'}表示附加写入(暂不理解)。

4、path模块

推荐一篇博文《04-Node.js—path模块》(转发)。

参考笔记三,P51。

函数:

  1. 解析路径:resolve(path),path 前常附加当前目录__dirname
  2. 返回文件后缀:extname(path)

5、express模块

推荐一篇博文《09-Node.js—express框架》(转发)。

参考笔记三,P46、P50、P51。

express模块是基于Node.js的web应用开发框架,主要用于搭建js服务器。

5.1 响应相关函数

  1. 重定向:res.redirect(url)
  2. 响应文件:res.download(path),path 可以是绝对 / 相对路径,此函数是基于fs.readFile()res.end()的封装;
  3. 以json字符串作为响应体:res.json({})
  4. 设置响应体:res.send(),此函数是基于res.end()(http模块)的封装,可响应任何类型,且只保留数据部分,如会将str最外层的''/""省略。

    注意:此函数是设置响应体,也是响应。虽是响应,但请求处理未结束,也由于是响应,故在其后不能再做响应配置,如:res.write()(http模块)、res.set()(见第7项);
  5. 响应文件:res.sendFile(path.resolve(__dirname + path))。path 必须是绝对路径,故拼接了__dirname

5.2 中间件

1、路由中间件。

“路由中间件”表现为具有三个参数(req, res, next)的函数,用于封装路由公共代码(匹配路由前的操作),故需要置于所有路由之前(即中间件之前的路由不会执行中间件,因为路由匹配至上而下),且必须调用next()才能执行路由(暂不知next是什么)。

2、静态资源中间件。

设置项目根目录为目录express.static(目录)

注意:中间件必须使用ser.use()引入。

5.3 Router

Router是一个完整的中间件和路由系统,可看作是一个小型的js服务器(当然并不是js服务器,故需要引入到js服务器中使用)。

使用步骤:

  1. 创建路由:var rou = express.Router()
  2. 配置路由(与js服务器相同);
  3. 开放接口:module.exports = rou
  4. 引入:ser.use(require(Router路径)),路径必须是相对路径,且必须采用./格式。

注意:

  1. 若使用 Router,必须在 Router 的路由的适当位置调用next()(先在路由回调函数上添加next参数)。因为 Router 是子服务,一般使用 Router 时,主服务中肯定也有路由,如果不调用next(),则不会检索主路由;
  2. PS:Router 没有跨域问题,原因未知。

5.4 解析请求体数据

debug 一下,就可以发现req对象具有三个属性,query(封装请求行数据)、params(封装动态请求数据,暂不清楚)、body(封装请求体数据)。

假设body = {id: 2023},则通过req.body.idreq.body['id']即可获取参数id的值,之所以能获取到,是因为body中数据的格式是js对象。换言之,若请求数据的格式不是js对象,就不一定能解析成功。

这时,可以使用body-parser模块进行辅助解析。

步骤:

  1. (1)若请求数据封装的方式是x-www-form-urlencoded,构造对象:var urlParser = bodyParser.urlencoded({extended: false});(2)若封装方式是json,构造对象:var jsonParser = bodyParser.json()
  2. 将路由的第2个参数设置为urlParser

PS:我尝试了很多方法进行模拟测试,ajax、postman、form等等,可不知为何,req对象中始终没有body属性(js服务端使用vscode开发),故根本无法测试,所以只能请大家自行测试领悟了。

5.5 综合示例

先言:代码稍微有点长,一是为了尽量多使用express模块的函数,做个示例;二是为了使功能稍微丰满一点。大家阅读的时候,直接跳过与业务相关的代码,留意以上四个知识点相关的部分就OK。

1、主服务代码。

const express = require('express')
const bodyParser = require('body-parser')
const path = require('path') const ser = express()// 构建服务
// 引入静态资源中间件,
ser.use(express.static('./'))// 设置项目根目录为当前目录 // 引入路由中间件
ser.use((req, res, next) => {
res.set('access-control-allow-origin', 'http://127.0.0.1:5501')// 跨域配置
next()
}) // 引入Router
ser.use(require('./r1'))// Router与当前文件同目录,文件名是 r1.js var users = [
{
id: 1,
name: '进步',
pass: '2023'
}, {
id: 2,
name: '于辰',
pass: '2021'
}
] ser.get('/g1', (req, res) => {
var id = req.query.userid
var result = users.find(item => {
if (item.id == id) {
res.json({
id: id,
user: item.name,
pass: item.pass
})
return true
}
})
if(!result)// 未找到
res.send('<h1>此账号异常</h1>')
}) var jsonParser = bodyParser.json()
ser.post('/p1', jsonParser, (req, res) => {
var user = req.body.username
var pass = req.body.password
var result = users.find(item => {
if(item.name == user && item.pass == pass) {
// 账号、密码正确,返回用户界面
res.sendFile(path.resolve(__dirname + '/userinfo.html'))
}
})
if(!result) {
// 账号、密码错误,返回首页
res.download('index.html')
}
}) ser.all('*', (req, res) => {
res.send('<h1>not found route</h1>')
}) // 启动服务,监听8081端口
ser.listen(8081, () => {
console.log('created')
})

2、Router代码

const express = require('express')
const bodyParser = require('body-parser') let rou = express.Router()// 创建Router // Router是完整的中间件和路由系统,故也可在此创建路由中间件
// ser.use((req, res, next) => {
// res.set('access-control-allow-origin', 'http://127.0.0.1:5501')
// next()
// }) rou.get('/rou/g1', (req, res) => {
var id = req.query.userid
}) var urlParser = bodyParser.urlencoded({extended: false})
rou.post('/rou/p1', urlParser, (req, res) => {
var user = req.body.username
var pass = req.body.password
}) rou.all('rou/*', (req, res, next) => {
// 路由检索自上而下,若匹配此路由,说明此Router中没有”有效“匹配的路由,
// 则调用 next() ”跳出“此Router,去主服务检索路由
next()
}) module.exports = rou// 开放接口

6、http模块

推荐一篇博文《05-Node.js—http模块》(转发)。

参考笔记三,P50。

http模块是一个Node.js中与HTTP协议对接的模块,用于搭建HTTP服务,或者说用于搭建js服务器。

相关操作:

1、创建http服务:http.createServer((req, res) => {})

2、获取请求行数据。

方法一:

// 由于http服务封装的req对象中没有query、params属性,
// 故需要使用url模块将req.url进行构造,从中获取请求行数据
const u = require('url')
var url = u.parse(req.url) // url中包含query属性,但其中数据的格式是字符串,故下行代码报错,无法获取
var ID = url.query.userid // 重新构造
var url = u.了parse(req.url, true)
// 这样格式就转化成了js对象

方法二:

// 原理同上,只是换用内置对象URL进行构造,
// 前缀'http...'是任意的。不过,出于业务考虑,应与客户端相同
var url = new URL(req.url, 'http://127.0.0.1:5500') // URL对象中请求行数据封装在属性searchParams中,而不是query
// searchParams中数据的格式不是js对象,需要调用get()获取
var id = url.searchParams.get('id')

3、获取请求体数据。

// req对象中同样没有body属性,需要使用'data'和'end'事件进行获取
var bodyData
req.on('data', temp => {
// temp的类型是String,故直接拼接
bodyData += temp
})
req.on('end', () => {
// bodyData是String,故如此无法获取,
var id = bodyData.userid
})

我暂且也不知如何解析:String → js对象,大家自行补充了。

PS:我未查阅资料的原因:(1)在上面express模块的示例中我提起过,不知为何req对象中没有body属性,故我无法测试;(2)express模块是基于http模块的封装,使用express模块搭建的js服务器更强大。

4、设置响应头时,若有多个值,需使用数组。

5res.write(str)需与res.end(str)连用。其中,res.write()用于附加响应。

6、当使用live-server打开html文件时,项目根目录为当前html文件所在目录。

PS:这一点我还不太理解,至少我测试http://localhost:8081/1.jpg时仍然访问不到图片(1.jpg与当前html文件同目录)。无妨,解决办法往下看。。。

7、关于静态资源无法访问问题

关于这个问题,相关概述在博文《05-Node.js—http模块》(转发)的第4.5项,找到其中“我们该如何解决?”那一段可见。

参考笔记三,P49.3。

在学习此模块时,一开始并未注意这个细节,对这句“对路径进行判断”无法理解,我认为只要路径正确怎会访问不到。

因为本人致力于Java,项目根目录都是自动配置,并不知Nodejs中很多情况需要手动配置。

这个问题出现的场合:

  1. 上面http模块中项目根目录无效导致无法访问静态资源;
  2. js服务器响应html文件,文件内css、js、img等静态资源无效或无法访问。(这就是那位博主所述的情况)

为了让大家充分理解,我逐步说明。。。

首先,若客户端请求的是图像文件,且正常响应,会无效吗?答案是 NO,除非响应有问题。比如:服务器未将图像的所有信息(二进制)响应,那么此图像肯定无法正常显示。(一般不会有这种操作)

然后,若客户端请求的是css、js等文件,同样正常响应,会无效吗?也是 NO。这种情况下即便响应不完整,也不会无效,因为是一并解释。

那为何无效或无法访问?

二种情况:

  1. 客户端处理响应时调用的是text(),而不是html()(以 jq 为例),以字符串的方式处理响应内容,自然无法识别标签(如:<script>),故无效;
  2. 找不到静态资源。

若是第一种情况,调用html()即可。

那为何找不到资源?

走到这一步,客户端已经可以正常处理响应,无论html、css、js还是img,故原因是:

  1. 访问路径有误;
  2. 服务器响应有误。

因为已经可以正常解析响应的html文件,即静态资源标签的src属性有效。

在解析html文件时,会同时根据src属性的路径向服务器发起请求。

因此,这种情况下src必须是完整路径,如:http://127.0.0.1:8081/1.jpg,这样才有可能找到文件。

完整路径能找到静态资源吗?

若js服务器由express模块搭建,只要配置好项目根目录(静态资源中间件),就可以直接找到静态资源。

若js服务器由http模块搭建,由于无法配置项目根目录,故只能由路由对完整路径进行处理,从而返回静态资源(那位博主说“对路径进行判断”就是这个意思)。

示例:

const httpSer = http.createServer((req, res) => {
if (req.url == '/1.jpg') {
fs.readFile(__dirname + url, (err, data) => {
res.end(data)
})
return
}
var data = fs.readFileSync(__dirname + '/index.html')
res.end(data)
})

OK!Perfect!!http模块中项目根目录无效导致无法访问静态资源的问题也解决了。

8、通用设置

参考笔记三,P49.2/4。

“通用设置”指不同模块中业务相同的操作或函数。

  1. 设置状态码:res.statusCode()res.status()
  2. 设置状态码描述:res.statusMessage
  3. 设置响应体:res.write()res.end()res.send()
  4. 设置响应头:res.setHeader('标头', 值)res.set('标头', 值)
  5. 获取请求头:req.headers.refererreq.get('referer')
  6. (就列举这些哈,其他操作或函数不常用或者通过debug就可以知晓,以名达意。)

注:

  1. “或”前操作或函数属http模块,后属express模块,且express模块是基于http模块的封装(express模块兼容http模块);
  2. res.end()res.send()都是设置响应体的末操作,故其后不能再做响应配置。

最后

本人的核心语言是Java,故有时倾向于以Java的思想进行阐述,这可能会给向前端发展的博友们的阅读带来不适。并且,由于本文相当于是我系统学习Node.js的笔记,也基于我的Java功底,所以有些阐述不会那么详细。

不过,Java作为一种强类型的编程语言,我的阐述会很严谨,所以需要大家在阅读时多一点耐心。


本文持续更新中。。。

JS服务端技术—Node.js知识点的更多相关文章

  1. vue.js 服务端渲染nuxt.js反向代理nginx部署

    vue.js的官方介绍里可能提到过nuxt.js,我也不太清楚我怎么找到这个的 最近项目vue.js是主流了,当有些优化需求过来后,vue还是有点力不从心, 比如SEO的优化,由于vue在初始化完成之 ...

  2. node.js服务端程序在Linux上持久运行

    如果要想在服务端部署node.js程序,让其持久化运行,就不能单单使用npm start命令运行,当然了,这样运行是毫无问题的,但是当关闭xshell窗口或者是关闭进程的时候(其实关闭xshell窗口 ...

  3. ASP.NET Core 与 Vue.js 服务端渲染

    http://mgyongyosi.com/2016/Vuejs-server-side-rendering-with-aspnet-core/ 原作者:Mihály Gyöngyösi 译者:oop ...

  4. NET Core 与 Vue.js 服务端渲染

    NET Core 与 Vue.js 服务端渲染 http://mgyongyosi.com/2016/Vuejs-server-side-rendering-with-aspnet-core/原作者: ...

  5. node.js入门系列(一)--Node.js简介

    什么是NodeJS JS是脚本语言,脚本语言都需要一个解析器才能运行.对于写在HTML页面里的JS,浏览器充当了解析器的角色.而对于需要独立运行的JS,NodeJS就是一个解析器. 每一种解析器都是一 ...

  6. Node.js的安装以及Node.js的模块管理

    索引: Node.js的安装以及Node.js的模块管理Node.js开发环境搭建以及对ES6的支持Node.js构建Vue.js项目Vue.js单文件组件的开发基于Vue.js的UI组件(Eleme ...

  7. 【Node.js】2.开发Node.js选择哪个IDE 开发工具呢

    安装完Node.js之后,就要为它选择一个有利的IDE用于开发. 相比较了多个IDE之后,定位在webstrom和sublime上. 有一个简单的比较: webstorm功能很丰富,前端开发工具的集大 ...

  8. 客户端技术:Cookie 服务端技术:HttpSession

    客户端技术:Cookie 服务端技术:HttpSession 07. 五 / android基础 / 没有评论   一.会话技术1.什么是会话:客户打开浏览器访问一个网站,访问完毕之后,关闭浏览器.这 ...

  9. node.js系列笔记之node.js初识《一》

    node.js系列笔记之node.js初识<一> 一:环境说明 1.1 Linux系统CentOS 5.8 1.2 nodejs v0.10.15 1.3 nodejs源码下载地址 htt ...

  10. [js高手之路]Node.js+jade抓取博客所有文章生成静态html文件

    这个周末,恶补了一下jade模板引擎,就为生成静态html文件,这篇文章需要知道jade以及看过我的上篇文章,我先给出他们的参考链接: [js高手之路]Node.js模板引擎教程-jade速学与实战1 ...

随机推荐

  1. NC14526 购物

    题目链接 题目 题目描述 在遥远的东方,有一家糖果专卖店. 这家糖果店将会在每天出售一些糖果,它每天都会生产出 \(m\) 个糖果,第i天的第j个糖果价格为 \(C[i][j]\) 元. 现在的你想要 ...

  2. MySQL 幻象行

    当同一个查询在不同的时间产生不同的行集时,就会出现所谓的幻像问题.例如,如果执行了两次SELECT,但是第二次返回了第一次没有返回的行,那么该行就是一个"幻象"行. 假设在表chi ...

  3. Vue+SpringBoot+ElementUI实战学生管理系统-9.教师管理模块

    1.章节介绍 前一篇介绍了班级管理模块,这一篇编写教师管理模块,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.实现效果 教师列表 修改教师 4.模块 ...

  4. vs 工程中替换 Qt 静态库

    上篇介绍了如何编译 Qt 静态库 编译 windows 上的 qt 静态库 这篇介绍如何替换已有的 Qt 静态库,比如 Qt5.15.0 有很多 bug,我们不得不提升 Qt 版本来避免 bug 导致 ...

  5. win32 - 将剪贴板位图存储为文件

    简单的demo: #include <iostream> #include <fstream> #include <windows.h> typedef struc ...

  6. 【Android 逆向】【攻防世界】app2

    1. 手机安装apk,随便点击,进入到第二个页面就停了 2. jadx打开apk,发现一共有三个activity,其中第三个activity: FileDataActivity 里面有东西 publi ...

  7. 解决Spring boot 单元测试,无法读取配置文件问题。

    1.启动类上加上@EnableConfigurationProperties 2.springboot版本springboot 2.X版本在单元测试中读取不到yml配置文件的值这是个大坑,在项目中写单 ...

  8. linux服务器界面初始--day01

    linux服务器界面初始 ip add show 查看服务器网卡信息还可以使用ifconfig 局域网ip: 192.168.1.0 10.0.0.0 172.16.1.0 如果网卡没有启用,我们需要 ...

  9. Celery在Django项目中集成

    使用celery第一件要做的最为重要的事情是需要先创建一个Celery实例对象,我们一般叫做celery应用对象,或者更简单直接叫做一个app.app应用对象是我们使用celery所有功能的入口,比如 ...

  10. 【LeetCode链表#11】环形链表II(双指针)

    环形链表II 力扣题目链接(opens new window) 题意: 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,使用整数 pos 来表示 ...