Node.js - day1
1. 整个node.js课程学习目标
- 【基础概念】
- 什么是node.js以及node.js的特点
- node.js适合做什么
- 什么是模块化、什么是Common.js模块化规范
- 模块、包、npm、包加载机制
- etc…
- 【基本能力】
- 掌握node.js中基本API的使用
- 能够使用npm管理包
- 能够使用ES6新语法
- 能够使用node.js创建基本的web服务器
- 【高阶内容】
- 能够使用Express框架、结合mysql数据库实现后端网站
注意:整个node.js学习阶段,概念类知识点较多,需要理解透彻!
2. 了解基本概念
2.1 语言 和 环境(平台) 之间的关系
- 语言,是编写代码的语法规范;程序员遵循特定的语法规范,编写出来的代码,只是单纯的文本字符串而已,并不具备可执行的特点;
- 环境(平台),提供了执行代码的能力,如果程序员编写的代码想要成功执行,必须要依赖于特定的执行环境;
- 例如:Javascript代码可以被浏览器中的JS解析引擎执行;
- 所以,浏览器,就是一个 Javascript 的执行环境;因为 Javascript 代码可以在浏览器中被执行;
2.2 前端 和 后端
- 前端主要工作
- 页面结构
- 美化页面样式
- 书写页面的业务逻辑
- 使用Ajax调用后台接口
- 后端主要工作
- 操作数据库
- 对外暴露操作数据库的API接口
- 前后端协作开发
2.3 浏览器环境 中的 Javascript
- 诞生:上世纪 90年代左右,网景 liveScript、表单验证
- 一战:
- 主角:微软和网景
- 结果:微软IE胜出
- 胜利的果实:ECMAScript 语言标准的诞生
- 二战
- 主角:谷歌、火狐、IE
- 结果:谷歌胜出
- 胜利的果实:谷歌的V8引擎是最快的JS解析引擎
- 回顾:浏览器中 Javascript 的组成部分
- ECMAScript核心 + DOM + BOM
2.4 Node环境 中的 Javascript
什么是 Node.js:就是 Javascript 的服务器端运行环境,可以让程序员使用 Javascript 来实现服务器端的编程;
是基于chorm的v8引擎的javascript运行环境,事件驱动,非阻塞I/O
node是后台语言,用javascript实现
Node.js 中 Javascript 的组成部分
- ECMAScript 核心 + 全局成员 + 核心 API 模块
- 全局成员:console、setInterval、setTimeout。。。
- 核心 API 模块:就是 Node 平台 单独提供的一些API,这些API是Node平台所独有的;
注意:Node.js 中 没有 BOM 和 DOM
- 由于 Node 服务器端运行环境中,没有浏览器 和 HTML 的概念,所以,Node中的javascript 提出了 DOM 和 BOM 这两个对象模型,取而代之的,是 全局成员 和 核心 API 模块;
2.5 ECMAScript 规范,浏览器中的js,Node中的js之间的关系
- ECMAScript 规范(标准):就是一本书,这本书中规定了语言的特性;
- 浏览器中的js:
- 浏览器中的 js 组成部分:ECMAScript 核心 + DOM + BOM
- Node中的js:
- Node中的 js 组成部分: ECMAScript 核心 + 全局成员 + 核心API成员
2.6 画图说明 浏览器中的js 与 Node中 js 之间的区别
2.7 总结
什么是 Node.js(我们自己给它下个定义)
就是一个服务器端的Javascript运行环境,可以让程序员,通过 Javascript 做 后台服务器编程开发
学习 Node.js 可以做什么
- 像 PHP 一样,使用 Javascript 编写符合规范的后端 API 接口 或 网站
- 使用 Node.js 开发一些实用的工具 或 包
- 基于 Socket 技术,开发类似于聊天室之类的即时通讯项目
- 基于 Electron 环境,开发桌面软件
- ect…
3. 环境安装
3.1 LTS 和 Current 版本区别
- LTS:【推荐在企业中使用】,是长期稳定版的安装包,运行稳定、安全;
- Current:【推荐学习或尝鲜去使用】,是最新特征版,这个安装包中有最新的Node特性;
3.2 下载安装
安装的 node中包含了npm
3.3 查看 Node 版本号
- 打开终端,在命令行输入命令
node -v
即可 - Tips: 如何进入终端呢?
- 【推荐】在任意目录的空白位置,先按住
shift
键不松开,然后,在空白位置,鼠标右键单击,会出来一个右键菜单,选择在此处打开 powershell/cmd 窗口
- 【了解】使用快捷键
windows徽标 + R
打开运行面板,输入cmd
后直接回车;
- 【推荐】在任意目录的空白位置,先按住
3.4 path 环境变量
- 什么是path环境变量
- Path环境变量的作用:能够让我们在终端中执行相关的命令,从而快速启动应用程序;
- 输入命令时,先在当前目录下查找此程序,如果没有找到,就去系统的path变量里找
- 系统环境变量和用户环境变量的区别
- 用户环境变量:电脑上可以设置不同的用户,各个之间的用户变量是私有的
- 系统环境变量:是公共的,不同用户都可以使用
- 通过终端输入命令的形式,快速启动应用程序时,路径的查找规则
- 先在当前运行 终端命令的 目录中查找,如果有则直接运行;
- 如果当前运行 终端命令的 目录中没有,则去全局的path环境变量中查找;
4. Node.js 环境中 执行 js 代码的两种方式
4.1 node 命令【推荐】
- 直接使用
node 要执行的js文件的路径
来执行指定的JS文件
小技巧:
- 在终端中,使用键盘的↑, 可以快速定位到上一次执行的命令
- 在终端中,使用键盘的
tab
键能够快速补全路径- windows 系统上,在终端中输入
cls
可以清屏
4.2 REPL 环境 - 了解
- 如何进入 REPL 环境: 打开任意终端,直接输入
node
并回车,就会进入到 REPL 环境中; - 如何离开 REPL 环境:按两次
ctrl + c
就能退出 REPL 环境; - REPL中,每个字母代表什么意思呢:
- R: Read 的意思,每当我们输入完毕代码之后,只要敲击回车,Node环境就会读取用户输入的代码
- E:Evaluate 的意思,表示把 Read 进来的用户代码,调用 类似于 Eval 的函数,去解析执行
- P:Print 输出的意思;把第二步中解析执行的结果,输出给用户;
- L:Loop 循环的意思,表示当输出完毕之后,进入下一次的 REP循环
5. ECMAScript 6常用语法
5.1 let(变量) 与 const(常量)
vscode打开集成的终端 ctrl+反引号(tab上方的),用tab可以补全路径
- 之前定义变量,用 var 关键字;有如下主要缺点:
- 存在变量提升问题,降低 js 代码的可阅读性
- 没有块级作用域,容易造成变量污染
- let 主要特性:用于定义变量
- 不存在变量提升问题,只有定义之后才能使用此变量
- 有
{ }
作用域
- const 主要特性:
- 不存在变量提升问题,只有定义之后才能使用此变量
- const 定义的常量,无法被重新赋值
- 当定义常量的时候,必须定义且初始化,否则报语法错误
- const 定义的常量,也有 块级作用域
5.2 变量的解构赋值(常用)
定义:所谓的解构赋值,就是把 某个对象中的属性,当作变量,给解放出来,这样,今后就能够当作变量直接使用了;
- 可以使用
:
为解构出来的变量重命名
js
// 变量的解构赋值
let user={
name:'zs',
age:18,
gender:'女'
}
// 结构赋值的标准写法
//把user中的变量释放出来
let {name,age:userage,gender}=user;
console.log(name);
//起了别名之后不能再访问age,只能方位userage
5.3 箭头函数(常用)
语法:
定义普通的
function
函数function show() { console.log('这是普通 function 定义的 show 方法') }
把方法定义为箭头函数(最完整的写法)
(形参列表) => { 函数体代码 } () => { console.log('这是普通 function 定义的 show 方法') }
箭头函数,本质上就是一个匿名函数;
箭头函数的特性: 箭头函数内部的 this, 永远和 箭头函数外部的 this 保持一致;
document.getElementById('btn').onclick = function () { setTimeout(function() { this.style.backgroundColor = 'red'; }, 1000); }//因为This的指向问题,此代码会报错
变体:
- 变体1:如果箭头函数,左侧的形参列表中,只有一个参数,则,左侧小括号可以省略;
- 变体2:如果右侧函数体中,只有一行代码,则,右侧的
{ }
可以省略,同时rreturn也可以省略; - 变体3:如果箭头函数左侧 只有一个 形参,而且右侧只有一行代码,则 两边的
()
和{}
都可以省略
注意: 如果我们省略了 右侧函数体的
{ }
,那么,默认会把 右侧函数体中代码的执行结果,当作箭头函数的调用结果 return 出去;var add = x => x+x
5.4 对象中 定义方法
和 定义属性
的便捷方式
let name='zs';
let age = 13;
let show =() => {
console.log('show');
}
let person = {
name,
age,
show,
// see : function () {
// console.log('see');
// }
see() {
console.log('see');
}
}
console.log(person)
6. 文件操作
6.1 文件读取
- fs.readFile
const fs = require('fs');
fs.readFile__dirname+'/files/2.txt','utf-8',(err,data)=>{
if(err) return console.log('文件读取失败'+err.message);
console.log('读取成功,文件的内容是'+data);
})
6.2 文件写入
- fs.writeFile
const fs = require('fs');
fs.writeFile(__dirname+'/files/2.txt','222你好',(err)=>{
if(err) return console.log('文件写入失败'+err.message);
console.log('文件写入成功');
})
6.3 文件追加
- fs.appendFile
const fs = require('fs');
fs.appendFile(__dirname+'/files/2.txt','\n444',(err)=>{
if(err) return console.log('追加失败'+err.message);
console.log('追加成功');
})
6.4 fs模块中路径操作问题【难点】
使用 fs 模块操作文件的时候,如果提供的操作路径是
相对路径
, 则会根据当前执行node命令时的磁盘目录,去拼接提供的文件的相对路径,从而容易出现问题;例如:const fs = require('fs') // 调用 fs.readFile 方法时,提供的第一个参数是相对路径,容易出现问题 fs.readFile('./files/1.txt', 'utf-8', (err, data) => { if(err) return console.log(err.message) console.log(data) })
推荐使用 node 中提供的
__dirname
来解决 fs 模块操作文件时候的路径问题
6.5 读取文件信息 fs.stat
fs.stat(__dirname+'/1.txt',(err,stats)=>{
if(err) return console.log('获取文件信息失败'+err.message);
console.log(stats.birthtime);
console.log(stats.size);
})
6.6 读取指定目录中所有文件的名称 fs.readdir
6.7 复制文件 fs.copyFile
const fs = require('fs');
fs.copyFile('./2.txt',__dirname+'/files/copy.txt',(err)=>{
if(err) return console.log('拷贝失败'+err.message);
console.log('拷贝成功')
})
7. 练习
- 整理
成绩.txt
文件中的数据到成绩 - ok.txt
文件中,整理好的文件中,格式类似于:
小红:99
小白:100
小黄:70
小黑:66
小绿:88
原来的格式:小红=99 小白=100 小黄=70 小黑=66 小绿=88
const fs = require('fs')
const path = require('path')
fs.readFile(path.join(__dirname,'/成绩.txt'),'utf-8',(err,data)=>{
if(err) return console.log('读取文件失败'+err.message)
// console.log(data)
let newStr = data.split(' ')
// console.log(newStr)
let newData = []
newStr.forEach(item=>{
if(item.length>0){
// console.log(item)
let newScore = item.replace('=',':')
//console.log(newScore)
newData.push(newScore)
}
})
// console.log(newData)
fs.writeFile(path.join(__dirname,'/成绩-ok.txt'),newData.join('\n'),err=>{
if(err) return console.log('写入失败')
console.log('写入成功')
})
})
8. 路径操作
- path.join([…paths])
- path.sep 输出当前系统的路径分隔符(windows为,Linux为/)
- path.basename(path[, ext]) 获取文件名,最后一个\之后的
- path.dirname(path) 获取路径名,最后一个\之前的
- path.extname(path) 获取文件的扩展名
Node.js - day2
0. 学习目标
- 了解同步和异步的概念
- 认识模块化 和 了解 Node.js 中CommonJS 模块化规范
- 了解npm包的规范
- 掌握npm的基本使用
- Node.js中
http
模块的基本使用
1. Javascript 的单线程和异步
Javascript 的解析和执行一直是单线程的,但是宿主环境(浏览器或node)是多线程的;
异步任务(ajax,定时器等耗时的任务)是由宿主环境开启子线程完成,并通过事件驱动、回调函数、队列,把完成的任务, 交给主线程执行;
所有的一部任务都是由浏览器执行的
Javascript解析引擎,一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行。
事件队列中任务执行的条件:1)主线程已经空闲
2)任务满足触发条件:定时函数(延时时间已经达到),事件函数(事件被触发),ajax回调函数(服务器端数据被响应)
2. Node中的API为什么几乎都是异步操作
- 什么样的操作需要使用异步处理:要把 耗时的操作,放到异步中由浏览器去执行,浏览器在执行过程中碰到回调函数时,会把它放到事件队列中,待到js引擎空闲时,就会去事件队列中查找他们并执行。;
- 异步执行任务的好处:能够提高 耗时的任务它的执行效率,提高 JS 解析引擎的工作效率;
3. 认识模块化
模块化就是一种约定,一定规范;
场景模拟:小强,小黄,小刚 共同基于 Node.js 开发项目!
- 为什么要有模块化:为了解决文件之间的依赖关系;
- 注意:模块化是一种开发思想;具体开发中需要定制符合实际需求的模块化规范!
- 大家可以把模块化规范,认为是一种明文的约定,大家都按照相同的约定写代码,减少了不必要的沟通成本,极大方便了各个模块之间的调用,方便别人,同时也方便自己;
4. 了解 CommonJS 规范
- 作用:是一套 Javascript 的模块化规范,规定了 模块的特性 和 各模块之间如何相互依赖;
- 用途:Node.js 中使用了 CommonJS 规范;
- 特点:同步加载模块;不适合在浏览器端使用,在浏览器端适合使用cmd.amd;
- CommonJS规范都定义了哪些内容:require,export,moudelwiki 对于 Modules 的描述
5. 模块作用域 和 全局作用域
在Node.js中有两个作用域,分别是 全局作用域 和 模块作用域;
- 全局作用域使用
global
来访问,类似于浏览器中的window
; - 每个 Javascript 文件,都是一个单独模块,每个模块都有自己独立的作用域,因此:使用require时模块中的成员,默认无法被其它模块访问。
5.1 使用 global 全局作用域在模块之间共享成员
- 如果在某个模块内部,想为 全局的 global 作用域挂载一些属性,需要显示的调用
global.***
来挂载; - 注意:在开发中,一般情况下,不推荐使用
global
全局作用域来共享成员,会存在全局变量污染问题;
5.2 模块作用域
module(模块标识)
module 属性是 Common JS 规范中定义的,它是一个对象,表示当前这个具体的 js 模块;
require(引用模块)
每一个实现了 CommonJS 规范的模块,必须定义一个 require() 函数,使用这个 require 函数,就能够 很方便的导入其它 模块中的成员,供自己使用;
exports(暴露模块成员)
每一个模块中,如果想要把自己的一些私有成员,暴露给别人使用,那么,必须实现一个 exports 对象,通过exports对象,可以方便的把模块内私有的成员,暴露给外界使用;
5.3 module.exports 和 exports 的关系
module.exports
和exports
默认引用了同一个空对象,指向地址相同;module.exports
和exports
作用一致,都可以向外暴露成员;- 一个模块作用域中,向外暴露私有成员时,永远以
module.exports
为准,开发中推荐使用,画内存图理解;
6. 了解 - 浏览器端的 AMD 和 CMD 模块化规范
注意:浏览器端不能使用 CommonJS规范;因为 CommonJS 下,模块是同步加载的;
AMD/CMD可以理解为是commonjs在浏览器端的解决方案,AMD/CMD下,模块都是异步加载的;
- AMD模块化规范代表:RequireJS
- 主要特性1:对于依赖的模块,AMD 是提前执行;
- 主要特性2:推崇依赖前置;
- CMD模块化规范代表:SeaJS
- 主要特性1:对于依赖的模块,CMD 是延迟执行;CMD 推崇 as lazy as possible.
- 主要特性2:推崇依赖就近;
- ES6的模块化(大趋势):es6是在语言标准层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案;
7. Node.js 中 模块
和 包
的概念
回顾:Node.js 由三部分组成:ECMAScript 核心 + 全局成员 + 模块成员
7.1 模块成员的分类
模块成员,根据一些区别,又可以分为三大类: 核心模块、第三方模块、用户自定义模块
7.1.1 核心模块
- 什么是核心模块:
- 随着Node.js的安装包,一同安装到本地的模块,叫做核心模块;
- 例如:
fs
,path
等模块,都是由Node.js官方提供的核心模块; - 只要大家在计算机中,安装了Node这个应用程序,那么,我们的计算机中就已经安装了所有的 核心模块;
- 如何使用核心模块:
require('核心模块标识符')
7.2.2 第三方模块
- 什么是第三方模块:
- 一些非官方提供的模块,叫做第三方模块;
- 注意,第三方模块,并不在我们的计算机上;
- 如果大家需要使用某些第三方模块,必须去一个叫做
NPM
的网站上搜索并下载才能使用;
- 如何使用第三方模块:
- 先从 npm 官网上下载指定的第三方模块
- 使用
require('第三方模块的名称标识符')
来导入这个模块 - 根据 第三方模块的 官方文档,尝试使用
7.2.3 用户自定义模块
- 什么是用户模块:
- 程序员在自己项目中写的 Javascript 文件,就叫做 用户自定义模块;
- 如何使用用户模块:
require('路径标识符')
7.2 包的定义和使用
7.2.1 什么是包
- 英文名叫做
Packages
,包是在模块基础上更深一步的抽象; - 包的目的:方便分发和推广基于 CommonJS 规范实现的 应用程序 或 类库;
- 包可以看作是 模块、代码 和 其它资源 组合起来形成的 独立作用域;
7.2.2 规范的包结构
- 包都要以一个单独的目录而存在;
package.json
必须在包的顶层目录下;package.json
文件必须符合 JSON 格式,并且必须包含如下三个属性:name
,version
,main
- name: 包的名字
- version: 包的版本号
- main: 表示包的入口文件
- 二进制文件应该在bin目录下;
- javaScript代码应该在lib目录下;
- 文档应该在doc目录下;
- 单元测试应该在test目录下;
- Node.js对包要求并没有那么严格,只要顶层目录下有
package.json
,并符合基本规范即可;
7.2.3 包描述文件 package.json【了解】
name:包的名称,必须是唯一
description:包的简要说明
version:符合语义化版本识别规范的版本字符串
keywords:关键字数据,通常用于搜索
maintainers:维护者数组,每个元素要包含name、email、web可选字段
contributors:贡献者数组,格式与maintainers相同。包的坐着应该是贡献者数据的第一个元素
bugs:提交bug的地址,可以是网址或者电子邮件地址
licenses:许可证数组,每个元素要包含type和url字段
repositories:仓库托管地址数组,每个元素要包含type、url和path字段
dependencies:包的依赖,一个关联数组,由包名称和版本号组成。
devDependencies:开发依赖项,表示一个包在开发期间用到的依赖项
7.2.4 自己尝试定义一个包含 加减乘除的 calc 计算器包
8. npm
8.1 npm 的两层含义
- NPM 是一个 第三方模块的托管网站,指的就是
https://www.npmjs.com/
; - NPM 是Node的包管理工具(全名叫做 Node package manager),在我们安装Node时候,就已经顺便也安装了 NPM 这个管理工具;
8.2 安装和卸载全局包(i5ting_toc)
- 什么是全局的包:安装到计算机全局环境中的包,叫做全局包;安装的全局包可以在当前电脑的任何目录下,直接通过命令行来访问;
- 如何安装全局包:运行
npm install 包名 -g
即可;其中-g
参数,表示 把包安装到全局目录中的意思; - 全局包的安装目录:
C:\Users\用户目录\AppData\Roaming\npm
- 什么样的包适合安装到全局:工具性质的包,适合安装到全局;
- 如何卸载全局包:要卸载某个全局的包,直接运行
npm uninstall 包名 -g
即可;其中uninstall
表示卸载的意思;
8.3 安装和卸载本地包
- 什么是本地的包:跟着项目安装的包,叫做本地包;本地包都会被安装到 node_modules 目录下;
- 注意:如果拿到一个空项目,必须在当前项目根目录中,先运行
npm init
或者npm init -y
命令,初始化一个package.json
的配置文件,否则包无法安装到本地项目中; - 如何安装本地包:运行
npm i 包名 --save
即可安装本地包;都安装到了当前项目的node_modules
目录下;
- 如果大家用的是npm 5.x的版本,可以不指定
--save
命令,如果用的是 npm 3.x 的版本,则需要手动指定--save
;
- 如果大家用的是npm 5.x的版本,可以不指定
package-lock.json
文件中记录了曾经装过的包的下载地址,方便下次直接下载包,能够加快装包的速度,提升装包的体验;- 如何卸载本地包:使用
npm uninstall/remove 包名 -S/-D
即可卸载指定的本地包;
8.4 其它常用命令
--save
的缩写是-S
开发阶段和上线阶段都会用--save-dev
的缩写是-D
开发阶段使用,上线之后不需要使用,在devdependence节点下install
的缩写是i
- 注意:
dependencies
节点,表示项目上线部署时候需要的依赖项;devDependencies
节点,表示项目在开发阶段需要的依赖项,但是当项目要部署上线了,devDependencies
节点中的包,就不再需要了! - 注意:当使用
npm i
快速装包的时候,npm会检查package.json
文件中,所有的依赖项,然后都为我们安装到项目中 --production
表示只安装dependencies
节点下,记录的包,不安装devDependencies
节点下的包;当项目要上线了,才会使用--production
命令
8.5 解决 npm 下载慢问题
- 默认,NPM在下载包的时候,连接的是国外的服务器,所以,有时候如果网速不是特别好,可能下载不下来包;此时,大家可以全局安装一个的工具,叫做
cnpm
- 如何安装
cnpm
:运行npm i cnpm -g
即可; - 如何使用
cnpm
:在装包的时候,只需要把npm
替换成cnpm
即可,例如:
- 使用
npm
安装jquery
:运行npm i jquery -S
- 使用
cnpm
安装jquery
: 运行cnpm i jquery -S
- 使用
9. 使用 Node 构建 web 应用
- PHP是后端的网站开发语言,PHP 开发出来的网站,可以通过 Apache 服务器托管运行起来;
- 疑问:Node中,可以使用 Javascript 编写后端网站,那么,有没有类似于 Apache 这样的服务器软件,来提供对应的网站服务呢?必须自己手写一个静态资源服务器
9.1 B/S 交互模型
什么是B/S:特指基于 浏览器(Browser) 和 服务器(Server) 这种交互形式;
- 什么是服务器:在网络节点中,专门对外提供资源服务的一台电脑;
- 什么是客户端:在网络节点中,专门用来消费服务的一台电脑;
- HTTP 协议的通信模型:
请求 - 处理 - 响应
的过程;
- 请求:由客户端发起请求;
- 处理:由服务器端处理请求;
- 响应:服务器端把处理的结果,通过网络发送给客户端;
- 什么是静态资源:服务器端只需要读取并直接发送给客户端、不需要进一步处理的资源,叫做静态资源;
- 什么是动态资源:服务器端没有现成的资源,需要服务器端动态生成的资源,叫做动态资源;
9.2 实现一个类似于Apache的 静态资源服务器
使用
http
核心模块,创建最基本的web服务器
创建最基本的web服务器
- 创建服务器:使用
const server = http.createServer()
创建服务器; - 绑定监听事件:通过
server.on('request', function(req, res) { 请求的处理函数 })
绑定事件 并 指定 处理函数; - 启动服务器:通过
server.listen(端口, IP地址, 启动成功的回调函数)
来启动服务器;
//引入http模块 const http = require('http'); //创建服务器 const server = http.createServer(); //绑定监听事件 server.on('request',function (req,res) { res.writeHeader(200,{ 'Content-Type':'text/plain;charset=utf-8' }) res.end('你好'); }) //启动服务器 server.listen(3000,'127.0.0.1',function () { console.log('server running at http://127.0.0.1:3000'); })
- 创建服务器:使用
防止响应内容中文乱码问题
通过 设置响应报文头的
Content-Type
,来指定响应内容的编码类型,从而防止乱码:res.writeHeader(200, { 'Content-Type': 'text/plain; charset=utf-8' //浏览器默认编码为gbk,此处设置text/plain表示普通文本,charset=utf-8表示以utf-8的形式进行编码 }) 需要在res.end()之前来设置
根据不同的URL返回不同的文本内容
- 使用
req.url
获取客户端请求的URL地址
- 使用
根据不同的URL返回不同的HTML页面
- 主要思路:使用
fs 模块
读取URL对应的HTML页面内容,并使用res.end()
响应给客户端即可;
text/plain 表示普通文本字符串 text/html 表示以html标签的形式解析文本
- 主要思路:使用
处理并返回css样式表
处理并返回Javascript文件
优化
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer();
server.on('request',(req,res)=>{
// res.writeHeader(200,{
// 'Content-Type':'text/html;charset=utf-8'
// })在html页面中已经设置了编码格式
let url = req.url;
if(url==='/') url = '/view/index.html';
fs.readFile(path.join(__dirname,url),(err,buf)=>{
if(err) return res.end('404,not found');
res.end(buf);
//buf表示二进制,能用二进制就不用string,因为在传递的数据时始终以二进制形式传递,用buf少了一层转化
})
})
server.listen(3000,'127.0.0.1',function () {
console.log('server running at http://127.0.0.1:3000');
})
9.3 在 Node 中 结合模板引擎 实现动态资源服务器
<h2>这是一个动态渲染的页面 </h2>
<p>name:{{name}}</p>
<p>gender:{{gender}}</p>
<p>
{{each hobby}}
{{$value}}
{{/each}}
</p>
const http = require('http');
const template = require('art-template');
const path = require('path');
const server = http.createServer();
server.on('request',(req,res)=>{
let url = req.url;
if(url==='/'){
const htmlStr = template(path.join(__dirname,'/view/1.html'),{name:'zs',gender:'女',hobby:['唱歌','跳舞']});
res.end(htmlStr);
}
})
server.listen(3000,'127.0.0.1',()=>{
console.log('server running at http://127.0.0.1:3000');
})
10. 作业
- 练习自定义
calc
计算器包,并尝试向calc
包中,添加 mod 求余数的方法 - 自己使用Node写一个
类似于Apache的静态资源服务器
相关文章
- js模块化编程之彻底弄懂CommonJS和AMD/CMD!
- 把模块化彻底搞个明白(ES6模块/CommonJS/AMD/CMD)
- wiki 对于 Modules的描述
- wiki 对于 Packages的描述
- nodejs模块与包
- [js的单线程和异步](http://www.cnblogs.com
Node.js - day3
0. 今天主要学习目标
- 掌握nodemon工具的使用
- 掌握express框架的基本用法
- 了解express框架提供的中间件的概念
- 能够连接并操作数据库
- 了解模块加载机制
1. 使用 nodemon
工具来自动重启web服务器
- nodemon的作用:能够实时监听当前项目中,文件的变化;只要监听到了文件的变化,则 nodemon 工具,会自动重新启动 web 服务器,从而使最新的代码生效;免去了程序员手动重启服务器的困扰;
- 如何安装:运行
npm i nodemon -g
全局安装即可; - 如何使用:
- 之前使用
node 要执行的文件路径
来运行 Node.js 代码; - 现在使用
nodemon 要执行的文件路径
来运行 Node.js 代码;
- 之前使用
- 注意:今后在开发Web项目的时候,推荐使用
nodemon
来启动 web 服务器
2. Node 中开发web项目的框架 - express
定义(什么是Express):一个快速的网站开发框架,封装了原生的http模块,用起来更方便;API更人性化
2.1 express 框架的特点
- 基于Node.js平台之上,进一步封装了
http
模块,从而提供了更好用,更友好的 API - 使用Express创建网站,比使用原生的http模块更加方便;
- Express 并没有覆盖 原生 http 模块中的方法,而是基于 原生方法之上,做了更友好的封装,让用户体验更好
2.2 express 框架的安装和基本使用
- 安装:运行
npm i express -S
即可安装 - 创建基本的
express
服务器:
- 导入
express
第三方模块; - 创建服务器的实例:调用
const app = express()
方法; - 通过
app.get()
或app.post()
方法,来监听客户端的get
或post
请求,具体语法:
- 监听
GET
请求:app.get('请求地址', (req, res) => { 处理函数 })
- 监听
POST
请求:app.post('请求地址', (req, res) => { 处理函数 })
- 监听
- 启动 express 服务器:通过
app.listen(端口, IP地址, 启动成功后的回调函数)
启动服务器;
- 导入
//导入express模块
const express = require('express');
//创建服务器
const app = express();
//监听路径
app.get('/',(req,res)=>{
res.send('ok你好吗');
})
//开启服务器
app.listen(3000,'127.0.0.1',()=>{
console.log('server running at http://127.0.0.1:3000');
})
//说明:express主要封装了http模块,使得创建服务器更加便捷
2.3 express 中的快捷方法
res.send()
- 支持 发送 字符串
Content-Type: text/html;
- 支持 发送 对象 或 数组
Content-Type: application/json
- 支持 发送 Buffer 此时会当作文件下载;
- 支持 发送 字符串
res.sendFile()
- 用法1:
res.sendFile(path.join(__dirname, './view/index.html'))
- 用法2:
res.sendFile('./view/movie.html', { root: __dirname })
- 注意:
res.sendFile()
可以向浏览器发送 静态页面;
- 用法1:
2.4 使用 express.static()
快速托管静态资源
如果我们网站中,有很多静态资源需要被外界访问,此时,使用 res.sendFile 就有点力不从心了;
这时候,express 框架,为我们提供了
express.static('静态资源目录')
来快速托管指定目录下的所有静态资源文件;
语法1:
app.use(express.static('public'));
app.use()
方法,是专门用来注册 中间件;express.static
是express的内置中间件;
const express = require('express'); const app = express(); //指定目录下的所有文件都可以直接被浏览器访问 app.use(express.static('./view')); app.listen(3000,'127.0.0.1',()=>{ console.log('http://127.0.0.1:3000') })
语法2:
app.use('/虚拟目录', express.static('public'))
2.5 为 express 框架配置模板引擎渲染动态页面
- 安装 ejs 模板引擎
npm i ejs -S
- 使用 app.set() 配置默认的模板引擎
app.set('view engine', 'ejs')
- 使用 app.set() 配置默认模板页面的存放路径
app.set('views', './views')
- 使用 res.render() 来渲染模板页面
res.render('index.ejs', { 要渲染的数据对象 })
,注意,模板页面的 后缀名,可以省略不写!
注:用ejs渲染的页面后缀名只能叫.ejs
const express = require('express')
const app = express()
//1.使用app.set()配置默认的模板引擎
app.set('view engine','ejs')
//2.配置模板页面的存放路径
app.set('views','./ejs-page')
app.get('/',(req,res)=>{
//3.使用render函数渲染页面
res.render('index.ejs',{name:'zs',gender:'女',hobby:['唱歌,跳舞,打游戏']})
})
app.listen(3000,'127.0.0.1',()=>{
console.log('http://127.0.0.1:3000')
})
<body>
<h1>这是用ejs渲染的页面</h1>
<p><%= name%></p>
<p><%= gender%></p>
<%hobby.forEach(item=>{%>
<%= item%>
<%})%>
</body>
2.6 在 express 中配置 art-template
- 安装 两个包
cnpm i art-template express-art-template -S
- 自定义一个模板引擎
app.engine('自定义模板引擎的名称', 渲染函数)
注意:此处模板引擎的名字一定是要渲染页面 的后缀名
- 将自定义的模板引擎,配置为 express 的默认模板引擎
app.set('view engine', '具体模板引擎的名称')
- 配置 模板页面得存放路径
app.set('views', '路径')
<body>
<h1>这是使用 art-template 渲染的模板页面</h1>
<p>姓名:{{name}}</p>
<p>年龄:{{age}}</p>
{{each hobby}}
<p>{{$index}} -- {{$value}}</p>
{{/each}}
</body>
const express = require('express')
const app = express()
// 1. 使用 app.engine() 方法自定义模板引擎
app.engine('html', require('express-art-template'))
// 2. 使用 app.set('view engine', '指定模板引擎名称') 来配置项目中用到的模板引擎
app.set('view engine', 'html')
// 3. 配置模板页面的存放路径
app.set('views', './art_page')
app.get('/', (req, res) => {
res.render('index.html', { name: 'zs', age: 22, hobby: ['玩游戏', '唱歌'] })
})
app.listen(3000, () => {
console.log('server running at http://127.0.0.1:3000')
})
3. 使用 express 框架中提供的路由来分发请求
- 什么是路由:路由就是对应关系;
- 什么叫做后端路由:前端请求的URL地址,都要对应一个后端的处理函数,那么 这种URL地址到 处理函数之间的对应关系,就叫做后端路由;
- 在Express中,路由的主要职责 就是 把请求分发到对应的处理函数中;
const express = require('express')
const app = express()
//引入路由模块
const router = require('./router.js')
//使用app.use()注册路由
app.use(router)
app.listen(3000,'127.0.0.1',()=>{
console.log('http://127.0.0.1:3000')
})
//这里是路由模块
const express = require('express')
//通过express.Router()得到一个路由对象
const router = express.Router();
//将路由规则挂载到router对象上
router.get('/',(req,res)=>{
res.sendFile('./view/index.html',{root:__dirname})
})
router.get('/about',(req,res)=>{
res.sendFile('./view/about.html',{root:__dirname})
})
//将路由对象暴露出去供外界使用
module.exports = router
- 在Express中,如何 定义并使用路由呢?
// 1. 封装单独的 router.js 路由模块文件
const express = require('express')
// 创建路由对象
const router = express.Router()
router.get('/', (req, res)=>{})
router.get('/movie', (req, res)=>{})
router.get('/about', (req, res)=>{})
// 导出路由对象
module.exports = router
- express 创建的 app 服务器,如何使用 路由模块呢?
// 导入自己的路由模块
const router = require('./router.js')
// 使用 app.use() 来注册路由
app.use(router)
4. Express 框架里 中间件的概念
4.1 什么是中间件
在应用程序开发中每一个处理环节都是一个中间件,中间件之间要共享数据,中间件之间要有先后的调用顺序,数据如果想要从上一个中间件流转到下一个中间件,必须调用相关的方法才可以
定义:中间件就是一个处理函数;只不过这个函数比较特殊,包含了三个参数,分别是
req
,res
,next
中间件中间共享的是req和res,调用下一个中间件时用的是next()
注意:中间件方法中的三个参数:
- req:请求对象;
- res:响应对象;
- next:next()可以被调用,表示调用下一个中间件方法;
4.2 Express 框架中对中间件的5种分类
- 应用级别的中间件: 挂载到 app 上的中间件
app.get('URL地址', (req, res, next)=> {})
;绑定到app对象上的 - 路由级别的中间件: 挂载到 router 对象上的中间件
router.get('url地址', (req, res, next)=>{})
绑定到router上的 - 错误级别的中间件: 回调函数中,有四个参数
app.use((err, req, res, next)=>{})
- 唯一内置的中间件:
express.static()
- 第三方中间件: 非express框架提供的,需要程序员手动安装才能使用的中间件;
body-parser
解析post 表单数据
中间件的概念,了解即可,因为实际开发中,我们都直接使用第三方现成的中间件;
4.3 自己模拟一个解析表单数据的中间件
// 导入 express 模块
const express = require('express')
const querystring = require('querystring')
// 创建 express 的服务器实例
const app = express()
// 定义 应用级别的中间件
app.use((req, res, next) => {
let dataStr = ''
// 只要客户端向服务器提交了表单,都会触发 req 的 data 事件
// 在 data 事件中,可以获取到客户端每次提交过来的,不完整的数据,每次发过来的数据都是chunk
req.on('data', chunk => {
dataStr += chunk
})
// 只要 req 触发了 end 事件,就表示表单数据,提交完毕了,dataStr 中存储的数据,就是完整的表单数据
req.on('end', () => {
console.log(dataStr)
const obj = querystring.parse(dataStr)
// querystring.parse可以把按照&,=分割的字符串解析成对象
//想要把 username=ls&password=123 字符串,解析为 { username: 'ls', password: 123 }
console.log(obj)
req.body = obj//把从客户端解析的数据挂载到req上,req,res在各个中间件之间共享
// 进入下一个中间件的处理环节;
// 注意:在中间件中,最后,一定要合理的调用一下 next() 方法,否则,服务器 无法结束这次响应!
next()
})
})
// 这是应用级别的中间件
app.get('/', (req, res) => {
res.sendFile('./11.index.html', { root: __dirname })
})
app.post('/postdata', (req, res) => {
console.log(req.body)
// 需求:如何从客户端提交的表单中,获取到 客户端提交过来的数据呢?
res.send(req.body)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(3001, function() {
console.log('Express server running at http://127.0.0.1:3001')
})
<body>
<h3>演示中间件 表单 Post 提交数据</h3>
<form action="/postdata" method="POST">
<p>用户名:
<input type="text" name="username">
</p>
<p>密码:
<input type="password" name="password">
</p>
<input type="submit" value="提交">
</form>
</body>
5. Express 中进行数据库操作
配置 MySql 数据库环境
mysql 第三方模块的介绍和基本配置
使用 mysql 第三方模块实现 CRUD
const express = require('express')
const app = express()
const mysql = require('mysql')
var con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: '0907'
});
con.connect();
app.get('/',(req,res)=>{
//--------------新增------------
const data = {username:'修改前',password:'18814072922'}
//此数据库插入数据的语法仅在node中适用,其他语言中语法不同
//?表示要传的参数,如果有两个参数,传递时用数组
con.query('insert into user set ?',data,(err,result)=>{
if(err) return console.log('数据插入失败'+err.message)
console.log('数据插入成功')
})
//--------------修改------------
const newData = {username:'修改后',password:'18834072922'}
con.query('update user set ? where username=?',[newData,'修改前'],(err,result)=>{
if(err) return console.log('修改数据失败'+err.message)
console.log('数据修改成功')
})
//--------------查询-------------
con.query('SELECT * from user', function (error, result) {
if(error) return console.log('获取数据失败'+error.message)
res.send(result)
});
//--------------删除-------------
con.query('delete from user where username=?','修改后',(err,result)=>{
if(err) return console.log('删除失败'+err.message)
console.log('数据删除成功')
})
})
app.listen(3000,'127.0.0.1',()=>{
console.log('http://127.0.0.1:3000')
})
6. 模块加载机制
优先从缓存中加载
- 当一个模块初次被
require
的时候,会执行模块中的代码,当第二次加载相同模块的时候,会优先从缓存中查找,看有没有这样的一个模块! - 好处:提高模块的加载速度;不需要每次都重新执行并加载模块!
核心模块的加载机制
- 先查找缓存;如果缓存中没有,再去加载核心模块;
用户模块的加载机制
先查找缓存;
如果缓存中没有则尝试加载用户模块;
如果在加载用户模块时候省略了后缀名,则:
首先,严格按照指定的名称去查找 其次,尝试加载后缀名是 .js 的文件 如果没有.js的文件,则尝试加载 .json 结尾的文件 如果没有 .json 的文件,则尝试加载 .node 结尾的文件 查找规则:index -> index.js -> index.json -> index.node
第三方模块的加载机制【了解】
- 先在项目根目录中查找
node_modules
文件夹 - 在
node_modules
文件夹下,查找模块相关的文件夹 - 在对应的文件夹下,查找
package.json
的文件 - 查找
package.json
文件中的main
属性(指定了模块的入口文件) - 如果找到了
main
属性,同时,main
属性指定的文件路径存在,那么尝试加载指定的文件模块 - 加入没有
main
属性,或者main
属性对应的文件不存在,或者没有package.json
,那么会依次尝试加载index.js
,index.json
,index.node
; - 如果没有
index
相关的文件,或者没有指定模块对应文件夹,或者,当前项目根目录中没有node_modules
文件夹,则向上一层目录中查找node_modules
,查找规则同上! - 最后,如果在项目所在磁盘的盘符根目录中,还找不到对应模块,则报错:
cannot find module ***
7. 作业
- 自己使用
express.static
把 今天资料
文件夹下,vue-cms
项目,托管成静态资源服务器; - 自己在
express
中,配置和使用ejs/art-template
来渲染一个动态页面; - 创建三个静态页面
index.html
,movie.html
,about.html
;分别使用三种方式,显示这三个静态页面;
- 直接 把 路由 挂载到
app
上; - 把 路由 抽离为 单独的路由模块,然后把路由,挂载到
router
上; - 从
router.js
模块中,再抽离出 一个handler.js
模块;
- 直接 把 路由 挂载到
相关文章
Node.js - day4
今天主要内容
- 获取参数的几种形式
- 了解web开发模式
- 设计数据库和后台接口
- 实现前端功能
1. express中获取参数的几种形式
- 获取
http://127.0.0.1:3001/user?id=10&name=zs
中的查询参数:
- 直接使用
req.query
获取参数即可; - 注意:URL 地址栏中通过
查询字符串
传递的参数,express 框架会直接解析,大家只需要使用req.query
直接获取 URL 中 查询字符串的参数;
- 直接使用
- 从URL地址中获取路径参数:
- 假设后台的路由是
app.get('/user/:id/:name', (req, res) => {})
(此处冒号表示参数项) - 假设客户端浏览器请求的URL地址为:
http://127.0.0.1:3001/user/10/zs
- 直接使用
req.params
可以获取URL地址中传递过来的参数;
- 假设后台的路由是
- 从post表单中获取提交的数据:
- 借助于
body-parser
来解析表单数据 - 安装:
npm i body-parser -S
- 导入:
const bodyParser = require('body-parser')
- 注册中间件:
app.use(bodyParser.urlencoded({ extended: false }))
- 使用解析的数据:
req.body
来访问解析出来的数据
- 借助于
2. Web 开发模式
2.1 混合模式(传统开发模式)
- 以后端程序员为主,基本上不需要前端程序员,或者,前端程序员只负责画页面、美化样式、写JS特效,前端程序员不需要进行数据的交互;
- 这种开发模式,在早些年比较常见;
- 传统开发模式下,用的最多的是 Jquery + 模板引擎 + Bootstrap
- 后端页面 .php .jsp .aspx .cshtml
2.2 前后端分离(趋势)
- 后端负责操作数据库、给前端暴露接口
- 前后端分离的好处:保证了各个岗位的职责单一;
- 前端负责调用接口,渲染页面、前端就可以使用一些流行的前端框架 Vue, React, Angular
3. 需求分析
- 后端项目运行地址:http://127.0.0.1:5000
- 前端项目运行地址:http://127.0.0.1:3000
前后端分离开发模式的注意点:
- 跨域问题
- 解决跨域问题,一般使用jsonp,但是jsonp只支持get请求,不支持post(表单提交常用)请求
- 如果不考虑 表单的 Post 提交,则 可以使用 JSONP 的形式来请求接口
- 但是,我们的项目中,涉及到了 英雄表单的 提交,表单提交一般都是Post
- 经过分析,由于JSONP,不支持Post,所以,我们的后端接口,无法设计成JSONP的接口;
- 前端项目 Jquery + 模板引擎 + Semantic UI
4. JSONP 和 CORS 的区别
JSONP的原理:动态创建script标签;
- JSONP发送的不是Ajax请求
- 不支持 Post 请求;
- jsonp的原理是插入script标签,它只支持get请求
CORS中文意思是
跨域资源共享
,需要服务器端进行CORS
配置;- CORS 发送的是真正的Ajax请求
- CORS 支持Ajax的跨域
- 如果要启用 CORS 跨域资源共享,关键在于 服务器端,只要 服务器支持CORS跨域资源共享,则 浏览器肯定能够正常访问 这种 CORS 接口;而且,客户端在 发送 Ajax的时候,就像发送普通AJax一样,没有任何代码上的变化;
对于Node来说,如果想要开启 CORS 跨域通信,只需要安装
cors
的模块即可;
5. 数据库设计 - heros
字段名 | 字段类型 | 字段描述 |
---|---|---|
id | int | 主键Id(自增) |
name | varchar | 英雄名称 |
gender | varchar | 性别 |
ctime | varchar | 创建时间 |
isdel | tinyint(布尔值) | 是否被删除 0 表示未删除;1 表示已经被删除 |
在 mysql 中的 tinyint 等同于 bool 值
6. 后台接口设计
获取所有英雄列表
- 请求类型:GET
- 请求地址:
http://127.0.0.1:5001/getallhero
- 请求的参数:无
插入新的英雄数据
- 请求类型:POST
- 请求地址:
http://127.0.0.1:5001/addhero
- 请求参数:{ name, gender }
根据Id获取英雄信息
请求类型:GET
请求地址:http://127.0.0.1:5001/gethero/:id
请求参数:通过 URL 地址,把要查询的英雄Id,携带过去
根据Id更新英雄数据
请求类型: POST
请求地址:http://127.0.0.1:5001/updatehero/:id
请求参数:{ name, gender }
根据Id软删除英雄数据
请求类型:GET
请求地址:http://127.0.0.1:5001/deletehero/:id
请求参数:通过 URL 地址栏传参,把 要删除的英雄Id提交给服务器
7. 后台接口项目实现
8. 前端功能实现
相关文章
Node.js(黑马博客案例)
今天主要内容
- 博客项目开发
- 能够看懂和绘制流程图
- Cookie 和 Session 知识点的复习和使用
- 登录、注销功能
1. 分析
- 昨天的英雄案例使用{前后端分离}开发的(先定义接口文档,根据接口文档编写后端API, 前端写页面同时根据文档调用接口)
- 今天的黑马博客,使用{传统的开发方式},我们切换到后端开发人员的身份,一边定义接口,一边写页面调用接口;
2. 搭建博客案例基本结构并渲染首页
主要目标1:在渲染首页的时候,把相关的配置搭建起来(模板引擎、静态文件托管)
3. 用户注册模块的实现
- 要求大家,能够根据自己对于业务逻辑的了解,能够画出业务流程图
- 圆角矩形或圆圈: 表示开始/结束
- 矩形: 表示一个处理过程
- 菱形: 表示判断
- 箭头:表示流转关系
4. 用户登录模块的实现
5. HTTP协议的无状态性
- HTTP协议的通信模型:基于
请求 - 处理 - 响应
的! - 由于这个通信协议的关系,导致了HTTP每个请求之间都是没有关联的,每当一个请求完成之后,服务器就忘记之前谁曾经请求过!
- 如果纯粹基于HTTP通信模型,是无法完成登录状态保持的!每次请求服务器,服务器都会把这个请求当作新请求来处理!
- 我们可以通过 cookie 技术,实现状态保持,但是由于cookie是存储在客户端的一门技术,所以安全性几乎没有,因此不要使用cookie存储敏感的数据!
6. cookie介绍
6.1 什么是cookie,作用是什么
- 由于Http协议是无状态的,且传统服务器只能被动的响应请求,所以,当服务器获取到请求的时候,并不知道当前请求属于哪个客户端!
- 服务器为了能够明确区分每个客户端,需要使用一些小技术,来根据不同的请求区分不同的客户端;
- 只要有请求发生,那么必然对应一个客户端,我们可以在每次客户端发起请求的时候,向服务器自动发送一个标识符,告诉服务器当前是哪个客户端正在请求服务器的数据;
- 如何提供这个标识符呢?我们可以在请求头(Request Headers)中添加一个标签,叫做
cookie
,这样,每次发送请求,都会把这个cookie随同其他报文一起发送给服务器,服务器可以根据报文中的cookie,区分不同的客户端浏览器。 如何在客户端请求头中添加标识符?
在Node中可以在
writeHeader
的时候,通过Set-Cookie
来将cookie标识通过响应报文发送给客户端!- 客户端也可以通过一些方式来操作自己的cookie,比如通过
jquery.cookie
这个插件!
6.2 cookie的基本使用
var http = require('http');
var server = http.createServer();
server.on('request', function (req, res) {
// 解析cookie
var cookies = {};
var cookieStr = req.headers.cookie; // 从请求的headers中获取cookie信息
cookieStr && cookieStr.split(';').forEach(function (item) {
var parts = item.split('=');
cookies[parts[0].trim()] = parts[1].trim(); // 将cookie解析出来,保存到对象中
});
res.writeHeader(200, {
'Content-Type': 'text/plain; charset=utf-8',
"Set-Cookie": ['issend=ok', 'age=20']
});
if(cookies.issend ==='ok'){
res.end('不要太贪心哦!');
}else{
res.end('呐,赏你一朵小红花~~');
}
});
server.listen(4000, function () {
console.log('服务器已启动!');
});
6.3 通过expires
设置Cookie的过期时间
// 设置 过期时间 为60秒之后
// 注意:在设置过期时间的时候,需要将时间转换为 UTC 格式
var expiresTime = new Date(Date.now() + 1000 * 60).toUTCString();
res.writeHeader(200, {
'Content-Type': 'text/html; charset=utf-8',
'Set-Cookie': ['isvisit=true;expires=' + expiresTime, 'test=OK']
});
res.end('<h3>你好,欢迎光临,送给你一个苹果!</h3>');
GMT和UTC有什么区别?格林尼治标准时(GMT)与世界时(UTC)是怎么回事
6.4 cookie可以被伪造,不安全
使用谷歌插件edit this cookie
,就能伪造cookie数据!所以不要使用cookie存储敏感的数据!比如登录状态和登录信息;
一些敏感的数据,应该存储都服务器端!
6.5 什么是Cookie的应用场景
- 对安全性要求不高
- 不需要存储大量的数据
- 主要应用场景,是用来做 客户端 与 服务器之间的 状态保持技术;
7. 登录退出及状态保存
7.1 使用express-session
来保存登录状态
7.1.1 什么是session
由于HTTP是无状态的,所以服务器在每次连接中持续保存客户端的私有数据,此时需要结合cookie技术,通过session会话机制,在服务器端保存每个HTTP请求的私有数据;
7.1.2 session原理
在服务器内存中开辟一块地址空间,专门存放每个客户端私有的数据,每个客户端根据cookie中保存的私有sessionId,可以获取到独属于自己的session数据。
7.1.3 在express中使用session
使用流程:先在app.js安装注册express-session,然后在服务器端能访问到req对象的对应的函数时为session挂载属性(user,islogin),然后在contoll中的index.js渲染首页的时候把挂载的属性传递过来,同时在index.ejs中用ejs模板语法控制按钮组的现实
- 安装session模块
npm install express-session -S
- 导入session模块
var session = require('express-session')
- 在express中使用
session
中间件:
// 启用 session 中间件,只要注册了中间件。之后凡是能访问到req,必然就能访问到req,session
app.use(session({
secret: 'keyboard cat', // 相当于是一个加密密钥,值可以是任意字符串
resave: false, // 强制session保存到session store中
saveUninitialized: false // 强制没有“初始化”的session保存到storage中
}))
- 将私有数据保存到当前请求的session会话中:
// 将登录的用户保存到session中
req.session.user = result.dataValues;
// 设置是否登录为true
req.session.islogin = true;
- 通过
destroy()
方法清空session
数据:
req.session.destroy(function(err){
if(err) throw err;
console.log('用户退出成功!');
// 实现服务器端的跳转,这个对比于 客户端跳转
res.redirect('/');
});
8. 注销功能的实现
req.session.destroy(()=>{
//使用redirect方法让客户端重新访问首页
res.redirect('/')
})
作业:
- 自己晚自习尝试着 把 用户注册 和 用户登录 的流程图画出来
- 大家自己能够搭建一个 类似于黑马博客的Web服务器,然后,把 express-session 尝试着配置好使用一下;
相关文章
Node.js - day6(黑马博客案例)
MD5 的特性
- MD5 是一种加密算法,在调用这个算法的时候,提供一个密码的明文, 调用的结果,得到一个 32 位长度的密文;
- MD5 算法的特性:相同的字符串,如果多次调用 md5 算法,得到的结果,完全一样;
- MD5 算法,无法被逆向解密;
- 但是,基于 md5 算法的第二个特性,我们可以进行碰撞暴力破解;(MD5 存在被暴力破解的安全性问题)
- 为了解决 简单的明文密码,被 md5 加密后,通过 暴力破解的安全性问题, 然后就出现了加盐的MD5加密;
- 目前,md5的暴力破解,又升级了,升级到了
彩虹表
; - 由于彩虹表出现,我们推荐大家,在存储网站密码的时候,使用
bcrypt
加密算法,得到加密之后的密文进行存储;
bcrypt 加密算法
- 在调用加密算法的时候,需要手动提供一个
幂次
; - 调用加密算法,得到的加密结果格式:
$版本号$循环的幂次$22位的随机盐 31位的密文
- 加密的
随机盐
和加密的幂次
,和加密算法的版本号
已经被存储到了真正的密文中;
- 加密的
项目中使用 bcrypt 的步骤
运行
npm i node-pre-gyp -g
在项目根目录中,打开终端,运行
cnpm install bcrypt -S
导入
bcrypt
// 导入加密的模块 const bcrypt = require('bcrypt')
定义幂次:
// 定义一个 幂次 const saltRounds = 10 // 2^10
调用
bcrypt.hash()
加密:// 加密的方法 bcrypt.hash('需要加密的密码', saltRounds, (err, pwdCryped) => { console.log(pwdCryped) 要在此方法中写数据库的操作,否则不起作用 })
调用
bcrypt.compare()
对比密码是否正确:// 对比 密码的方法 bcrypt.compare('123', '$2b$10$i1ufUKnC9fXTsF9oqqvLMeDnpNfYIvhyqKRG03adiebNFPkjW3HPW', function(err, res) { console.log(err) // 内部对比的过程: // 1. 先获取 输入的明文 // 2. 获取输入的密文 // 2.1 从密文中,解析出来 bcrypt 算法的 版本号 // 2.2 从密文中,解析出来 幂次 // 2.3 从密文中,解析出来前 22 位 这个随机盐 // 3. compare 方法内部,调用 类似于 hash 方法 把 明文,幂次,随机盐 都传递进去 最终得到正向加密后的密文 // 4. 根据最新得到的密文,和 compare 提供的密文进行对比,如果相等,则 返回 true ,否则返回 false; })
使用模板引擎处理公共部分
在PHP中,抽取公共的区域,直接使用PHP语法就行;
但是,在Express的框架中,并没有抽取页面公共部分的语法,需要模板引擎提供这样的语法;
添加文章并跳转到文章详情
- 发表文章之前,需要使用 第三方的插件,叫做
markdown + editor
=>mditor
注意:
mditor
这个第三方模块,提供了两个功能:- 功能1: 可以当作一个纯粹的MarkDown编辑器插件,在前端页面中使用;
- 功能2: 在Node端,我们可以
require('mditor')
,使用这个模块,提供的方法,把markdown
文本,解析转换为HTML
内容;
设计文章表的字段
完成文章编辑功能
首页文章列表渲染
把markdown文本转化成html
1.安装marked
2.导入
3。调用marked marked(要转化的文本)