HTTP请求报文
一个HTTP请求报文有 请求行、请求头、空行和请求数据 这几部分组成
请求行 有请求方法、url和http协议版本字段 3个组成
HTTP协议请求方法有 get、post、head、put、delete、options、trace、connect
请求头 由 关键字/值对组成,每行一对, 关键字和值用英文冒号":"分隔, 请求头部通知服务器有关于客户端请求的信息
空行 最后一个请求头之后是一个空行, 发送回车符和换行符, 通知服务器一下不在有请求头
请求数据不在 get 方法中使用, 而是在 post 方法中使用, post 方法适用于需要客户填写表单的场合, 与请求数据相关的最常使用的请求头是 Content-Type 和 Content-Length
响应报文
http 响应也由三部分组成, 分别是: 状态行, 消息报头,响应正文.
状态行: HTTP/1.1(响应采用的协议和版本号) 200(状态码) OK (描述信息)
消息报头: 有 关键字/值对组成, 每行一对, 关键字和值 用英文的冒号 ' : ' 分隔
响应正文: HTML页面
node 文件路径 手动运行
nodemon 文件路径 自动运行
location.href=’要跳转的网页’ 跳转到 ‘其他网页’
使用npm init -y 初始化包管理配置文件 (就是创建一个package.json)
运行 npm i express -S 安装node框架
module.exports 向外暴露私有成员
//创建服务器
const express = require('express');
const app = express();
__dirname 代表当前文件所在目录
//启动服务器
App.listen(端口号,()=>{});
//创建服务器
const express =require('express');
// 调用服务器
const app = express();
// 启动服务器
app.listen(5001,()=>{
console.log('http://127.0.0.1:5001')
});
cls 清空控制(终端)台
语言和环境(平台)之间的关系
- 语言,是编写代码的语法规范。程序员遵循特定的语法规范,编写出来的代码,只有单纯的文本字符串,并不具备可执行特点
- 环境(平台),提供了执行代码的能力,如果程序员编写的代码想要成功执行,必须要依赖于特定的执行环境
例如:JavaScript代码可以被浏览器中的JS解析引擎执行
所以,浏览器,就是一个JavaScript的执行环境;因为JavaScript代码可以在浏览器中被执行
前端与后端
- 前端主要工作
(1) 页面结构
(2) 美化页面样式
(3) 书写页面的业务逻辑
(4) 使用AJAX调用后台接口
- 后端主要工作
(1) 操作数据库
(2) 对外暴露操作数据库的API接口
- 前后端协作开发
什么是nodejs?
就是JavaScript的服务器端运行环境,可以让程序员使用JavaScript来实现服务器端的编程
node.js中Javascript的组成部分
① ECMAScript核心 + 全局成员 + 核心API模块
1) 全局成员:console、setInterval、setTimeout..........
2) 核心API模块:就是node平台单独提供的一些API,这些API是node平台所独有的
注意:node.js 中没有 BOM 和 DOM
由于node服务器端运行环境中,没有浏览器和HTML 的概念,所以,node中的JavaScript提出了 BOM和DOM 这两个对象模型,取而代之,是全局成员 和 核心API模块
ECMAScript规范,浏览器中的JS ,node 中的JS 之间的关系
- ECMAScript规范(标准):就是一本书,这本书中规定了语言的特性,ES6规范、ES7规范
- 浏览器中的JS
① 浏览器中的JS组成部分:ECMAScript核心 + DOM + BOM
- Node中的JS
① node中的JS组成部分:ECMAScript核心 + 全局成员 + 核心API成员
1) ECMAScript核心 →指的就是ES6语法
学习node.js 可以做什么
node.js就是一个服务器的JavaScript编程符合规范的后端API 接口 或者 带后台功能的网站
使用node.js 开发一些使用的工具 或者 包
i5ting_toc 工具的作用,就是把 md文档,转换为HTML 网页(i5ting_toc -f 文件 【-f代表要转换的】)
运行 npm i i5ting_toc -g 全局安装这个工具
运行 i5ting_toc -f 文件名 进行文件的转换(转换成HTML页面)
基于 socket技术,开发类似于聊天室之类的即时通讯项目
基于electron环境 开发桌面软件
俩个安装包的区别
LTS:是长期稳定版的安装包,运行稳定,安全【相对稳定】
Current:是最小特性版,这个安装包中有最新的node特性【学习和尝试使用 不太稳定】
什么是path环境
path环境变量的作用:能够让我们在终端中执行相关命令,从而快速启动应用程序
let(变量) 与const(常量)
之前定义变量,有var 关键字:有如下主要缺点:
存在变量提升问题,降低JS代码的可阅读性
没有 {} 块级作用域,容易造成变量污染
变量可以被多次重复定义
let主要特性:
不存在变量提升问题,只有定义之后才能使用此变量
有 { } 作用域,而且,在同一作用域中,无法被重复定义,但是可以重新赋值
const(常量)主要特性:
不存在变量提升问题,只有定义之后才能使用此变量
const 定义的常量 无法被重新赋值
当定义常量的时候,必须定义且初始化,否则报语法错误
const定义的常量,也有 块级作用域
可以直接通过 = 为常量对象中的属性赋值,但是无法直接使用 = 为常量重新赋值
变量的解构赋值
定义:所谓的解构赋值,就是把某个对象中的属性,当做变量,给解放出来,这样,今后就能够当做变量直接使用
可以使用 : 为解构出来的变量重命名
例如:
// 变量的解构赋值
const { name : name123, age, gender } = person
console.log(name123)
箭头函数
把方法定义为箭头函数
(形参列表)=>{函数体代码}
箭头函数,本质上就是一个匿名函数
箭头函数的特性:箭头函数内部的this 永远和箭头函数外部的this保持一致
变体:
(1) 变体1:如果箭头函数,左侧的形参列表中,只有一个参数,则 左侧的小括号可以省略
(2) 变体2:如果右侧函数体中,只有一行代码, 则 右侧的{ } 可以省略
(3) 变体3:如果箭头函数左侧只有一个形参,且右侧只有一行代码,则两边的() 和{ } 都可以省略
注意:如果我们省略了右侧函数体的{ } ,那么,默认会把右侧函数体中的代码的执行结果,当做箭头函数的调用结果 直接return 出去, 所以不用自己手动 [return 会报错]
对象中的定义方法和定义属性
fs模块提供了一些API,用于以一种类似标准POSIX函数的方法与文件系统进行交互
使用require方法 导入fs模块
fs文件夹系统模块的作用就是来操作文件的
const fs = require('fs');
所有的文件系统操作都有异步与同步两种形式
异步形式的最后一个参数都是完成时回调函数,传给回调函数的参数取决于具体方法,但回调函数的第一个参数都会给异常,如果操作成功完成,则第一个参数是null或者undefined
err.message //错误[标志]信息,错误报文;(错误原因)
文件读取:fs.readFile()
接收三个参数:
// 参数1:【必选参数】字符串的文件路径;表示要读取哪个文件
// 参数2:【可选参数】字符串类型的编码格式(utf-8),默认为 null
// 参数3:【必选参数】callback回调函数,有两个形参 ①err 错误的结果 ②data 读取到的内容
// 代码:
fs.readFile('文件名','utf-8',(err, data) => {
// 如果读取文件成功,则 err 为 null, data 是成功之后的数据
// 如果读取失败,则 err 为一个错误对象, data 为 undefined
// err 是Error类型的一个对象
// err.message //错误信息,错误报文;(错误原因)
console.log(err) // null
console.log(data) // <Buffer 文件内容>
// Buffer 是二进制的数据,只不过在展示的时候,为方便展示,操作系统帮我们把二进制的Buffer转换为了16进制的格式
})
文件写入:fs.weiteFile()
// 参数1: 要把文件写入到哪个路径中(文件名或者文件描述符)
// 参数2: 要写入的内容
// 参数3: 编码格式
// 参数4:回调函数 只有一个形参err
// 异步的写入数据到文件,如果文件已经存在,则覆盖文件,data可以是字符串或者buffer
// 如果data是一个buffer,则encoding 选项无效
代码:
fs.writeFile('./files/2.txt', '666','utf-8', err => {
if (err) return console.log('写入文件失败!' + err.message)
console.log('写入文件成功!')
})
文件追加:fs.appendFile()
参数1:要往哪个文件追加内容
参数2:要追加的内容
参数3:(可选)要以什么编码格式追加进去 默认utf-8
参数4:回调函数 err(错误信息)
fs.appendFile('01-nodejs/1.txt','\n222','utf-8',err=>{
if (err) return console.log('追加失败:'+err.message);
console.log('追加成功')
})
异步的追加数据到一个文件,如果文件不存在则会创建这个文件,不会报错
fs模块中路径操作问题
使用fs模块操作文件的时候,如果提供的操作路径是 相对路径 ,则会根据当前执行node 命令是的磁盘目录,去拼接提供的文件相对路径,从而容易出现问题
注意:在调用 fs 相关的方法的时候,如果提供的操作路径是相对路径,那么很容易出现路径问题,在操作文件的时候 会直接以当前运行node 命令是,所在的路径来拼接用户提供的相对路径
解决方法:在操作文件的时候,提供相对路径
推荐使用node中提供的__dirname 来解决fs模块操作文件时候的路径问题
__dirname 代表当前文件所在目录
__filename 代表当前文件的文件名(代表当前文件所在目录和文件名)
读取文件信息 fs.stat (不是文件内容)
参数1:要读取那个文件(文件名)
参数2:
参数3:回调函数 有俩个形参 一个是err 错误信息 一个是stats 读取成功的信息对象(有文件大小、文件的创建时间、还有方法......等等可以查文档有详细的说明)
代码:↓
fs.stat(__dirname + '文件夹名',(err,stats)=>{
if(err) return console.log('读取文件失败'+ err.message)
console.log(stats.size) // 读取文件大小(不能直接获取目录大小) 单位是 字节 Byte
console.log(stats.birthtime) // 文件创建时间
......等等
console.log(stats.isFile()) // 方法: 判断是否是一个文件
console.log(stats.isDirectory()) // 判断是否是一个目录
......等等
})
读取指定目录下所有文件的名称 fs.readdir()
参数1:要读取那个路径
参数2:
参数3:回调函数(读取的结果) 有俩个形参 err 错误信息 files [数组] 代表读取到的所有文件的文件名
异步的readdir(3)。读取一个目录的内容,回调有俩个参数(err,files),其中files是目录中不包'.'和'..'的文件名的数组
可选的options参数用于传入回调的文件夹名,他可以是一个字符串并指定一个字符编码,或者一个对象且有一个encoding属性指定使用的字符编码。如果encoding设置'buffer',则返回的文件名会被作为buffer对象传入,注意:'path'的路径是以前文件为基准进行查找的,而不是运行的时候的相对路径
复制文件 fs.copyFile()
参数1:要被拷贝的源文件名称
参数2:拷贝操作的目标文件名
参数3:(可选)拷贝操作修饰符 默认0
参数4:回调函数 一个形参 err 错误对象
异步的将 src 拷贝到 dest。Asynchronously copies src to dest. 默认情况下,如果 dest 已经存在会被覆盖。回调函数没有给出除了异常以外的参数。Node.js 不能保证拷贝操作的原子性。如果目标文件打开后出现错误,Node.js 将尝试删除它。
flags 是一个可选的整数,用于指定行为的拷贝操作。唯一支持的 flag 是 fs.constants.COPYFILE_EXCL ,如果 dest 已经存在,则会导致拷贝操作失败。
路径操作
首先导入模块
path 模块提供了一些工具函数,用于处理文件与目录的路径。可以通过以下方式使用:
const path = require('path')
path.join() 只要涉及到路径片段的拼接一定要使用path.join()方法,尤其是读取文件的时候
可以解决路径问题 只要涉及到查找上一层目录的文件 就必须用path.join()方法 不可以使用 ../ 的方法
path.sep
提供了平台特定的路径片段分隔符
- Windows 上是 \
- POSIX 上是 /
path.basename(path[,ext]) 获取文件名,不包含文件路径
EXT是延伸文件系统(英语:Extended file system,缩写为 ext或 ext1),译为扩展文件系统,一种文件系统,
path.basename('/foo/bar/baz/asdf/quux.html');// 返回: 'quux.html'
path.basename('/foo/bar/baz/asdf/quux.html', '.html');// 返回: 'quux'
path.dirname(path)获取文件所处路径,不包含文件名
path.extname(path) 获取文件扩展名
JavaScript的单线程和异步
JavaScript的解析和执行一直是 单线程的,但是 宿主环境(浏览器或者node)是多线程的
异步任务是有宿主环境开启子线程完成,并通过事件驱动 回调函数 队列,把完成的任务,交给主线程执行
JavaScript借新引擎,一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行
Node中的API为什么几乎都是异步操作
- 什么样的操作需要使用异步处理:要把耗时的操作,放到异步中去执行
- 异步执行任务的好处:能够提高耗时的任务他的执行效率,提高JS解析引擎的工作效率
认识模块化
模块化就是一种约定,一定规范
场景模拟:三个人共同基于node.js开发项目
- 为什么要有模块化:为了解决文件之间的依赖关系
- 注意:模块化是一种开发思想:具体开发中需要定制符合实际需求的模块化规范
- 大家可以把模块化规范,认为是一种文明的约定,大家都按照相同的约定写代码,减少了不必要的沟通成本,极大方便课各个模块之间的调用,方便别人,同时也方便自己
了解commonJS规范
- 作用:是一套JavaScript的模块化规范,规定了模块化的特性和各模块之间如何相互依赖
- 用途:node.js中使用可commonJS规范
- 特点:同步加载模块,不适合在浏览器使用
- CommonJS规范都定义了哪些内容:wiki对于Modules的描述
每个模块中,都应该有exports ,作用:是把当前模块中的成员暴露出去,给别人使用
module就代表当前这个模块
module.require是用来导入其他模块的
在node中,每一个JS文件,大家都可以把他认为是一个模块
CommonJS规范中,每一个模块,都有自己独立的作用域
默认情况下,外界导入当前模块之后,无法直接访问当前模块中的成员
因为 模块中的成员 ,默认都是私有的
在浏览器中,如果定义一个变量,这个变量默认属于全局window作用域
但是,在node如果模块中定义一个变量,默认 不属于全局global作用域
在今后开发中,不要使用global作用域,会存在变量污染问题
模块作用域和全局作用域
在node.js中有两个作用域,分别是全局作用域和模块作用域
- 全局作用域使用global 来访问,类似于 浏览器中的window
- 每个JavaScript文件,都是一个单独模块,每个模块都有自己独立的作用域,因此 模块中的成员,默认无法被其他模块访问
模块作用域
- module(模块标识)
module属性是CommonJS 规范中定义的,他是一个对象,表示当前这个具体的JS 模块
- require(引用模块)
每一个实现了CommonJS规范的模块,必须定义一个require()函数,使用这个require函数,就能够很方便的导入其他模块中的成员供自己使用
- exports(暴露模块成员)
每一个模块中,如果想要把自己的一些私有成员,暴露给别人使用,那么 必须实现一个exports对象,通过exports对象,可以方便的把模块内私有的成员,暴露给外界使用
module.exports和exports的关系
- module.exports和exports默认引用了同一个空对象
- module.exports和exports作用一致,都是可以向外暴露成员
- 一个模块作用域中,向外暴露私有成员时,永远以module.exports为准
浏览器端的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 规范,成为浏览器和服务器通用的模块解决方案;
模块成员的分类
模块成员,根据一些区别又分为三大类:核心模块、第三方模块、用户自定义模块
核心模块
(1) 随着node.js的安装包,一同安装到本地的模块,叫核心模块
(2) 例如:fs,path等模块,都是由node.js官方提供的核心模块
(3) 只要大家在计算机中,安装了node 这个应用程序,那么 我们的计算机中就已经安装可所有的核心模块
(4) 使用核心模块:require('核心模块标识符')
第三方模块
(1) 一些非官方提供的模块,叫做第三方模块
(2) 注意:第三方模块,并不在我们计算机上
(3) 如果大家需要使用某个第三方模块,必须去一个叫npm 的网站上搜索并下载才能使用
使用第三方模块
(1) 先从npm 官网上下载指定的第三方模块
(2) 使用require('第三方模块的名称标识符')来导入这个模块
(3) 根据第三方模块的 官方文档 尝试使用
自定义模块
程序员在自己项目中写的JavaScript文件,就叫做自定义模块
使用自定义模块:require('路径标识符')
规范的包结构
- 包都要以一个单独的目录而存在
- package.json 必须在包的顶层目录下
- pachage.json 文件必须符合JSON格式,并且必须包含三个属性:name version main
(1) name :包的名字
(2) version: 包的版本号
(3) main:表示包的入口文件
- 二进制文件应该在bin目录下
- JavaScript代码应该在lib目录下
- 文档应该在doc目录下
- 单元测试应该在test目录下
- Node.js 对包要求并没有那么严格,只要顶级目录下右package.json,并符合基本规范即可
包描述文件package.json
name:包的名称,必须是唯一
description:包的简要说明
version:符合语义化版本识别规范的版本字符串
keywords:关键字数据,通常用于搜索
maintainers:维护者数组,每个元素要包含name、email、web可选字段
contributors:贡献者数组,格式与maintainers相同。包的坐着应该是贡献者数据的第一个元素
bugs:提交bug的地址,可以是网址或者电子邮件地址
licenses:许可证数组,每个元素要包含type和url字段
repositories:仓库托管地址数组,每个元素要包含type、url和path字段
dependencies:包的依赖,一个关联数组,由包名称和版本号组成。
devDependencies:开发依赖项,表示一个包在开发期间用到的依赖项
npm的两层含义
- NPM是一个第三方模块的托管(官方)网站 指的就是https://www.npmjs.com/
- NPM是node的包管理工具(全名叫做Node Package Manager),在我们安装node的时候,就已经顺便也安装npm这个管理工具
安装和卸载本地包
- 本地包:跟着项目安装的包,叫做本地包:本地包都会被安装到node_modules目录下
- 注意:如果拿到一个空项目,必须在当前项目目录中,先运行 npm init 或者 npm init -y 命令,初始化一个package.josn的配置文件,否则包无法安装到本地项目中
- 安装本地包:运行 npm i 包名 --save 即可安装本地包,都安装到了当前项目的 node_modules目录下。如果用的是npm 5.X 的版本,可以不指定--save命令,如果用的是 npm 3.x 的版本,则需要手动指定--save
- package-lock.json文件中记录了曾经装过的包的下载地址,方便下次直接下载包,能够加快装包的速度,提升装包的体验
- 卸载本地包:使用 npm uninstall/remove 包名 -S/-D 即可卸载指定的本地包
其他常用命令
npm i 包名 -g (全局安装)
- --save 的缩写是 -S
- --save-dev的缩写是-D
- install 的缩写是 i
- 注意:dependencies节点,表示项目上线部署时候需要的依赖项,devDependencies节点,表示项目在开发阶段需要的依赖项,但是当项目要部署上戏了,devDependencies节点的包,就不在需要了
- 注意:当使用 npm i 快速装包的时候,npm 会检查package.json文件中,所有的依赖项,然后都会为我们安装到项目中
- npm --production表示只安装dependencies节点下,记录的包,不安装devDependencies节点下的包,当项目上线了,才会使用--production命令
解决npm下载慢的问题
- 默认NPM 在下载包的时候,连接的是国外的服务器,所以有时候如果网速不是特别好,可能下载不下来包,此时,可以全局安装一个工具,叫 cnpm
- 安装cnpm:运行 npm i cnpm -g 即可
- 使用cnpm: 在安装包的时候,只要把npm 替换成cnpm即可
B/S交互模型
B/S:特指基于 浏览器(Browser)和服务器(Server)这种交互形式
服务器:在网络节点中,专门对外提供资源服务的一台电脑
客户端:在网络节点中,专门用来消费服务的一台电脑
HTTP协议的通信模型:请求 处理 响应 的过程
请求:由客户端发起请求
处理:由服务器端处理请求
响应:服务器端把处理的结果,通过网络发送给客户端
静态资源:服务器端只需要读取并直接大宋给客户端、不需要进一步处理的资源,叫做静态资源
动态资源:服务器端没有现成的资源,需要服务器端动态生成的资源 叫做 动态资源
创建最基本的web 服务器
首先引入http模块: const http = require('http')
创建服务器:使用 const server = http.createServer()创建服务器
绑定监听事件:通过 server.on('request',function(req,res){请求的处理函数})绑定事件 并指定处理函数 req 请求对象 res 响应对象
启动服务器:通过 server.listen(端口号,ID地址,启动成功的回调函数) 来启动服务器 例如:
server.listen(3000,'127.0.0.1',()=>{
console.log('server running at http://127.0.0.1:3000');
})
防止响应内容乱码问题
通过 设置响应报文头的 Content-Type 来指定响应内容的编码类型,从而防止乱码(固定写法如下:)
res.writeHeader(200, {
'Content-Type': 'text/html; charset=utf-8'
})
结束当前响应,并发送一些数据到客户端 : res.end()
根据不同的URL返回不同的文本内容
使用req.url 获取客户端请求的URL地址
根据不同的URL返回不同的HTML页面
主要思路:使用fs模块 读取URL对应的HTML 页面内容,并使用 res.end响应给客户端即可
使用nodemon工具来自动启动web服务器
nodemon的作用:能够实时监听当前项目中文件的变化,只要监听到了文件的变化,则nodemon工具会自动重新启动nodemon服务器,从而使最小的代码生效,免去了程序员手动重启服务器的困扰
安装:运行 npm i nodemon -g 全局安装即可
使用:
之前使用node 要执行的文件路径 来运行node.js代码
现在使用 nodemon 要执行的文件路径 来运行node.js代码
注意:今后在开发web项目的时候,推荐使用 nodemon来启动web服务器
定义(什么是Express):一个快速的网站开发框架,封装了原生的http模块,用起来更方便,API更人性化
Express框架的特点
- 基于node.js平台之上,进一步封装了http模块,从而提供了更好用,更友好的API
- 使用express创建网站,比使用原生的http模块更加方便
- Express并没有覆盖原生http模块中的方法,而是基于原生方法之上,做了更友好的封装,让用户体验更好
Express框架的安装和基本使用
- 安装:npm i express -S
- 创建基本的express服务器
(1) 导入express第三方模块 const express = require('express')
(2) 创建服务器的实例:调用 const app = express()方法
(3) 通过app.get() 或者app.post()方法,来监听客户端的get或post请求 具体语法:
① 监听GET请求:app.get('请求地址',(req,res)=>{处理函数})
② 监听post请求:app.post('请求地址'(req,res)=>{处理函数})
1) 使用post()解决编码问题是 用querystring.parse(str[,sep[,eq[,options]]])【自动解码】
参数1:要解析的URL查询字符串
参数2:【可选】用于界定查询字符串的键值对的子字符串。默认‘&’
参数3:【可选】用于界定查询字符串中的键与值的子字符串。 默认为‘=’
参数4:【可选】
(4) 启动express服务器:通过app.listen(端口号,IP地址,启动成功的回调函数) 启动服务器
express中的快捷方法
- res.send()
(1) res.send()与res.end的区别:
① res.send()自带utf-8编码 不用自己在手动写入
(2) 支持发送字符串,[系统动态写入Content-Type:text/html]
① res.send('内容')
(3) 支持发送对象或者数组[系统动态写入 Content-Type:application/json]
① res.send({name:'张三',age:20}) 对象
② res.send([])数组
(4) 支持发送Buffer此时会当做文件下载(不会带上后缀名)
- res.sendFile()
(1) 用法1:res.sendFile(path.join(__dirname,'文件名'))
(2) 用法2:res.sendFile('文件名',{root:__dirname})
(3) 也支持下载文件(会自动带上后缀名)
① res.sendFile('要下载的文件',{root:__dirname,headers:{
'Content-Disposition':'attachment;filename= 文件名.后缀名'
}}) // Content-Disposition 指定文件类型 filename文件名
注意res.sendFile()可以向浏览器发送 静态页面
res.redirect(要跳转的路径) // 跳转到指定页面
res.render(‘模板页面路径’,{ /*数据对象*/ })
下载文件
app.get('/',(req,res)={
res.download('要下载的文件名','自己改的文件名'err=>{
if(err) return console.log(); // 失败
console.log()// 成功
})
})
使用express.static() 快速托管静态资源
如果我们在网站中,有很多静态资源需要被外界访问,此时,使用res.sendFile就有点力不从心;
这时候,express框架,为我们提供了 express.static('静态资源目录') 来快速托管指定目录下的所有静态资源文件
语法1:app.use(express.static('资源目录'))
app.use()方法,值专门用来注册 中间件;
express.static是express的内置中间件
语法2:app.use('/虚拟目录',express.tatic('资源目录'))
为express框架配置模板引擎渲染动态页面
- 安装ejs 模板引擎 npm i ejs -s
- 使用app.set()配置默认的模板引擎app.set('view engine','ejs')
- 使用app.set()配置默认模板页面的存放路径 app.set('views','要存放到哪个文件目录下')
- 使用res.render()来渲染模板页面 res.render(‘index.ejs’,{要渲染的数据对象}),注意,模板页面的后缀名,可以省略不写。(最好还是写上)
(1) 第一个参数:要渲染的页面名称→index.ejs
(2) 第二个参数:要渲染的内容 {} 里的内容
(3) 如果渲染的内容里有标签 只要把<%= %>改成<%- %>就可以了
这个模板只有 <%%> 没有 {{}} 的方法
可以省略不写
在express中配置art-template
- 安装两个包 cnpm i art-template express-art-template -S
- 自定义一个模板引擎 app.engine('自定义模板引擎的名称',渲染函数)
(1) app.engine('html',require('express-art-template'))推荐使用这个
- 将自定义的模板引擎,配置为express的默认模板引擎 app.set('view engine','具体模板引擎的名称')
- 配置 模板页面的存放路径 app.set('views','路径')
使用express框架中提供的路由来分发请求
1.路由:就是对应关系
2.后端路由:前段请求的URL地址,都要对应一个后端的处理函数,那么 这种URL地址到处理函数之间的对应关系,就叫后段路由
3.在express中,路由注意职责 就是 把请求分发到对应的处理函数中
4.在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
5.express创建的app服务器,使用路由模块
// 导入自己的路由模块
const router = require('./router.js')
// 使用 app.use() 来注册路由
app.use(router)
'路由:就是对应关系就是匹配规则
路由不会自己想当然的自我调用,而是由客户端请求到达服务器之后,拿着客户端请求的method类型,请求的URL地址,来和后端的路由规则,进行逐条匹配
如果匹配成功,则立即调用路由规则中指定的出路函数,使用指定的处理函数 来处理和响应这次请求
后端路由由三部分组成,分别是:请求类型(Method) ,请求地址(URL), 处理函数(就是路由规则中的回调函数)
对于路由来说,路由的主要职责:就是把客户端的请求,分发到对应的处理函数中
注意:对于路由来说,只关心分发请求的过程,不关心处理函数中具体的业务实现逻辑;'
mysql第三方模块基本配置
- 要安装操作数据库的第三方包 npm i mysql -S
- 导入包 const mysql = require('mysql')
- 创建数据库连接对象
const conn = mysql.createConnection({
host: '127.0.0.1', // 要连接到哪个电脑上的数据库
user: 'root', // 登录数据库的用户名
password: 'root', // 登录数据库的密码
database: 'heima_47' // 指定当前这个数据库连接对象,要操作哪个数据库})
4.使用conn.query('要知晓得sql语句',要提供的数据,(err,reslt)=>{回调函数}) 来执行sql语句
// result 执行的结果
// err 错误对象
// 查询数据
conn.query('要执行的Sql语句', 回调函数)
const sql1 = 'select * from users'
conn.query(sql1, (err, result) => {
// 如果执行Sql语句失败,则 打印报错信息
if (err) return console.log(err.message)
console.log(result)
})
//添加数据
const user = { username: '小绿', address: '日本' }
const sql = 'insert into users set ?'
conn.query(sql, user, (err, result) => {
if (err) return console.log(err.message)
console.log(result)
})
//修改数据
const user = { id: 4, username: '老驴', address: '小日本儿' }
const sql = 'update users set ? where id=?'
conn.query(sql, [user, user.id], (err, result) => {
if (err) return console.log(err.message)
console.log(result)
})
//删除数据
const sql = 'delete from users where id=?'
conn.query(sql, 4, (err, result) => {
if (err) return console.log(err.message)
console.log(result)
})
'中间件
定义:中间件就是一个处理函数; 只不过这个函数比较特殊,包含三个参数,分别是 req、res、next
注意:中间件方法中的三个参数是:
req: 请求对象 res:相应对象 next:next()可以被调用,表示调用下一个中间件方法
每个流水线上的数据,都可以叫’中间件’ 这些数据就是中间件的操作对象
中间件之间会共享一些数据
每个中间件,只负责做一件事情,功能单一
中间件之间,可以进行顺序的调用
如果上一个中间件,没有吧操作的结果,放回流水线,则下一个中间件,就无法继续进行操作了
Express框架中对中间件的5种分类
- 应用级别的中间件:挂在到app上的中间件 app.get('URL地址',(req,res,next)=>{})
- 路由级别的中间件:挂在到router对象上的中间件 router.get('URL地址',(req,req,next)=>{})
- 错误级别的中间件:回调函数中,有四个参数app.use((err,req,res,next)=>{})
- 唯一内置的中间件:express.static() // 托管静态资源
- 第三方中间件:非express框架提供的,需要程序员手动安装才能使用的中间件,body-parser 解析post表单数据
模块加载机制
优先从缓存中加载
当一个模块初次被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属性指向的文件不存在,则直接报错:Error:Cannot find module ***
- 假如没有main属性,或者没有package.json,那么会依次尝试加载index.js,index.json, index.node;
- 如果没有index相关的文件,或node_modules中,没有模块名称对应的文件夹,或者,当前项目根目录中没有node_modules文件夹,则向上一层目录中查找node_modules,查找规则同上!
- 最后,如果在项目所在磁盘的盘符根目录中,还找不到对应模块,则报错:cannot find module ***
. express中获取参数的几种形式
获取 http://127.0.0.1:3001/user?id=10&name=zs 中的查询参数:
直接使用 req.query 获取参数即可;
注意:URL 地址栏中通过 查询字符串 传递的参数,express 框架会直接解析,大家只需使用 req.query 直接获取 URL 中 查询字符串的参数;
从URL地址中获取路径参数:
假设客户端浏览器请求的URL地址为:http://127.0.0.1:3001/user/10/zs
假设后台的路由是 app.get('/user/:id/:name', (req, res) => {})
直接使用 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.1 混合模式(传统开发模式)
以后端程序员为主,基本上不需要前端程序员,或者,前端程序员只负责画页面、美化样式、写JS特效,前端程序员不需要进行数据的交互;
这种开发模式,在早些年比较常见;
传统开发模式下,用的最多的是 Jquery + 模板引擎 + Bootstrap
后端页面 .php .jsp .aspx .cshtml
2.2 前后端分离(趋势)
后端负责操作数据库、给前端暴露接口
前后端分离的好处:保证了各个岗位的职责单一;
前端负责调用接口,渲染页面、前端就可以使用一些流行的前端框架 Vue, React, Angular
前端页面的后缀名 .html .vue .jsx
JSONP 和 CORS 的区别
JSONP的原理:动态创建script标签;
JSONP发送的不是Ajax请求
不支持 Post 请求,只支持 Get 请求;
CORS中文意思是跨域资源共享 ,需要服务器端进行 CORS 配置;
CORS 发送的是真正的Ajax请求
CORS 支持Ajax的跨域
如果要启用 CORS 跨域资源共享,关键在于 服务器端,只要 服务器支持CORS跨域资源共享,则 浏览器肯定能够正常访问 这种 CORS 接口;而且,客户端在 发送 Ajax的时候,就像发送普通AJax一样,没有任何代码上的变化;
对于Node来说,如果想要开启 CORS 跨域通信,只需要安装cors的模块即可;
'MVC:就是分层开发的思想;
把复杂的业务处理,分为职能单一的小模块,各个模块之间看似相互独立,其实又各自有依赖关系
MVC的好处:保证了模块职能的单一性,方便程序的开发、维护、拓展
MVC中,M代表Model 是数据库操作层, V代表View,是页面视图层 C代表Controller,是业务处理层
注意: 在MVC分层开发思想中,Controller业务逻辑层,是最重要的一部分;
module.exports把文件暴露出去
URL解码
decodeURL(要解码的内容)
URL编码
encodeURL(要编码的内容)