Nodejs学习笔记
# Node.js
- Node.js是JavaScript运行时环境
- Node.js可以解析和执行JavaScript代码
- 以前只有浏览器可以解析和执行JavaScript代码
- 现在可以脱离浏览器来运行,一切都归于Node.js
- Node.js中的JavaScript
- 没有BOM、DOM
- EcmaScript
- 在Node这个JavaScript执行环境中为JavaScript提供了一些服务器级别的操作API
- 例如文件读写
- 网络服务的构建
- 网络通信
- http服务器
# 优缺点
- 异步非阻塞的
I/O
(I/O线程池) - 特别适用于
I/O
密集型应用(对比传统java服务器) - 事件循环机制(独有的一套,与浏览器不一样)
- 单线程(成为单线程,败也单线程)
- 跨平台(几乎常见语言都支持)
不足之处:
回调函数嵌套太多、太深
单线程,处理不好CPU密集型
# 读取文件
使用require方法加载fs核心模块
第一个参数未读取文件的路径
成功data->数据,error->null,失败data->undefined没有数据,error->错误对象
data返回二进制,toString返回文件的信息
const fs = require('fs');
fs.readFile('./data/hello.txt',function(error,data){
console.log(data.toString())
})
2
3
4
# 写文件
- 第一个参数是文件的名字,可不存在
- 第二个参数是要写的文件内容
- 第三个参数是回调函数
- 当为不规范的文件名时,文件写入失败
const fs = require('fs');
fs.writeFile('./data/hello.md','大家好,我是node.js',function(error){
if(error){
console.log('文件写入失败')
}else{
console.log('文件写入成功')
}
})
2
3
4
5
6
7
8
# http
- 帮你创建编写服务器
- 回调函数request请求对象,response响应对象
- request.url获取当前请求路径
- response对象有一个方法:write可以用来给客户端发送请求响应数据,write可以使用多次,但是最后一定要使用end来结束响应,否则客户端会一直等待
- res.end()响应内容只能是二进制数据或字符串
- 中文系统默认编码为gbk,服务器默认是utf-8编码,第一个参数内容类型,第二个参数为文本类型
- 图片就不需要指定编码了
res.setHeader('Content-Type','text/plain;charset=utf-8')
//加载http核心模块
const http = require('http')
//使用http.createServer()方法创建一个Web服务器
//返回一个Server实例
const server = http.createServer()
//客户端请求过来,自动触发服务器request请求事件,然后执行第二个参数:回调处理
server.on('request',function (request,response) {
console.log('收到客户端的请求了!' + request.url)
console.log('请求我的客户端端口号是:' + request.socket.remotePort)
console.log('请求我的客户端的ip地址是:' + request.socket.remoteAddress)
//所有的url都是以/开头的
const url = request.url;
if(url=='/index'){
response.end('hello node.js')
}else{
// response.write('hello')
// response.write(' nodejs')
response.end('hello world')
}
// 结束响应
// response.end()
})
//绑定端口号,启动服务器
server.listen(3000,function () {
console.log('服务器启动成功了,可以通过http://127.0.0.1:3000/来进行访问')
})
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
# Node中的JavaScript
EcmaScript
- 没有DOM、BOM
核心模块
- Node为JavaScript提供了很多服务器级别的API,这些API绝大多数都被包装到了一个具名的核心模块中了,例如文件操作的
fs
核心模块,http服务构建http
模块,path
路径操作模块,os
操作系统信息模块等 - 以后只要这个模块是核心模块,就必须
const fs = require('fs')
1- Node为JavaScript提供了很多服务器级别的API,这些API绝大多数都被包装到了一个具名的核心模块中了,例如文件操作的
第三方模块(通信)
模块是完全封闭的,外部无法访问内部,内部也无法访问外部
优点:可以完全避免变量名命名冲突的问题
require(模块化编程)=>文件作用域
加载文件模块并执行里面的代码
拿到被加载文件模块导出的接口对象
可以省略后缀名
相对路径必须加./,例如
require('./os.js') require('./os')
1
2
exports
- 默认是一个空对象
用户自定义模块
# 模板引擎
- 安装npm install art-template
# 服务端渲染&客户端渲染
- 服务端渲染在浏览器中查看网页元素可搜索到,客户端渲染不行
- 客户端渲染不利于SEO搜索引擎优化,百度很难搜索到
- 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
- 真正的网站是两者结合做的,例如京东的商品列表是服务端渲染的,目的是为了SEO搜索引擎优化,评论是客户端渲染,不需要SEO优化
# 什么是模块化
- 文件作用域
- 通信规则
- 加载require
- 导出
# CommonJS模块规范
在Node中的JavaScript还有一个很重要的概念,模块系统
模块作用域
使用require方法用来加载模块
使用exports接口对象用来导出模块的成员
如果一个模块需要直接导出某个成员,而不是挂载
导出单个成员,必须使用module.exports = add,拿到的只有一个,后者会覆盖前者
- 也可以导出多个成员
exports
和module.exports
等价,可以导出多个成员,返回的也是个对象module.exports = { add:function(){ return x + y }, str:'hello' } //exports { foo: 'bar', add: [Function] }
1
2
3
4
5
6
7
8
exports.foo = 'bar' exports.add = function(x,y){ return x+y }
- 导出多个成员exports.a = add (前面的add是变量)
1
2
3
4
5
6
7
# 原理解析
exports
和module.exports
的一个引用- 也就是说在模块中有这么一句代码var exports = module.exports
- 最后return的是module.exports
console.log(exports === module.exports) //true
exports.foo = 'bar'
//等价于
module.exports.foo = 'bar'
2
3
4
5
6
# exports和module.exports的区别
# 优先从缓存加载
- 被require加载过的,将不再调用加载,只执行一次加载
- 在次调用主要是拿到里面的接口对象
- 由于在a中加载b了,所以从缓存中加载,只会拿到里面的接口对象,不会再次加载
- 避免重复加载,提高模块加载效率
# 第三方模块的加载规则
# package.json
- 建议每个项目都要有一个
package.json
文件(包描述文件) - 这个文件可以通过
npm init
的方式来自动初始化出来 - 最有用的是
dependencies
选项,用来保存第三方包的依赖信息 - 建议执行
npm install
都加上--sava
,用来保存第三方包的依赖信息 - 如果
node_modoles
删除了,已经有第三方依赖包了,直接执行npm install
或者npm i
是install的简写
# npm
- node package manager
# npm网站
npmjs.com
# npm命令行工具
--save
简写-S
,install
简写i
只要安装了node就已经安装了npm
查看npm版本
npm --version
升级npm,
npm install --global npm
常用命令
npm iniy -y
可以跳过向导,快速生产package.json文件npm install 包名
npm install --save 包名
- 下载并保存依赖项(package.json中的dependencies选项)
npm i -S 包名
npm install
- 只下载
npm i
npm uninstall 包名
- 只删除,如果有依赖项会依然保存
npm uninstall --save 包名
- 删除的同时会把依赖信息也去除
npm un -S 包名
npm --help
- 查看使用帮助
npm 命令 --help
- 查看指定命令的使用帮助
cnpm i -S bootstrap@3.3.7
- 安装指定版本
- npm view jquery versions
- 查看jquery的所有版本
- npm root -g
- 查看全局安装的地址
- npm ls jquery
- 查看当前环境下的版本
# 解决npm被墙问题
- npm存储包文件的服务器在国外,有时候会被墙,速度很慢,所以我们需要解决这个问题
- 网站
npm.taobao.org
- 在任意目录下安装淘宝的cnpm,
--global
表安装全局npm install --global cnpm
- 接下来安装包的时候把之前的
npm
替换成cnpm
- 如果不想安装
cnpm
又想使用淘宝的服务器下载npm config set proxy https://registry.npm.taobao.org
- 查看npm配置信息
npm config list
# Buffer
接受客户端的数据(视频、图片等)
- 类数组,用来存储数据(二进制数据)
- 效率高
- Buffer的大小一旦确定了,不可修改
- 灭个元素占用内存的大小为1字节
- Buffer是Node中的核心模块,无需下载,无需引入
# 使用
new一个Buffer实例,性能差
使用性能好的
- Buffer.alloc()
- Buffer.allocUnsafe()
- Buffer.from将数据存入一个Buffer实例
- 为什么是二进制的:用户存储的不一定是字符串,可能是媒体类型的文件
# Express
- 原生的http在某些方面表现不足以应对我们的开发需求,所以我们就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码更高度统一
- 在Node中,有很多Web开发框架,我们这以学习
express
为主 - 网站
expressjs.com
- get请求在express中可以直接req.query来获取查询字符串参数
// 引包
const express = require('express')
// 创建服务器应用程序,也就是原来的http.createServer
const app = express()
// 公开指定目录
app.use('/public/',express.static('./public/'))
app.get('/',function(req,res){
res.send('hello express')
})
app.get('/about',function(req,res){
// 在express中可以直接req.query来获取查询字符串参数
console.log(req.query)
res.send('你好,我是express!')
})
app.listen(3000,function(){
console.log('app is running at port 3000.')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 文件操作路径和模块标识路径问题
- 文件操作(fs)中的相对路径可以省略
./
- 在模块加载中,相对路径中的
./
不能省略,.js
可以省略 - 所有文件操作的API都是异步的
# 修改完代码自动重启服务器
- 第三方命名航工具:
nodemon
来解决修改代码重启服务器问题 nodemon
是一个基于Node.js开发的一个第三方命令行工具- 需要安装
cnpm install --global nodemon
- 安装完后,使用
nodemon app.js
自动重启服务器
# 基本路由
- 路由器
- 请求方法get,post
- 请求路径
- 请求处理函数
# 公开静态资源服务
- 当省略第一个参数的时候,则可以通过省略/public的方式来访问
- 访问时直接写文件名即可,不用加/public,直接public文件下的资源
// 引包
const express = require('express')
// 创建服务器应用程序,也就是原来的http.createServer
const app = express()
// 公开指定目录
//当以/public/开头的时候,去./public/目录中找对应的资源
app.use('/public/',express.static('./public/'))
//当省略第一个参数的时候,则可以通过省略/public的方式来访问
//访问时直接写文件名即可,不用加/public
app.use(express.static('./public/'))
app.get('/',function(req,res){
res.send('hello express')
})
app.get('/about',function(req,res){
// 在express中可以直接req.query来获取查询字符串参数
console.log(req.query)
res.send('你好,我是express!')
})
app.listen(3000,function(){
console.log('app is running at port 3000.')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Express中的render
express中默认不存在render渲染数据,render只有安装了模板引擎才有
- 安装
npm install --save art-template
npm install --save express-art-template
2
- 配置
// view engine setup
app.engine('html', require('express-art-template'));
2
# Express中get请求和post请求
get请求获取表单参数通过
req.query
post请求获取请求体数据,没有内置获取post请求的api
- 通过express 的middleware
- 安装
npm install --save body-parser
- 配置
var express = require('express') //引包 var bodyParser = require('body-parser') var app = express() //配置body-parser //只要配置了,就可以通过req.body来获取post请求的请求体数据了 // parse application/x-www-form-urlencoded app.use(bodyParser.urlencoded({ extended: false })) // parse application/json app.use(bodyParser.json()) app.use(function (req, res) { res.setHeader('Content-Type', 'text/plain') res.write('you posted:\n') //通过req.body来获取请求体数据 res.end(JSON.stringify(req.body, null, 2)) })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19- 通过req.body获取请求参数
# 提取路由模块
router.js
const fs = require('fs')
const express = require('express')
// 创建路由容器
const router = express.Router()
// 把路由挂载到router路由容器中
router.get('/students',function(req,res){
fs.readFile('./db.json','utf8',function(err,data){
if(err){
return res.status(500).send('Server error.')
}
const students = JSON.parse(data).students
res.render('index.html',{
fruits:[
'苹果',
'香蕉',
'橘子'
],
students
})
})
})
router.get('/students/new',function(req,res){
res.render('new.html')
})
router.post('/students/new',function(req,res){
// 获取表单的数据
// console.log(req.body)
// 处理,将数据保存在db.json中
// 发送响应
})
router.post('/students/edit',function(req,res){
})
router.get('/students/delete',function(req,res){
})
// 导出router
module.exports = router
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
37
38
39
40
41
42
43
44
app.js
const router = require('./router')
// 把路由容器挂载到app服务中
app.use(router)
2
3
# package.json和package-lock.json
- npm5以前没有
package-lock.json
这个文件 - npm5以后才加入了这个文件
- 当年安装包的时候,npm都会生产或者更新
package-lock.json
这个文件 - npm5以后版本安装包不需要加
--save
参数,它会自动保存依赖信息 - 当你安装包的时候,会自动创建
package-lock.json
这个文件 package-lock.json
会保存node_modules
中所有包的信息(版本,下载地址)- 重新
npm i
的时候下载的速度会快很多 - 锁定版本号,防止自动升级新版
- ^锁定大版本
- ~锁定小版本
- 重新
# express案例
# MongoDB
# 关系型数据库和非关系型数据库
表就是关系,或者说表与表之间存在关系
所有的关系型数据库都要通过
sql
语言来操作所有的关系型数据库在操作之前都需要设计表结构
而且数据表还支持约束
- 唯一的
- 主键
- 默认值
- 非空
非关系型数据库非常的灵活
有的非关系型数据库就是key-value键值对
但是MongoDB是长的最像关系型数据库的非关系型数据库
- 数据库->数据库
- 数据表->集合
- 记录->文档对象
MongoDB不需要设计表结构
-
- community Server
配置环境用户变量Path
C:\Program Files\MongoDB\Server\4.4\bin
1
验证是否安装成功
mongod --version
注意:不支持事务,复杂查询时语句过于频繁
# 启动和关闭数据库
启动
- 在C盘下手动新建
/data/db
(在哪执行的命令就在哪个盘建 - 执行命令
mongod
- 如果想要修改数据存储目录,可以:
mongod --dbpath=数据存储路径
停止
- 开启服务的控制台
ctrl+c
- 直接关闭服务的控制台
# 连接和推迟数据库
连接
- 直接执行
mongo
命令
退出
- 执行
exit
# 基本命令
show dbs
- 查看显示所有数据库
show collections
- 查看当前操作的数据库的集合
db
- 查看当前操作的数据库
use 数据库名称
- 切换到指定的数据库(如果没有会新建)
- 插入数据
db.students.insertOne({"name": "Jack"})
- 查看插入的数据
db.students.find()
- 查看更多数据
it
# 在Node中如何操作MongoDB数据
# MongoDB数据库的基本概念
- 数据库
- 集合
- 文档
- 指定连接的数据库不存在,当你插入第一条数据后被创建
{
//数据库
qq:{
//集合
users:[
//一条记录一个对象(文档)
{name:"张三",age:15},
{},
{}
],
products:[
]
},
taobao{
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# mongoose
- 使用第三方mongoose在操作MongeDB数据库
- mongoose官网 (opens new window)
- 第三方包:
mongoose
基于MongoDB官方mongodb
包再次封装 - 安装mongoose,
npm i --save mongoose
hello word:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
//设计集合结构(表结构)
const Cat = mongoose.model('Cat', { name: String });
const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));
2
3
4
5
6
7
# 设计Schema发布model
const mongoose = require('mongoose');
const Schema = mongoose.Schema
//1.指定连接的数据库不存在,当你插入第一条数据后被创建
mongoose.connect('mongodb://localhost/test');
//2.设计集合结构
let userSchema = new Schema({
username:{
type:String,
required:true
},
password:{
type:String,
required:true
},
email:{
type:String
}
});
// 3.将文档结构发布为模型
//mongoose.model方法就是将一个架构发布为一个model
//第一个参数,传入一个大写名词单数字符串来标识你的数据库名称
//mongoose会自动将大写名词字符串生成小写复数的集合名称
//第一个参数:架构Schema
// 返回值:模型构造函数
let User = mongoose.model('User', userSchema);
//4.使用构造函数对users集合中的数据为所欲为了,增删改查
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
# 增加数据
let admin = new User({
username:"admin",
password:"123456",
email:"admin@admin.com"
})
admin.save(function(err,ret){
if(err){
console.log('保存失败')
}else{
console.log('保存成功')
console.log(ret)
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查询数据
//查询所有数据
User.find(function(err,ret){
if(err){
console.log('查询失败')
}else{
console.log(ret);
}
})
//单独查一个条数据(数组)
User.find({
username:'zs'
},function(err,ret){
if(err){
console.log('查询失败')
}else{
console.log(ret);
}
})
//单独查一个条数据(数组中的第一条数据,对象)
User.findOne({
username:'zs'
},function(err,ret){
if(err){
console.log('查询失败')
}else{
console.log(ret);
}
})
//查插入的第一条数据(对象)
User.findOne(function(err,ret){
if(err){
console.log('查询失败')
}else{
console.log(ret);
}
})
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
37
38
39
# 删除数据
根据条件删除一个:
Model.findOneAndRemove(conditions,[options],[callback])
根据id删除一个:
Model.findByIdAndRemove(id,[options],[callback])
//删除
//删除所有zs用户
User.remove({
username:'zs'
},function(err,ret){
if(err){
console.log('删除失败')
}else{
console.log('删除成功')
console.log(ret)
}
})
2
3
4
5
6
7
8
9
10
11
12
# 更新数据
- 第一个参数为id
- 第二个参数为要更改的数据
- 第三个参数为回调函数
根据条件更新所有
Model.update(conditions,doc,[options],[callback])
根据指定条件更新一个:
Model.findOneAndUpdate([conditions],[update],[options],[callback])
根据id更新一个:
User.findByIdAndUpdate('5fae9d0f7b967b3b9cf51177',{
password:'123'
},function(err,ret){
if(err){
console.log('更新失败')
}else{
console.log('更新成功')
console.log(ret)
}
})
2
3
4
5
6
7
8
9
10
# Node-MySQL
# 使用Node操作MySQL数据库
安装:
npm install --save mysql
npm官网找mysql
实例:增删改查步骤一样
var mysql = require('mysql');
// 1.创建mysql数据库
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'root',
database: 'users'
});
// 2.连接mysql数据库
connection.connect();
//3.执行数据操作
connection.query('SELECT * FROM `users`', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results);
});
// NULL自动生成id
// connection.query('INSERT INTO users VALUES("NULL","admin","123456")', function (error, results, fields) {
// if (error) throw error;
// console.log('The solution is: ', results);
// });
//4.关闭连接
connection.end();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Promise
- 解决异步回调地狱嵌套
promise
,方便代码维护 - 第一个then方法接收的function就是容器中的resolve
- 调用的resolve方法就是then方法传递的function
- 调用的reject相当于调用了then方法的第二个参数
- 第二个的data为第一个then方法return的数据
- 第二个then中如果第一个then中没有return,则为undefined
- 多个then链式调用异步编程
# Promise基本语法:
let fs = require('fs')
let p1 = new Promise(function (resolve,reject){
fs.readFile('./data/hello.txt','utf8',function(err,data){
if(err){
reject(err)
}else{
//这里调用的resolve方法就是then方法传递的function
resolve(data)
}
})
})
let p2 = new Promise(function (resolve,reject){
fs.readFile('./data/hello.md','utf8',function(err,data){
if(err){
reject(err)
}else{
//这里调用的resolve方法就是then方法传递的function
resolve(data)
}
})
})
//链式调用异步编程
p1.
then(function(data){
console.log(data)
//当p1读取成功之后
return p2
},function(err){
console.log('读取文件失败了',err)
})
.then(function(data){
console.log(data)
})
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
37
# 封装Promise 版本的readFile
API:
let fs = require('fs')
function pReadFile(filePath){
return new Promise(function (resolve,reject){
fs.readFile(filePath,'utf8',function(err,data){
if(err){
reject(err)
}else{
//这里调用的resolve方法就是then方法传递的function
resolve(data)
}
})
})
}
pReadFile('./data/hello.txt')
.then(function(data){
console.log(data)
return pReadFile('./data/hello.md')
})
.then(function(data){
console.log(data)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 封装Promise版本的ajax
pGet('http://127.0.0.1:3000/students')
.then(function(data){
console.log(data)
})
function pGet(url){
return new Promise(function(resolve,reject){
let xhr = new XMLHttpRequest()
xhr.onload = function(){
resolve(JSON.parse(xhr.responseText))
}
xhr.onerror = function(err){
reject(err)
}
xhr.open('get',url)
xhr.send()
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Promise数据库操作
# Promise用户注册
//用户注册
// 1.判断用户是否存在
// 如果存在,结束注册
// 如果不存在,注册
User.findOne({
username:'aaa'
})
.then(function(user){
if(user){
//用户已存在,不能注册
console.log('用户已存在')
}else{
//用户不存在,可以注册
return new User({
username:'aaa',
password:'123',
email:'abcdefg'
}).save()
}
})
.then(function(ret){
console.log('注册成功')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23