前段时间看的b站的免费Node视频做的一些笔记,今天抽空整理了一下,有里面老师课程的东西,也有自己之后查的一些资料或者图片。相当于再做一次总结吧。总体的内容可以看上面的导图。
1. 初识node.js
1.1. 浏览器中的javascript的组成部分
1.1.1 js核心语法、
在学习node之前,首先我们要去了解一下javascript的基础知识,比如需要看下《javascript高级程序设计》,es6或者其他一些js基础的书,博客,视频等。
- 变量,数据类型
- 循环,分钟,判断
- 函数,作用域 this
- 原型链,异步 es6 。。。
1.1.2 webapi
- DOM操作
- BOM操作
- 基于xmlhttprequest 的ajax操作 。。。
1.2. 为什么javascript可以在浏览器中执行呢?
在浏览器中,当有待执行的javascript代码,会调用javascript解析引擎,不同的浏览器采用的是不同的javascript解析引擎
- chrome 浏览器 - v8
- Firefox浏览器 - OdinMonkey
- safri 浏览器 - JSCore
- IE 浏览器 - Chakra
Chrome浏览器的V8引擎性能最好
1.3. 为什么javascript可以操作浏览器的BOM和DOM?
浏览器中内置了很多api,比如DOMApi BOMApi AjaxApi
开发者可以使用javascript代码代码调用这些webapi,然后这些待执行代码就会调用javasciprt解析引擎。
1.4. 浏览器中的javascript运行环境
运行环境是指代码政策运行所需要的环境
Chrome浏览器运行环境如果需要正常执行js:
- v8引擎
- 内置Api:DOM BOM Ajax api
内置api是由运行环境提供的特殊接口,只能在所属的运行环境中被调用
2. node简介
2.1 什么是node.js
node.js是一个基于Chrome V8引擎的javascript运行环境
2.2 node.js中的javascript运行环境
node.js运行环境
- V8 引擎
- 内置API:fs path http js内置对象 querystring 等等。。
- 浏览器是js的前端运行环境
- node.js是js的后端运行环境
- node.js中无法操作DOM,BOM等浏览器内置API
2.3 node.js可以做什么
node.js作为一个js的运行环境,仅仅提供了基础的功能和api,然而,基于node.js提供的这些基础,很多强大的工具和框架层出不穷:
2.4 node.js怎么学?
- javascript基础语法
- node内置API模块(fs,path,http等)
- express mysql
3.fs文件系统模块
3.1 什么是fs文件系统模块
fs模块是node.js官方提供的,用来操作文件的模块,它提供了一系列的方法和属性,用来满足用户对文件的操作需求
fs.readFile(): 用来读取指定文件中的内容 fs.writeFile(): 用来写入指定文件中的内容
const fs = require('fs');
3.2 读取指定文件中的内容
fs.readFile(path[,options],callback)
- path:字符串,表示文件的路径
- options:表示以哪种编码格式来读取文件
- callback:文件读取完后,通过毁掉函数拿到读取的结果
以utf8的编码格式,读取制定文件中的内容,并且打印err和dataStr的值
const fs = require('fs');
fs.readFile('./text.txt','utf8',function(err,dataStr) {
console.log(err); // 执行成功为null
console.log('...');
console.log(dataStr);
})
// null
// ...
// hhh
3.3 写入指定文件中的内容
fs.writeFile(file,data[,options],callback);
- file:字符串,表示文件的路径
- data: 表示需要写入的内容
- options:表示以哪种编码格式来读取文件
- callback:文件读取完后,通过毁掉函数拿到读取的结果
const fs = require('fs');
fs.writeFile('./2.txt','kk','utf-8',function(err,dataStr) {
console.log(err); // 文件写入成功为null
// 写入失败,返回错误对象
console.log(dataStr);
})
4. 考试成绩整理
哈哈=4 哈哈哈哈=7 网盘=9 经济=10
哈哈: 4
哈哈哈哈: 7
网盘: 9
经济: 10
const fs = require('fs');
fs.readFile('./成绩.txt','utf-8',function(err,dataStr) {
console.log(dataStr);
if (err) {
return console.log('读取文件失败')
}
const arrOld = dataStr.split(' ');
const arrNew = [];
arrOld.forEach(item => {
arrNew.push(item.replace('=',': '));
})
const newStr = arrNew.join('\r\n');
fs.writeFile('./成绩-ok.txt',newStr,'utf-8',function(err,dataStr) {
console.log(err);
if (err) {
return console.log('写入失败');
}
})
})
5. 动态路径
在使用fs模块操作文件的时候,如果提供的操作路径是./ ../开通的相对路径时,很容易出现路径动态拼接错误的问题,原因:代码在运行的时候,会以执行node命令时所处的目录,动态拼接出被操作文件的完整路径。
fs.readFileSync('./1.md','utf-8',function(err,dataStr) {
if (err) {
return console.log('错误了');
}
})
比如有个文件b-node 里面有1.js和1.md,现在在1.js里面写node代码读取1.md的内容,执行node 1.js
-
/Users/wangpangzi/Desktop/node-record/b-node node 1.js
-
/Users/wangpangzi/Desktop/node-record/b-node/1.md
-
/Users/wangpangzi/Desktop/node-record node 1.js
-
/Users/wangpangzi/Desktop/node-record/1.md (找不到,报错)
需要在对应的目录下执行node命令。
如何解决这个问题:使用绝对路径 __dirname 表示当前文件目录
6. path 路径模块
6.1 什么是path路径模块
path路径模块手node.js提供的,用来处理路径的模块,它提供了一系列的方法和属性,用来满足用户对路径的处理需求。
例如:
- path.join():用来将多个路径片段拼接成一个完整的路径字符串。
- path.basename():用来从路径字符串中,将文件名解析出来。
如果要在javascript代码中,使用path模块来处理路径,需要先导入:
const path = require('path');
6.2 路径拼接
1. path.join()的语法格式
path.join([...paths]);
- ...paths 路径片段的序列
- 返回值:string
const path = require('path');
const pathstr = path.join('/a','/b/c','../','./d','e');
// ../ 有抵消路径的效果,注意下
// ./ 不会抵消
console.log(pathstr)
const pathstr2 = path.join(__dirname,'./a');
console.log(pathstr2);
// /a/b/d/e
// /Users/wangpangzi/Desktop/node-record/b-node/4-14/a
凡是涉及到路径拼接的操作,都使用path.join(),不用+进行字符串的拼接。
2. path.basename()的语法格式
path.baseename(path[,ext]);
- path 表示一个路径的字符串
- ext 表示文件的扩展名
- 返回值 表示路径中的最后一部分
const path = require('path');
const fpath = '/a/b/c/index.html';//文件存在的路径
var fullName = path.basename(fpath);
console.log(fullName);
// index.html
var fullName1 = path.basename(fpath,'.html');
console.log(fullName1);
// index
3. path.extname()的语法格式
使用path.extname()方法,可以获取路径中的扩展名部分
path.extname(path);
- path 表示一个路径的字符串
- 返回值 返回得到的扩展名字符串
const path = require('path');
const fpath = '/a/b/c/index.html';//路径字符串
const fext = path.extname(fpath);
console.log(fext); // .html
7. http 模块
7.1 什么的客户端和服务器端
在网络节点中,负责消费资源的电脑,叫做客户端;负责对外提供网络资源的电脑,叫做服务器。
http模块是node.js官方提供的,用来创建web服务器的模块,通过http模块提供的http.createServer()方法,就可以方便的把一台普通的电脑,变成一台web服务器,从而对外提供web资源服务。
使用http模块,首先进行导入:
const http = require('http');
7.2 进一步理解http模块作用
服务器和普通电脑的区别在于,服务器上安装了web服务器软件,通过安装这些服务器软件,就可以把一台普通的电脑变成一台web服务器。
7.3 服务器相关的概念
IP地址: 互联网上每台计算机的唯一地址 域名和域名服务器: 端口号:
7.4 创建最基础的web服务器
- 导入http模块
const http = require('http');
- 创建web服务器实例 调用http.createServer()方法,可以快速的创建一个web服务器
const server = http.createServer();
- 为服务器实例榜单request事件,监听客户端的请求
sever.on('request',(req,res) => {
// 只要有客户端来请求服务器,就会触发request事件,从而调用这个事件处理函数
console.log('成功');
})
- 启动服务器 调用服务器实例的.listen()方法,可以启动当前的微博服务器实例。
server.listen(80,() => {
console.log('启动');
});
1. req请求对象 res响应对象
只要服务器接受到了客户端的请求,就会调用通过server.on为服务器榜单的request事件处理函数。
如果想在事件处理函数中,访问与客户端相关的数据和熟悉,可以使用如下方式:
server.on('request',(req,res) => {
// url 地址
// method GET
const str = `jing ${req.url} and ${req.method}`; // jing / and GET
console.log(str);
res.end(str);
})
2. 解决中文乱码的问题
server.on('request',(req,res) => {
const str = `哈哈哈哈 ${req.url} and ${req.method}`; // jing / and GET
console.log(str);
res.setHeader('Content-Type','text/html; charset=utf-8')
res.end(str);
})
3. 根据不同的请求返回不同的响应结果
- 获取请求的url地址
- 设置默认的响应内容为404 not found
- 判断用户请求的是否为/ 或者/index.html首页
- 判断用户请求的是否为/about.html关于页面
- 设置Content-Type 相应头,防止中文乱码
- res.end()把内容响应给客户端
动态响应内容
server.on('request',(req,res) => {
const url = req.url;
let content = '<div>404</div>';
if (url === '/' || url === '/index.html') {
content = '<h1>首页</h1>'
} else if(url === '/about.html') {
content = '<h1>关于</h1>'
}
res.setHeader('Content-Type','text/html; charset=utf-8');
res.end(content);
})
8. 模块化
- 模块化的好处
- CommonJs规定了哪些内容
- nodeJs中模块的三大分类
- npm管理包
- 规范包结构
- 模块的加载机制
8.1 编程里面的模块化
遵守一定的规则,把一个大恩家拆分成独立并且互相依赖的多个小模块
把代码进行模块化划分的好处:
- 提高了代码的复用性
- 提高了代码的可维护性
- 可以实现按需加载
8.2 模块化规范
对代码进行模块化的拆分和组合时,需要遵循的规则
- 使用规范来引入和导出模块
8.3 node中模块的分类
- 内置·模块 http fs http
- 自定义模块:用户创建的模块
- 第三方模块(需要下载使用)
8.4 加载模块
使用require方法进行加载
const fs = require('fs');
const custom = require('./custom.js');
const monent = require('moment');
使用require方法加载其他模块的时候,会窒息被加载模块中的代码;可以省略扩展名
8.5 模块作用域
3.js
const jing = 'jing';
function foo() {
console.log(jing);
}
4.js访问不到3.js中的数据,防止全局污染
const cutom = require('./3.js');
console.log(cutom);
8.6 向外共享模块作用域中的成员
1. module对象
在.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息,
id: '.',
path: '/Users/wangpangzi/Desktop/node-record/b-node/4-20', //当前模块存储路径
exports: {}, // 可以向外共享成员
filename: '/Users/wangpangzi/Desktop/node-record/b-node/4-20/4.js',
loaded: false,
children: [
Module {
id: '/Users/wangpangzi/Desktop/node-record/b-node/4-20/3.js',
path: '/Users/wangpangzi/Desktop/node-record/b-node/4-20',
exports: {},
filename: '/Users/wangpangzi/Desktop/node-record/b-node/4-20/3.js',
loaded: true,
children: [],
paths: [Array]
}
],
paths: [...]
9. node中的模块化
9.1 向外共享模块作用域中的成员
- mmodule.exports对象
在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用。
外界使用require方法导入自定义模块时,得到的就是module.exports所指向的对象
当2中没有东西
const m = require('./2');
console.log(m); // {}
当文件2中写入以下内容: 将会打印{ username: 'zhangsan', sayhello: [Function (anonymous)], age: 24 }
// 在一个自定义模块中,默认情况下,module.exports = {};
const age = 24;
// 向module.exports对象上挂载username属性
module.exports.username = 'zhangsan';
module.exports.sayhello = function() {
console.log('hello');
}
module.exports.age = age;
9.2 共享成员时的注意点
使用require方法导入模块的时候,导入的结果,永远以module.exports指向的对象为准;
3文件
const n = require('./4');
console.log(n);
4文件
module.exports.username = 'zhangsan'; //module.exports上挂载username
module.exports.sayhello = function() { //module.exports上挂载函数sayhello
console.log('hello');
}
// module.exports指向全新的对象
module.exports = {
name: 'jing',
sayhi() {
console.log('0')
}
}
对象属性赋值,和对象赋值
9.3 exports 对象
由于module.exports单词写起来麻烦,为了简化向外共享成员的代码,node提供了提供了exports对象,默认情况下,exports和module.exports指向的是同一个对象,最终结果,以module.exports为准。
const username = 'lj';
module.exports.username = username;
exports.age = 30;
exports.foo = function() {
console.log('hhh');
}
{ username: 'lj', age: 30, foo: [Function (anonymous)] }
9.4 exports和module.exports的使用误区
时刻谨记,require模块的时候,得到的永远是module.exports指向的对象
- 注意1
exports.usename = 'jing';
module.exports = {
gerden:'nna',
age:33
}
{ gerden: 'nna', age: 33 }
- 注意2
module.exports.usename = 'jing';
exports = {
gerden:'nna',
age:33
}
{ usename: 'jing' }
- 注意3
exports.usename = 'jing';
module.exports.age = 33;
{ usename: 'jing', age: 33 }
- 注意4
exports = {
usename:'jing',
age:90
}
module.exports = exports;
module.exports.gender = 'nam';
{ usename: 'jing', age: 90, gender: 'nam' }
9.5 CommonJs 模块化规范
node.js 遵循了CommonJs的规范,CommonJs规定了模块的特性和各模块之间如何相互依赖
CommonJs规定:
- 每个模块内部,module变量代表当前模块
- module变量是一个对象,它的exports属性是对外的接口
- 加载某个模块,其实是价值该模块的module.exports熟悉,require方法用于加载模块
10. web开发模式
目前主流的web开发模式有两种
- 基于服务器端渲染的传统web开发模式
- 基于前后端分离的新型web开发模式
10.1服务器端渲染的web开发模式
服务器端渲染:服务器发送给客户端的html页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要用ajax这样的技术额外请求页面的数据
app.get('/index.html',(req,res) => {
// 1.要渲染的数据
const user = {name:'zs',age:20}
// 2. 服务器端通过字符串的拼接,动态生成html内容
const html = `<h1>姓名:${user.name},年龄: ${user.age}</h1>`;
// 3.把生成好的页面内容响应给客户端,因此,客户端拿到的好似带有真实数据的html,页面
res.send(html);
})
10.2 服务器端渲染的优缺点
优点:
- 前端耗时少:因为服务器端负责动态生成html内容,浏览器只需要直接渲染页面就可以。尤其是移动端
- 有利于SEO: 因为服务器端响应的是完整的html页面内容,所以爬虫更容易爬取获得信息,更有利于SEO
缺点:
- 占用服务器端资源:服务器端完成html页面内容的拼接,如果请求较多,会对服务器端造成一定的压力
- 不利于前后端分离,开发效率低:使用服务器端渲染,则无法进行分工合作,尤其对于前端复杂高的项目,不利于项目高效开发
10.3 前后端分离开发
前后端分离:前后端分离的开发模式:依赖于ajax技术的广泛应用,简而言之,前后端分离的web开发模式,就是后端只负责提供api接口,前端使用ajax调用接口的开发模式。
优点:
- 开发体验好:前端专注于ui页面的开发,后段专注于api的开发,且前端有更多的选择性
- 用户体验好:ajax技术的广泛应用,极大的提高了用户的体验,可以轻松的实现页面的局部刷新。
- 减轻了服务器端的渲染压力,因为页面最终是在每个用于的浏览器中生成的。
缺点:
- 不利于SEO(搜索引擎优化),因为完整的html页面需要在客户端动态拼接下完成,所以爬虫对无法爬取页面的有效信息。 (解决方案,利于vue,react等前端框架SSR:服务器端渲染技术,能够很好的倔强SEO问题)
10.4 前后端身份认证
通过一定的手段,完成对用户身份的确认
在web开发中,涉及用户的身份认证:手机验证码登陆,邮箱密码登陆,二维码登陆
10.5 不同开发模式下的身份认证
对应服务器端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:
- 服务器端渲染推荐使用session认证机制
- 前后端分离推荐使用Jwt认证机制
1. session认证机制
基于http协议的无状态性
了解http协议的无状态是进一步学习session认证机制的必要前提。
http协议的无状态性,指的是客户端的每次http请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次http请求的状态
如何突破http无状态的限制
cookie
2. 什么是cookie
cookie是存储在客户端浏览器中的一段不超过4kb的字符串,它由一个名称,一个值和其他几个用于控制cookie有效期,安全性,用于范围的可选属性性组成
不同域名下的cookie各种独立,每当客户端发起请求时,会自动把当前域名下所有未过期(expires)的cookie一起发送到服务器
- 自动发送(发请求时)
- 域名独立
- 过期时限
- 4kb限制
3. cookie 在身份证中的作用
-
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的cookie,客户端会自动将cookie保存在浏览器中
-
当客户端再次请求服务器端数据的时候,浏览器会自动将身份认证相关的cookie,通过请求头的形式发给服务器端,服务器即可验明客户端的身份
4. cookie不具有安全性
cookie是存储在浏览器中的,而且浏览器也提供了读写cookie的api,因此cookie很容易被伪造,不具有安全性,因此不建议服务器将重要的隐私数据,通过cookie的形式发送给浏览器。
cookie+session认证(服务器端认证)
5. session 工作原理
- 客户端-服务器端: 客户端登陆:提交账号和密码,服务器端严重账号和密码,将登陆成功后的用户信息存储在服务器的内存,同时生产对应的cookie字符串
- 服务器端-客户端: 服务器端响应:将生产的cookie响应给客户端,浏览器端自动把cookie存储在当前域名下
- 客户端-服务器端: 当客户端下一次进行请求的时候,通过请求头自动把当前域名下未过期的可用的cookie发生给服务器。
- 服务器根据请求头的cookie从内存中对应的用户信息,当前用户信息认证成功后,服务器端针对当前用户生成特定的响应内容。
- 服务器端-客户端:把当前用户对应的页面内容响应给浏览器。
10.6 express 中使用session认证
- 配置express-session中间件安装成功后,需要通过app.use()来注册session中间件,示例代码如下:
var session = require('express-session');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
const express = require('express');
const app = express();
// 配置session中间件
const session = require('express-session');
app.use(
session(
{
secret: 'jing',
resave: false,
saveUninitialized: true,
}
)
)
app.use(express.static('./pages'));
app.use(express.urlencoded({extended: false }));
10.7 向session中存取数据,以及销毁
当express-session中间件配置成功后,可以通过req.session来访问和使用session对象,从而存储用户的关键信息。
// 登陆api接口
app.post('/api/login',(req,res) => {
//判断用户提交的登陆信息是否正确
if (req.body.username !== admin || req.body.password !== '0000') {
return res.send({status: 1,msg:'登陆失败'})
}
//只有成功配置了express-session这个中间件之后,才能呗访问到
// 将登陆成功后的用户信息,保存到session中
req.session.user = req.body; // 用户的信息
req.session.islogin = true; // 用户的登陆状态
res.send({status:0,msg:'登陆成功'})
})
// 从session中取数据
app.get('/api/username',(req,res) => {
// 判断用户是否登陆
if(!req.session.islogin) {
return res.send({status:1,msg:'jing'})
}
res.send({status:0,msg:'success',username:req.session.user.username})
})
// 退出登陆的接口
app.post('/api/logout',(req,res) => {
//清空当前客户端对应的session信息
req.session.destroy()
res.send({
status: 0,
msg: '退出登陆成功'
})
})
10.8 JWT认证机制
- 了解session认证的局限性
session认证机制需要配合cookie才能实现,由于cookie默认不支持跨域访问,所以,当涉及到钱的跨域请求后端接口的时候,需要做很多额外的处理,才能实现跨域session认证
注意:
- 当前端请求后端接口不存在跨域问题的时候,推荐使用session身份认证机制
- 前端需要跨域请求后段接口的时候,不推荐使用session身份认证机制,推荐使用JWT认证机制
1. 什么是JWT
JWT:是目前最流行的跨域认证解决方案
- 客户端登陆提交账号和密码给服务器端
- 服务器端验证账号和密码,验证通过后,将用户的信息对象,经过加密之后生产token字符串
- 服务器端想要:将生成的token发送给客户端
- 客户端将接收到的token存储到localstorage或者sessionstorage
- 客户端再次发起请求时,通过请求头的Authorization字段,将token发送给服务器
- 服务器端将token字符串还原成用户的信息对象
- 用户的身份认证成功后,服务器针对当前用户生成特定的响应内容
- 服务器端响应,把当前用户对应的页面内容响应给客户端
用户的信息通过token字符串的形式,保存在客户端浏览器中,服务器端通过还原token字符串的形式来认证用户身份
2.JWT字符串的组成部分
-
header:头部
-
payload: 有效荷载 这个部分是真正的用户信息,它是用户信息经过加密后生成的字符串
-
signature: 签名
比如:{id:3,username:'admin'}
用户的信息经过加密后,就是payload部分队员的字符串
header+signature只是为了保证token的安全性。
3. JWT的使用方式
客户端将接收到的token存储到localstorage或者sessionstorage,客户端再次发起请求时,通过请求头的Authorization字段,将token发送给服务器
格式如下:
Authorization: Bearer <token>
npm install jsonwebtoken express-jwt
-
jsonwebtoken: 用于生产JWT字符串
-
express-jwt: 用于将JWT字符串解析还原成JSON对象
// 导入用于生产JWT字符串的包
const jwt = require('jsonwebtoken')
// 导入用于将客户端发送过来的JWT字符串,解析还原车管JSON对象的包
const expressJWT = require('express-jwt');
4. 定义secret密钥
为了保证JWT字符串的安全性,防止JWT字符串在网络传输中被比尔破解,需要撞门定义一个用于加密和解密的secret密钥