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

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. NC16513 无关(relationship)

    题目链接 题目 题目描述 若一个集合A内所有的元素都不是正整数N的因数,则称N与集合A无关. 给出一个含有k个元素的集合A={a1,a2,a3,...,ak},求区间[L,R]内与A无关的正整数的个数 ...

  2. Typora 快捷方式给字体设置颜色

    1.下载并安装 AutoHotkey (具体步骤可自行百度) 访问 AutoHotkey 主页: https://autohotkey.com/ 点击下载: https://autohotkey.co ...

  3. NamedTuple技巧用法

    PS: 第一眼看到这个代码的时候,就联想到了go中的构造函数,虽然知道go中的构造函数其实就类比于python中的构造函数__init__,但是不得不说,这个太像了 在日常编码中,我们经常需要写一些返 ...

  4. go控制grpc的metadata

    grpc让我们可以像本地调用一样实现远程调用,对于每一次的RPC调用中,都可能会有一些有用的数据,而这些数据就可以通过 metadata来传递.metadata是以key-value的形式存储数据的, ...

  5. Android 全面屏体验

    一.概述 Android 应用中经常会有一些要求全屏显隐状态栏导航栏的需求.通过全屏沉浸式的处理可以让应用达到更好的显示效果.在 Android 4.1 之前,只能隐藏状态栏, 在 Android4. ...

  6. 03、Etcd 客户端常用命令

    上一讲我们安装 etcd 服务端,这一讲我们来一起学学如何使用 etcd 客户端常见的命令.文章内容来源于参考资料,如若侵权,请联系删除,谢谢. etcd可通过客户端命令行工具 etcdctl 对et ...

  7. 【Azure 应用服务】Azure Function App在部署时候遇见 503 ServiceUnavailable

    问题描述 在VS Code中编写好 Azure Function App代码后,通过  func azure functionapp publish 部署失败,抛出 503 Service Unava ...

  8. 21 Educational Codeforces Round 136 (Rated for Div. 2)Knowledge Cards(树状数组、set、+思维、数字华容道)

    最开始猜了个结论错了,猜的是必须要有\(m+n-1\)个方格空着,这样才能保证任意一张牌能从起点到终点. 其实并不是,参考数字华容道,实际上是只要除了终点和起点,以及自身这个方格.我们只需要留出一个空 ...

  9. CUDA指针数组Kernel函数

    技术背景 在前面的一篇文章中,我们介绍了在C++中使用指针数组的方式实现的一个不规则的二维数组.那么如果我们希望可以在CUDA中也能够使用到这种类似形式的不规则的数组,有没有办法可以直接实现呢?可能过 ...

  10. Word中的公式复制到Visio中乱码问题

    将word中编辑好的公式复制到Visio中出现乱码问题 如图所示问题: 解决方案(Visio 选项 --> 高级 --> 显示 ->勾选禁用增强元文件优化) 具体的公式导入和解决操作 ...