NodeJs
第一章
node是什么
- Node.js 是一个开源与跨平台的 JavaScript 运行时环境。 它是一个可用于几乎任何项目的流行工具!
- Node.js 在浏览器外运行 V8 JavaScript 引擎(Google Chrome 的内核)。 这使 Node.js 表现得非常出色。
- Node.js 应用程序运行于单个进程中,无需为每个请求创建新的线程。 Node.js 在其标准库中提供了一组异步的 I/O 原生功能(用以防止 JavaScript 代码被阻塞),并且 Node.js 中的库通常是使用非阻塞的范式编写的(从而使阻塞行为成为例外而不是规范)。
- 当 Node.js 执行 I/O 操作时(例如从网络读取、访问数据库或文件系统),Node.js 会在响应返回时恢复操作,而不是阻塞线程并浪费 CPU 循环等待。
- 这使 Node.js 可以在一台服务器上处理数千个并发连接,而无需引入管理线程并发的负担(这可能是重大 bug 的来源)。
- Node.js 具有独特的优势,因为为浏览器编写 JavaScript 的数百万前端开发者现在除了客户端代码之外还可以编写服务器端代码,而无需学习完全不同的语言。
- 在 Node.js 中,可以毫无问题地使用新的 ECMAScript 标准,因为不必等待所有用户更新其浏览器,你可以通过更改 Node.js 版本来决定要使用的 ECMAScript 版本,并且还可以通过运行带有标志的 Node.js 来启用特定的实验中的特性。
检查自己电脑是否安装了node
打开cmd输入如下命令,如果正常返回了版本号,则说明已经安装成功
1 | node -v |
node学习网址
- node官方下载地址 http://nodejs.cn/
- node中文文档 http://nodejs.cn/api/
- node中文社区 https://cnodejs.org/
- node入门电子书 https://www.nodebeginner.org/index-zh-cn.html
使用 node.js 执行 js 文件
在要执行的 js 文件目录下执行
1 | node xxx.js |
使用 nodejs 读取文件
首先需要引入 fs ,fs 是 nodejs 中专门用来处理文件的一个库。读取到的文件默认是二进制文件流,所以要用 toString() 来转一下
1 | var fs = require("fs"); |
使用 nodejs 写入文件
1 | var fs = require("fs"); |
使用 nodejs 创建一个简单的 http 服务器
1 | // 1.引入http服务 |
http服务的简写形式
1 | let http = require('http') |
nodejs 中的模块的导入和导出
使用 require
导入一个其他模块,用一个变量来接收。
1 | // 使用 require 导入其他模块 |
使用 exports
导出一个模块,可以导出方法,变量
1 | // nodejs中两个互相引用的js如果变量名称相同不会相互受影响 |
除了单独导出外也可以批量导出
1 | function fun1() { |
使用 { }
接收批量导出的内容,用到几个引入几个
1 | var { fun2 } = require("./06导出模块") |
exports 和 module.exports 的区别
- 每个模块都有一个
module
对象 - module 对象中有一个
exports
对象 - 我们可以吧需要导出的成员都挂载到
module.exports
接口对象中 - 也就是
module.exports.XXX = XXX
的方式 - 但每次都
module.exports.XXX
很麻烦,点太多了 - 所以
Node
为了方便,同时在每一个模块对象中都提供了一个成员叫做exports
exports === module.exports
结果为true
- 所以对于
module.exports.XXX
的方式可以写成exports.XXX
- 当一个模块需要导出单个成员的时候,这个时候必须写成
module.exports = XXX
- 不要使用
exports = XXX
,这种方式不管用 - 因为每个模块最终向外
return
的是module.exports
- 而
exports
只是module.exports
的引用 - 所以即便对
exports = XXX
重新赋值,也不影响module.exports
- 但是有一种赋值方式为
exports = module.exports
这种赋值表示重新建立起两者的引用
1 | console.log(exports === module.exports); // true |
ip地址和端口号概念
- IP地址用来定位计算机的位置
- 端口号用来定位具体的应用程序
- 所有需要联网的通信和应用程序都会占用一个端口号
- 端口号的范围是 0~65536 之间
- 在计算机中有一些默认的端口号,例如
- http 服务的80端口
- 在开发过程中只会用到一些常见的端口,例如:8080,3000,8088…
响应式数据的content-type
- 声明文本格式的数据
res.Header("Content-Type","text/plain; charset=utf-8")
- 声明 html 格式的数据
res.setHeader("Content-Type", "text/html; charset=utf-8")
1 | var http = require("http"); |
http + fs 实现简单的页面渲染
- 在线工具地址 https://tool.oschina.net/
- 不同资源对应的 Content-Type 不一样,可以通过上面的网址查询
- url专业术语:统一资源定位符
- 一个 url 对应一个资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 在线工具地址 https://tool.oschina.net/
// 不同资源对应的 Content-Type 不一样,可以通过上面的网址查询
var http = require("http");
var fs = require("fs");
var server = http.createServer();
server.on("request", (req, res) => {
let url = req.url;
if (url === "/") {
// 利用 fs 读取文件,将文件内容发送个浏览器
fs.readFile("./resources/index.html", (err, data) => {
if (err) {
res.setHeader("Content-Type", "text/plain; charset=utf-8")
res.end("文件请求失败")
} else {
res.setHeader("Content-type", "text/html; charset=utf-8")
res.end(data)
}
})
} else if (url === "/jietu") {
// url专业术语:统一资源定位符
// 一个 url 最终指向一个资源
fs.readFile("./resources/jietu.png", (err, data) => {
// data默认是二进制数据,可以通过 toString 转为咱们能识别的字符串
// res.end 支持两种格式的数据类型,一种是二进制,一种是字符串
// 图片不需要指定编码格式,一般需要指定编码的都是字符串需要指定
res.setHeader("Content-Type", "image/png");
res.end(data)
})
}
})
server.listen(3000, () => {
console.log("3000端口启动成功");
})
path 路径操作模块
path 用于处理文件和路径的目录,可以使用一下方式使用它
1 | var path = require('path') |
path.basename() 获取路径中的文件名
path.basename() 方法会返回 path 的最后一部分,第二部分表示要去掉的后缀,没有则不去掉文件后缀
1 | console.log(path.basename(pathUrl)); |
path.dirname() 获取路径中的目录名
1 | console.log(path.dirname(pathUrl)); |
path.extname() 返回 path 的扩展名
1 | console.log(path.extname(pathUrl)); |
path.parse() 解析地址
path.parse() 方法会返回一个对象,其属性表示 path 的有效元素
1 | console.log(path.parse(pathUrl)); |
path.format() 根据对象还原地址
path.format() 方法从对象返回路径字符串
1 | console.log(path.format(path.parse(pathUrl))); |
path.join() 拼接路径
path.join() 方法会将所有给定的 path 片段连接到一起(使用平台特定的分隔符作为定界符),然后规范化生成的路径
1 | console.log(path.join('c:','abc','def')); |
path.isAbsolute() 检测路径是否为绝对路径
1 | // path.isAbsolute() 方法检测 path 是否为绝对路径。 |
获取当前文件所处目录的绝对路径并拼接地址
1 | console.log(path.join(__dirname, 'public')); |
node 中的其他成员
在每个模块中 ,除了 require
、exprots
等模块之外的相关 API 之外,还有两个特殊成员:
__dirname
,可以用来动态的获取当前文件所属目录的绝对路径__dilename
,用来动态的获取当前文件的绝对路径__dirname
和__dilename
获取到的路径是不受 node 命令所属路径影响的
在操作文件中,使用相对路径是不可靠的,因为在 node 中操作文件的路径被设计为相对于执行 node 命令所处的路径(不是bug,人家这样设计有使用场景)
所以为了解决这个问题,只需要把相对路径变成绝对路径就可以了
所以使用 __dirname
和 __dilename
来解决这个问题
另外在拼接路径中,为了避免出现低级错误,常常采用 path
模块提供的 join
方法来帮助我们拼接路径
所以为了避免出现上面这种错误,以后在操作文件时最好都将相对路径写成绝对路径
第二章
代码中无分号问题
当代码中采用没有分号的风格时需要注意以下几点即可
[
开头(
开头- ` 开头
当一行代码是以上面三个字符开头时,需要在这行代码前加上分号,否则会报错
除了分号外,也可以加上 ! ~ 等
1 | function day(){ |
根据路径获取文件内容
1 | var http = require("http"); |
读取某个文件夹下的所有文件
使用 fs.readdir()
函数读取某个文件夹下的所有文件,接受两个参数,分别如下
- 要读取的文件夹地址
- 读取后的回调函数,有两个参数
- 第一个是失败时的参数
- 第二个是读取成功后返回的数据,是数组格式的数据
1 | var fs = require("fs") |
在 node 中使用模板引擎
- 首先引入模板引擎插件
- 安装
npm install art-template
- 官方文档:http://aui.github.io/art-template/zh-cn/
- 安装
- 安装完成后,引入模板引擎
const template = require('art-template')
- 引入
fs
,用来读取文件和读取目录 - 引入
http
,用来搭建服务,给浏览器返回页面 - 读取页面数据后继续读取文件目录,获取目录列表
- 然后键页面数据转成字符串,交给模板引擎解析数据,并将目录列表数据传给页面,循环显示出来
js
1 | const artTemplate = require('art-template') |
html核心代码
1 | <!-- 在node中使用模板引擎.html --> |
服务端渲染和客户端渲染的区别
客户端渲染不利于 SEO 搜索引擎优化
服务端渲染可以被爬虫抓取到,客户端异步渲染很难被爬虫爬取到
所以真正的网站既不是纯异步也不是纯服务端渲染的
而是两者结合来做
例如:京东的商品列表采用的就是服务端渲染,目的为了 SEO 搜索引擎优化
而它的商品评论列表采用的就是客户端渲染,因为评论不需要 SEO 优化
两者优缺点 — 客户端渲染
- 优点
- 响应速度快,用户可以立马看到页面大致样式
- 用户体验好,翻页时无需重新加载页面
- 缺点
- 请求数据速度稍慢,需要等待页面加载完成后再去请求数据(最少两次请求)
- 不利于 SEO 优化,爬虫很难爬到,因为页面数据是异步的
- 优点
两者优缺点 — 服务端渲染
- 优点
- 页面和数据一次性返回给浏览器,用户看到页面是就是已经加载好的页面
- 利于 SEO 搜索引擎优化,爬虫可以爬取到
- 缺点
- 加载速度慢,翻页时需要重新加载页面
- 用户体检不好
- 优点
如何查看一个网页是服务端渲染还是客户端渲染
- 右键查看源代码,如果网页中的数据可以在源代码中找到,说明是服务端渲染,否则是客户端渲染
处理网站中的静态资源
- 服务端渲染页面时,静态文件资源和页面文件要放在两个单独的文件中,避免和程序运行文件放在一起
- 页面文件放在 view 文件夹下
- 静态资源文件放在 public 文件夹下
- css
- js
- img
- lib
- 页面中包含 src 和 href(不包含a标签)属性的标签在请求资源时页面加载过程中就会进行请求加载
- 服务端要根据这个资源的请求路径做处理
js文件中针对静态资源的请求单独处理
1 | // 此处判断如果是以 /public/ 开头的,如果是则认为这个请求是要获取静态资源 |
页面中请求静态资源时就不能以路径的方法去获取,而是以 url
的方法去获取,让服务端去给我们返数据
1 | <!-- 服务端渲染页面时,读取静态文件要以一个文件开头,有服务端去处理这个请求路径 --> |
渲染评论列表
使用模板渲染引擎,在服务端渲染完成后返回给浏览器。
1 | // 要循环渲染的数据 |
1 | <ul class="list-group"> |
使用表单默认提交和 h5 自带校验
- 在
form
标签中设置action
属性的值,表示表单数据提交的地址,浏览器会请求这个地址,并传递表单数据 - 在
form
标签设置method
属性,表示提交的方法get
post
- 在表单内部
label
标签上设置for
属性 - 在
input
标签上设置id
属性,和label
标签上的for
属性一致 - 在
input
标签上设置required
表示必填 - 在
form
结构内部添加type
值为submit
的按钮,点击按钮完成表单提交1
2
3
4
5
6
7
8
9
10
11
12
13
14<form action="/submitMsg" method="get">
<div class="form-group">
<label for="input_name">您的昵称</label>
<input type="text" class="form-control" placeholder="您的昵称" id="input_name" required min="2" maxlength="10"
name="name">
</div>
<div class="form-group">
<label for="testarea_msg">留言内容</label>
<textarea class="form-control" placeholder="留言内容" required id="testarea_msg" maxlength="20" minlength="5"
name="msg"></textarea>
</div>
<button type="submit" class="btn btn-success">提交</button>
</form>
使用 url 模块解析路径地址
1 | // 引入 url 模块 |
使浏览器重定向
- 浏览器什么时候会重定向
- 接口状态返回
301
或者302
时会重定向301
:永久重定向,第一次跳转后再次跳转时不会发送两次请求,直接重定向到新的地址302
:临时重定向,表示后面会变,可以用来做页面跳转
- 如果接口返回的状态码是
301
或者302
时会去响应头中寻找Location
指向的地址,然后自动请求这个地址,完成跳转操作
- 接口状态返回
- node 如何让浏览器重定向
- 设置接口返回的状态码为
302
res.statusCode = 302
- 设置响应头中的
Location
res.setHeader('Location', '/')
第二个参数是要重定向的地址
- 最后结束响应
res.end()
- 设置接口返回的状态码为
实现代码
1 | // 判断请求路径,处理参数,然后进重定向操作 |
第三章
require 加载规则
- 默认加载缓存,已经被加载过的模块不会重复加载,但是可以重复使用模块中导出的方法
a.js
1 | console.log('加载了a模块') |
b.js
1 | console.log('加载了b模块'); |
main.js
1 | require('./public/a') |
上面代码中 main.js
导入 a
模块,在 a
模块中又加载了 b
模块,因为 b
模块是被首次加载,所以会打印 b
模块中的代码,然后 b
模块导出了一个方法,最后又回到 main.js
的时候,b
模块已经被加载过了,所以不会再执行 b
模块的代码,但是 b
模块导出的方法可以使用,最终代码执行结果为
1 | 加载了a模块 |
require 加载机制
1.加载本地文件
require('./xxx')
一个点开头表示引入当前目录下的文件require('../xxx')
两个点开头表示引入上一级目录的文件require('/xx')
以/
开头表示当前文件所属磁盘的根目录(几乎不用)require('./index.js')
后缀名.js
可以省略
2.加载系统模块
核心模块的本质也是加载本地文件,但是核心模块的代码已经被编译到了二进制文件中,我们只需要按照模块名字导入即可
require('fs')
require('http')
3.加载第三方模块
第三方模块需要通过 npm install xxx(第三方包名)
来下载,然后通过 require('第三方包名')
来引入,不可能有第三方包名与系统的包名一样。引入第三方包的寻找机制如下,以art-template
包为例:
先找到当前文件所处目录的
mode_modules
文件然后找
node_modules/art-template/package.json
文件在
node_modules/art-template/package.json
文件中有一个mian
属性main
属性中声明的是入口模块之后加载使用这个第三方包
实际上最终还是加载的第三方文件
如果
package.json
文件不存在或者main
指定的入口模块也没有则
node
会自动找还目录下的index.js
文件也就是说
index.js
会作为一个默认的备选项如果以上任何一个条件都不成立,则会进入上一级目录中的
node_modules
目录查找,如果上一级也没有,则继续往上一级查找,只到找到为止如果没找到,则会报错
包文件说明
每个项目中最好都有一个 package.json
文件,可以简单理解为项目的说明书,这个文件中记载了项目名称,版本号,依赖包文件等。
首先在空白项目中执行 npm init
然后设置相关信息
1 | PS D:\AA-demo练习\node-demo> npm init |
除了采用上面手动创建 pacjage.json
文件外,也可以使用 npm init -y
跳过向导,快速生成 package.json
。
然后在安装第三方包是加上 --save
会自动在 package.json 中添加本次安装了那些包和这些包的版本号,例如输入 npm install jquery --save
,然后打开 package.json
1 | "dependencies": { |
可以在 dependencies
属性中看到本次安装了 3.6
版本的 jquery
。如果我们后续包 node_modules
文件删除后,只需要执行 npm install
就可以吧项目依赖的所有包自动安装回来。
npm 常用命令
npm init
- 初始化项目,创建
package.json
- 还可以写:
npm init -y
跳过向导,直接生成package.json
- 初始化项目,创建
npm install
- 一次性吧
dependencies
选项中的依赖包全部安装 - 简写:
npm i
- 一次性吧
npm install XXX --save
- 下载并保存依赖项到
package.json
- 简写:
npm i -S
- 下载并保存依赖项到
npm uninstall XXX
- 只删除包,不删除依赖项
- 简写:
npm un
npm uninstall XXX --save
- 删除包的同时删除依赖项
- 简写:
npm un -S
npm 配置淘宝镜像
首先安装 cnpm
,global
表示在全局安装
1 | npm install --global cnpm |
之后使用 cnpm install XXX
的方式下载第三方包,如果想使用 npm
开头的同时,用淘宝镜像安装,可以给 npm
配置如下信息
1 | npm config set registry https://registry.npm.taobao.org/ |
这句代码表示每次下载的时候通过这个网址下载安装依赖,可以使用 npm config list
来查看是否配置成功
1 | PS D:\AA-demo练习\node-demo> npm config list |
安装和使用 express
安装
1 | npm install express --save |
express 初体验
1 | // 引入 express |
第四章
express 使用
安装
1 | npm install -save express |
使用
1 | var express = require('express') |
使用 nodemon 完成更新代码后自动重启服务
安装,需要注意的是 nodemon
是需要全局安装的
1 | npm install -g nodemon |
使用 nodemon
运行 node
服务,之后保存时自动重启服务
1 | nodemon index.js |
express 中的静态文件
使用 /public 前缀地址来访问 public 目录中的文件了。
1 | app.use('/public', express.static('public')) |
栗子
- http://localhost:3000/public/images/kitten.jpg
- http://localhost:3000/public/css/style.css
- http://localhost:3000/public/js/app.js
- http://localhost:3000/public/images/bg.png
- http://localhost:3000/public/hello.html
不带前缀访问 public 目录中的文件
1 | app.use(express.static('public')) |
栗子
- http://localhost:3000/images/kitten.jpg
- http://localhost:3000/css/style.css
- http://localhost:3000/js/app.js
- http://localhost:3000/images/bg.png
- http://localhost:3000/hello.html
通过带有 /static 前缀地址来访问 public 目录中的文件
1 | app.use('/static', express.static('public')) |
栗子
- http://localhost:3000/static/images/kitten.jpg
- http://localhost:3000/static/css/style.css
- http://localhost:3000/static/js/app.js
- http://localhost:3000/static/images/bg.png
- http://localhost:3000/static/hello.html
express 中使用 art-template 渲染引擎
安装
1 | npm install --save art-template |
配置
1 | // 配置 art-template 模板引擎 |
使用
1 | // express 为 response 相应的对象提供了一个方法 render |
express 解析表单的 post 提交数据
安装 body-parser
1 | npm install --save body-parser |
使用,使用 res.body
获取 post
数据,是一个对象类型的数据
1 | var express = require('express') |
将文件直接按照 utf8 格式读取
默认使用 fs
读取文件,读取到的数据是二进制的数据,需要利用 toString()
方法转为人可以读取内容,现在利用下列方法,可以在读取数据是返回的就是经过转换后的数据
1 | fs.readFile('./db.json', 'utf8', (err, data) => { |
结合 express 配置 router 模块
之前我们都把路径请求都写在 index.js 文件中,这样路径多的话,一个文件的体积就过于庞大,所以,我们要将路由模块单抽离出来,减少代码的复杂度,同时方便后期维护。
- 首先新建 router.js 文件
1 | var fs = require('fs') |
- 在 index.js 文件中引入 router.js 并将 router 挂载到 app 中
1 | // 引入 router.js |
使用回调函数获取异步数据
1.封装读取数据的方法,接受一个函数参数
1 | // 封装学生列表的增删改查方法 |
2.使用封装的读取方法
1 | var express = require('express') |
第五章
封装原生的 get 请求
- 回调函数的使用
获取倒计时1秒后的执行方法返回的数据
错误版本
1 | var ret = 0; |
正确版本
1 | function sucCount(x, y, callback) { |
封装get请求
1 | // 封装ajax请求 |
package-lock.json 文件的作用
如果我们的 npm
版本是 5 版本以上,则我们每次使用 npm
安装依赖时会自动生成或者更新 package-lock.json
文件。那么这个文件有什么用呢?
npm5
以后的版本在安装依赖时不用加--save
参数,也可以自动保存依赖信息- 当安装依赖时,会自动创建或者更新
package-lock.json
这个文件 package-lock.json
这个文件会保存node_modules
中所有的依赖的信息(版本、下载地址)- 这样的话
npm install
时的速度会提升
- 这样的话
- 从文件名称看
lock
称之为锁- 这个
lock
是用来锁定版本的 - 例如在
package.json
文件中某个依赖包的版本为^3.1.5
,这个^
的意思就是这个依赖可以在3.0
这个大版本中往上升级,但是不会超出3.0
版本中的最高版本,不会升级为4.0
版本。如果没有package-lock.json
这个文件的话,在下次npm install
时,如果发现这个包存在更高版本,那么npm
会下载最新版本的依赖,有时候不同版本之间可能会存在不兼容的情况,所以为了避免这种情况,package-lock.json
中就记录了每个依赖包的具体版本号,这样在下次npm install
时就会按照package-lock.json
中记载的版本号来下载。也就是锁定版本,不会升级
- 这个
find 和 findIndex 的原理
首先声明一个数组
1 | let user = [ |
find
常用来查找某个元素,找到第一个符合条件的元素后便会退出循环,并返回这个元素
1 | // find 原理,根据下标找元素 |
findIndex
常用来查找某个值在数组中的下标位置,找到第一个符合条件的元素后便会退出循环,并返回这个元素所属下标
1 | // findIndex 原理,根据元素找下标 |
MongoDB 简介及安装
关系型数据库和非关系型数据库
- 所有的关系型数据库都需要通过
SQL
语句来操作 - 所有的关系数据库在操作之前都要设计表结构
- 而且数据表支持约束
- 唯一的
- 主键
- 默认值
- 非空
- …
- 非关系型数据库非常灵活
- 有的非关系型数据库就是
key-value
键值对 - 但是
MongoDB
是长得最像关系型数据库的非关系型数据库- 数据库 -> 数据库
- 数据表 -> 集合(数组)
- 表记录 -> 文档对象
MongoDB
不需要设计表结构- 可以任意的往里面存数据,没有结构性这么一说
安装
下载包之后一直next,然后配置环境变量,将 MongoDB
的安装目录中的 bin
文件地址配置到环境变量中,然后打开命令提示符输入 mongod --version
敲回车看到版本号说明安装成功
启动和关闭 MongoDB 服务
启动
输入命令后会看到很多前面是日期的日志,这是不要关闭窗口,表示服务启动成功
1 | mongod |
MongoDB
默认使用执行 mongod
的磁盘根目录下的 data\db
文件夹作为自己的数据存储目录,如果要更改默认的存储目录,输入如下命令
1 | mongod --dbpath=数据存储地址 |
关闭
直接 ctrl + c,或者直接关闭窗口
MongoDB 基本感知
链接数据库
1
mongo
MongoDB 创建数据库
如果数据不存在会自动创建,存在则切换到该数据库下
1 | use myusers |
查看所有数据库
1
2
3
4show dbs
admin 0.000GB
config 0.000GB
local 0.000GB这里看不到我们新建的数据库是因为我们还没有往数据库中插入任何输入
往数据库中插入数据
1
2db.myusers.insert({"name":"张三"})
WriteResult({ "nInserted" : 1 })此时重新查看所有数据库就可以看到我们我们新建的 myusers 数据库
1
2
3
4
5show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
myusers 0.000GB查看某个数据库中的数据
1
2db.myusers.find()
{ "_id" : ObjectId("606ec04705d3413a7204af25"), "name" : "张三" }查看 test 数据库中 kittens 表中的所有数据
1 | 先查看当前所在的数据库 |
- 删除某个数据库
1
2
3
4
5
6
7
8
9
10
11
12
13查看当前所在数据库
db
myusers
删除当前数据库
db.dropDatabase()
{ "dropped" : "myusers", "ok" : 1 }
查看所有数据库
show dbs
admin 0.000GB
config 0.000GB
local 0.000GB - 删除集合,集合相当于数据库中的表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16创建集合 runoob
db.createCollection("runoob")
{ "ok" : 1 }
查看所有集合
show tables
myusers
runoob
删除某个集合
db.runoob.drop()
true
查看某个集合
show tables
myusersNodeJs 中链接 MongoDB
- 使用 MongoDB 官方的 npm 包
npm install mongodb
- nodejs中使用MongoDB菜鸟教程
- 我们使用 mongoose
npm install mongoose
- mongoose中文文档
1 | // 首先引入 mongoose |
MongoDB 数据库的基本概念
- 可以有多个数据库
- 一个数据库中可以有多个集合(表)
- 一个集合中可以有多个文档 (表记录)
- 文档结构很灵活,没有任何限制
- MongoDB 非常灵活,不需要想 MySQL 一样先创建数据库、表、设计表结构
- 这里只需要:当你插入数据的时候指定往哪个数据库中的那个集合操作就可以了
- 一切都由 MongoDB 来自动完成建库建表操作
可以用一个对象来模拟这种结构
1 | // 整个对象相当于MongoDB数据库 |
设计 schema 和发布 model
允许使用的 SchemaTypes 有:
- String
- Number
- Date
- Buffer
- Boolean
- Mixed
- ObjectId
- Array
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35// 引入 mongoose
let mongoose = require('mongoose')
// 1.引入 schema(架构)
let Schema = mongoose.Schema
// 链接数据库,即使 itusers 数据库不存在也没关系,也可以直接链接,在第一个插入数据时会自动创建这个数据库
mongoose.connect('mongoose://localhost/itusers')
// 2.设计集合结构(表结构)
// 字段名称就是表结构中的属性名称
let UserSchema = new Schema({
// 用户名称
name: {
// 类型为 string
type: String,
// 这个字段必填
require: true
},
password: {
type: String,
require: true
},
email: {
type: String
}
})
/**
* 3.将文档结构发布为模型
* mongoose.model 方法接收两个参数
* 第一个参数为数据库的集合名称,传入大写的名词会自动转换为小写的复数集合名称
* 这个例子中的 User 会被转成 users ,也就是说生成的集合名称叫做 users
* 第二个参数传入一个Schema实例 ,约定了这个集合中字段名称和文档结构
*/
let User = mongoose.model('User',UserSchema)
mongoose 增删改查
添加数据
先插入一条数据,此时的数据还没有真正的保存到数据库中
1 | /** |
将数据保存到数据库中,持久化数据
1 | useUser.save((err,res)=>{ |
查询数据
查询集合中的所有数据。语法:集合名称.find(callback)
1 | User.find((err,data)=>{ |
查询集合中符合条件的所有数据。语法:集合名称.find(查询条件,callback)
,查询到的数将会被放进一个数组中,查不到时返回 []
1 | User.find({name:"张三"}, (err, data) => { |
查询集合中符合条件的第一条数据。语法:集合名称.findOne(查询条件,callback)
,查询到的数据直接是一个对象,查不到时返回 null
1 | User.findOne({name: "张三"}, (err, data) => { |
模糊查询一个字段
1 | // 查询 name 中包含 李 的数据 |
多条件模糊查询
1 | // 查询 name 中包含 李 的,或者 password 中有 123 的 |
删除数据
删除某个集合中符合条件的所有数据
1 | // 下面代码会将 User 集合中的 name 是 张三的数据全部删除 |
删除集合中符合条件的第一条数据
1 | // 删除 User 集合中 name 是 张三的第一条数据 |
根据id删除数据
1 | // 根据id删除元素 |
修改数据
根据 id 修改数据
1 | let newdata = { |
修改符合条件的第一条数据
1 | User.updateOne({ name: '张三'}, newdata, (err, data) => { |
模糊查询数据后修改符合条件的所有数据
1 | // 将数据中 name 里面包含有 李 的数据全部修改为 河南彭于晏 |
Nodejs 连接 MySql
安装依赖
1 | npm install mysql --save |
连接数据库
1 | // 引入操作 mysql 的包 |
添加数据
1 | let addSQL1 = 'INSERT INTO users VALUES(NULL,"张三","18","0","15038888888","admin@admin.com")' |
修改数据
1 | // 修改 sql 语句,将 id 是 2 的 name 和 age 修改 |
删除数据
1 | // 删除 sql 语句,删除 id 等于 3 的数据 |
查询数据
1 | // 查询 sql 语句 |
前后模糊查询
模糊查询 users 表中的 name 字段,只要包含 张 的就数据就会被查询出来
1 | let sql = 'SELECT * FROM users WHERE name LIKE '%张%'' |
修改表中的列名
语法:ALTER TABLE 表名 CHANGE 旧列名 新列名 字段类型和长度
1 | let sql = 'ALTER TABLE users CHANGE gerder gender VARCHAR(322)' |
修改表中的列的长度
语法:ALTER TABLE 表名 MODIFY COLUMN 列名 字段类型和要修改的长度
1 | let sql = 'ALTER TABLE users MODIFY COLUMN gender VARCHAR(255)' |
使用 Promise 解决地狱回调问题
什么是回调地狱,回调地狱产生的原因是由于多个异步方法深入嵌套导致,致使代码结构不好理解,下面代码就是地狱回调
1 | // 回调地狱产生原因是由于多个异步方法相互嵌套,导致代码越嵌套越深 |
使用 ES6 语法中的 Promise 来解决上面的这种问题
1 | let fs = require('fs') |
json-server 服务器
前言
安装这个插件可以仅仅写一个 json 文件就可以启动一个数据服务,非常方便,支持输入参数查询
安装
1 | npm install -g json-server |
查看版本
1 | json-server -v |
使用
首先新建 db.json 文件,并写入如下内容
1 | { |
启动服务, 加上 –watch 可以实时监控 json 文件的变化,无需重复启动服务
1 | json-server --watch .\db.json |
启动成功后默认端口是 3000,浏览器输入 http://localhost:3000/ 可以看到一个页面,页面中显示了所有可以请求的路径。
输入 http://localhost:3000/posts 可以看到 posts 数组中的所有数据
输入 http://localhost:3000/posts/1 表示只返回 id 是 1 的数据
输入 http://localhost:3000/posts?title=李四 表示只返回 title 是李四的数据
第六章
art-template 中的 include-extend-block
子模板
利用子模板可以重组利用相同的页面内容
封装模板
1 | <!-- hader.html --> |
引入模板,使用 include 加上空格,跟上要引入的模板路径
1 | <!-- layout.html --> |
模板继承
模板继承可以将一整个页面引用过来,值需要填入对应的“坑”即可
创建模板,其中使用 block 给孩子页面留下对应的坑,孩子只需要往坑里填内容就可以在模板上展示出来
1 | <!-- layout.html --> |
使用模板,extend 跟上空格,后面再加上模板路径,填坑的方式和留坑的方式相同,但是名称和位置要一一对应
1 | <!-- index.html --> |
项目搭建
1 | |-- 目录结构 |
路由设计
接口地址 | 请求方式 | 请求参数 | 是否需要登录 | 接口描述 |
---|---|---|---|---|
/ | get | 渲染首页页面 | ||
/homelist | post | 获取首页列表数据 | ||
/register | get | 渲染注册页面 | ||
/register | post | email、password、nickname | 处理注册请求 | |
/login | get | 渲染登录页面 | ||
/login | post | email、password | 处理登录数据 |
设计表结构-用户表
字段名称 | 字段类型 | 是否必填 | 默认值 | 可选值 | 字段描述 |
---|---|---|---|---|---|
String | true | 用户名 | |||
nickname | String | true | 昵称 | ||
password | String | true | 密码 | ||
created_time | Date | false | Date.now | 创建时间 | |
last_modified_time | Date | false | Date.now | 最后修改时间 | |
avatar | String | false | avatar-max-img.png | 用户头像 | |
bio | String | false | 个人简介 | ||
gender | Number | false | -1 | [-1, 0, 1] | 性别 |
birthday | Date | false | 生日 | ||
status | Number | false | 0 | [0, 1, 2] | 用户状态 |
处理注册逻辑
- 首先判断用户输入的邮箱或者昵称是否在数据中已经存在
- 如果存在则提示该昵称或者邮箱已经存在,请修改后重试
- 如果不存在进行注册处理
- 使用 md5 加密对用户输入的密码进行加密处理,为了尽可能保证数据安装,使用双层加密
- 定义后台返回的错误状态码
- 如果是服务器出错,返回 500 状态
- 如果接口调用成功则接口状态为200,但是要返回不同的错误码来判断逻辑业务
- 业务处理失败 code = 1111
- 业务处理成功 code = 0000
mongodb 或者查询语句
1 | >db.col.find( |
express 直接返回 json 格式的数据
1 | return res.status(500).json(err) |
md5 加密数据
安装
1 | npm install md5-node |
使用
1 | let md5 = require('md5-node') |
封装一个记录错误日志的方法
此方法接受一个错误信息,可以将这个错误信息和发生错误的时间记录到一个 txt 文件中,方便查看程序的错误信息
1 | let fs = require('fs') |
注册完整示例
1 | // 处理注册逻辑 |
利用 async 和 await 优化代码结构
async 和 await 是 promise 的语法糖,处理异步请求非常简单,下面是优化后的代码
1 | // 处理注册逻辑 |
表单同步提交和异步提交
- 同步提交页面会锁死,并且服务器返回的数据会渲染在页面上,返回上一页时上次填写的表单数据会被清空
- 异步提交时表单返回的数据不会渲染在页面上,并且提交时不会锁死页面,还可以做其他事情
客户端的重定向针对于异步请求无效
当客户端是异步请求时,服务端进行重定向操作后,浏览器也会进行异步重定向,从而不会让页面发生跳转
通过 session 保存登录状态
安装
1 | npm install express-session |
配置
1 | let session = require('express-session') // 使用 express 的 session |
使用
1 | // 写入session |
中间件概念
客户端请求服务端的数据,服务端返回数据之前会对数据进行一系列的处理,每个处理过程我们称之为中间件
express 中添加中间件的方法,app.use
1 | let express = require('express') |
在项目中添加处理404的中间件
1 | // 配置路由文件 |
在项目中添加错误统一处理中间件
- 注意:只有 next(err) 方法携带了错误参数并且中间件接受四个参数才会进入到错误处理的中间件中
1 | // 发生错误时,执行next方法,进入到下一个中间件中,并且携带错误参数,会进入到接受四个参数的中间件中 |
1 | // 配置处理错误的中间件,只有后台发生错误就会进来这里面 |