Node.js笔记目录
- Node.js 笔记
- 1.Node.js 简介
- 2.Node创建 http 服务
- 3.Node 中的模块
- 4.Node 中的模板引擎
- 5.Node 模块加载规则
- 6.Node 中 exports 和 module.exports 的区别
- 7.npm
- 8.渲染区别
- 9.Express
- 10.修改完代码自动重启
- 11.在 Express 中使用 art-template 模板引擎
- 12.在 Express 中 获取表单 get 请求参数
- 13.在 Express 中获取表单 post 请求体数据
- 13.1 在 Express 中配置使用 `express-session` 插件
- 14.异步编程
- 15.MongoDB
- 16.mongoose
- 17.node 操作 MySQL 数据库
- 18.Promise 回调
- 19.Express 中间件
Node.js 笔记
1.Node.js 简介
-
node 不是一门语言,不是库,不是框架,它是 JavaScript 运行时环境,以前只有浏览器能解析 js 代码,现在 node 也可以
-
npm 是 世界最大的开源库生态系统,绝大多数 js 相关的包都放在了这个上面,这样做的目的是为了开发人员方便去下载
2.Node创建 http 服务
// 使用 node 构建一个 node 服务器
// 在 node 中专门提供了一个核心模块 : http
// 1.加载 http 核心模块
var http = require('http');
// 2.使用 http.createServer() 方法创建一个 http 服务器
// 返回一个 Server 实例
var server = http.createServer();
// req 请求事件处理函数,需要接受两个参数
/*
Request 请求对象
请求对象可以用来获取客户端的一些请求信息,例如请求路径
Response 响应对象
响应对象可以用来给客户端发送响应消息
*/
// 3.提供服务
// 发请求 接受请求 处理请求 发送响应
// 注册 request 请求, 当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数:回调处理
server.on('request', function(req, res) {
// console.log('收到请求了,请求路径为', req.url);
// res 对象有一个方法: write 可以用来给客户端发送响应;
// write 可以使用多次,但是最后一次要使用 end 来结束响应.否则客户端会一直等待
var url = req.url;
// 1.
// if (url == '/') {
// res.end('index page')
// } else if (url == '/login') {
// res.write('login')
// } else if (url == '/regist') {
// res.write('regist')
// } else {
// res.end('404 not found')
// }
// 2.仿商品数据
if (url === '/product') {
var product = [
{
name: '苹果1',
price: '8888'
},
{
name: '苹果2',
price: '8888'
},
{
name: '苹果3',
price: '8888'
}
]
// 注:发送响应只能是二进制数或字符串
res.end(JSON.stringify(product));
}
})
// 4.绑定端口号,启动服务器
server.listen(3000, function() {
console.log('loading...');
})
// 运行
node 文件名
3.Node 中的模块
-
核心模块
- 如 fs、http、path、os等
// fs 模块的使用 var fs = require('fs') // 读文件 fs.readFile('文件路径', function(error, data) {}) // 写文件 fs.writeFile('文件路径', '写的内容', function(error) {}) // 读文件目录 fs.readdir('文件路径', function(error, files) { // files 是一个文件数组 })
// url 模块 解析 url // url.parse('/1111') // url.parse() 是将路径解析成一个对象 第二个参数为 true 表示直接 将 "?" 后的地址(查询字符串)解析成一个对象, 在 query 中 url.parse(req.url, true)
-
第三方模块
-
自定义模块
3.1Path 路径操作模块
- path.basename
- 获取一个路径的文件名(默认包含扩展名)
- path.dirname
- 获取一个路径中的目录部分
- path.extname
- 获取一个路径的扩展名部分
- path.parse
- 把一个路径转为对象
- root根路径
- dir 目录
- base 包含后缀名的文件名
- ext 后缀名
- name 不包含后缀名的文件名
- 把一个路径转为对象
- path.join
- 进行路径拼接
- path.isAbsolute
- 判断一个路径是否事绝对路径
3.2 Node中的其它成员
在每个模块中除了 require
、exports
等模块相关API之外,还有两个特殊的成员
__dirname
动态获取 可以用来获取当前文件的绝对路径__filename
动态获取 可以用来获取当前文件的绝对路径__dirname
和__filename
是不受执行 Node 命令所属路径影响的
补充:
__dirname: 总是返回被执行的 js 所在文件夹的绝对路径
__filename: 总是返回被执行的 js 的绝对路径
在文件操作中,使用相对路径是不可靠的,因为在 Node 中文件操作的路径被设计为相对于执行 node 命令所处的路径。
所以,为了解决这个问题,只需要把相对路径变为绝对路径。
这里就可以使用 __dirname
和 __filename
变成绝对路径
在拼接的过程中,为了避免拼接错误,所以推荐多使用 path.join() 来辅助拼接
补充:模块操作路径不受影响,模块中的路径标识和文件操作中的相对路径标识不一致,模块路径就是相对于当前文件路径
4.Node 中的模板引擎
- art-template
npm install art-template -S
var template = require('art-template')
var ret = template('hello {{name}}', {
name: '你好'
})
console.log(ret); // hello 你好
5.Node 模块加载规则
- require方法加载规则
- 优先从缓存加载(不会加载重复的模块代码)
- 核心模块
- 路径形式的模块
- 第三方模块
- node_modules
- 第三方模块加载,首先会去找到这个文件下的 是否有 package.json 这个文件夹,然后再到 package.json 里面找到 main , main 就是指向的 index.js 文件,如果 没有 package.json 文件,就会默认去找 index.js. 如果都不成立,就会去上一级找 node_modules ,一直找到磁盘根目录,否则报错。
- package.json 包描述文件
- dependencies 选项的作用
6.Node 中 exports 和 module.exports 的区别
exports 和 module.exports 的区别
- 每个模块中都有一个 module 对象
- module 对象中有一个 exports 对象
- 我们可以把需要导出的成员都挂载到 module.exports 接口对象中
- 也就是:
moudle.exports.xxx = xxx
的方式 - 但是每次都
moudle.exports.xxx = xxx
很麻烦,点儿的太多了 - 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:
exports
exports === module.exports
结果为true
s- 所以对于:
moudle.exports.xxx = xxx
的方式 完全可以:expots.xxx = xxx
- 当一个模块需要导出单个成员的时候,这个时候必须使用:
module.exports = xxx
的方式 - 不要使用
exports = xxx
不管用 - 因为每个模块最终向外
return
的是module.exports
- 而
exports
只是module.exports
的一个引用 - 所以即便你为
exports = xx
重新赋值,也不会影响module.exports
- 但是有一种赋值方式比较特殊:
exports = module.exports
这个用来重新建立引用关系的 - 之所以让大家明白这个道理,是希望可以更灵活的去用它
7.npm
-
常用指令
// 安装
npm install xxx 简写 npm i xxx
// 全局安装
npm i xxx -g
// 生产环境安装(发布之后需要用到的 express、jQuery等)
npm i xxx --save 简写 npm i xxx -S
// 开发环境安装(开发要用到的 如 webpack、gulp)
npm i xxx --save--dev 简写 npm i xxx -D
// 卸载就是 把 install 改成 uninstall 或 i 改成 un
npm uninstall xxx
npm help
查看使用帮助
npm 命令 --help
查看指定命令的帮助
例如 uninstall 命令的简写,可以输入 npm unintsall --help 查看
// npm 自己升级自己
npm i npm -g
# 使用淘宝镜像
npm i cnpm -g
# 如
npm i jquery
cnpm i jquery
# 如果不想安装 cnpm 又想使用淘宝服务器来下载
npm i jquery --registry=https://registry.npm.taobao.org
# 但是每次手动这样加参数很麻烦,所以可以把这个选项加入配置文件中
npm config set registry https://registry.npm.taobao.org (淘宝)
registry http://r.cnpmjs.org/ (官方)
# 查看 npm 配置信息
npm config list
7.1 package 和 package-lock.json
npm 5以前是不会有 package-lock.json 这个文件的
npm 5 以后会生成这个文件
当你安装包的时候,npm 都会 生成或者关系 package-lock.json 这个文件
npm 5 以后的版本不需要加 --save 参数,它会自动保存依赖信息
当你安装包的时候,会自动创建或者更新 package-lock.json 这个文件
package-lock.json 这个文件会保存 node_modules 中所有 包的信息(版本,下载地址)
重新 npm 的时候会比以前快
从文件来个,有一个lock称之为锁,
这个lock用来锁定版本的
如果这个项目依赖了 1.11.1
如果你重新下载会下载最新版本,而不是1.11.1
我们目的就是希望锁住1.11.1这个版本
所以这个package-lock.json这个文件的另一个作用就是锁定版本号,防止自动升级
8.渲染区别
服务端渲染和客户端渲染的区别
- 客户端渲染不利于 SEO 搜索引擎优化
- 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
- 所以你会发现真正的网站既不是纯异步也不是纯服务端渲染出来的
- 而是两者结合来做的
- 例如京东的商品列表就采用的是服务端渲染,目的了为了 SEO 搜索引擎优化
- 而它的商品评论列表为了用户体验,而且也不需要 SEO 优化,所以采用是客户端渲染
9.Express
// 安装
npm i express -S
// 起步 简单的服务 hello
var express = require('express')
var app = express()
app.get('/', function(req, res) {
res.send('hello express')
})
app.listen(3000, function() {
console.log('running...')
})
10.修改完代码自动重启
// 安装
npm i nodemon -g
// 使用
nodemon xxx(app.js)
11.在 Express 中使用 art-template 模板引擎
安装
# 安装
npm install --save art-template
npm install --save express-art-template
配置
app.engine('html', require('express-art-template'));
使用
app.get('/', function (req, res) {
res.render('index.html', {
user: {
name: 'aui',
tags: ['art', 'template', 'nodejs']
}
});
});
如果希望改变默认的 views
视图渲染存储目录,所以
# 第一个参数 views 一定不要写错
app.set('views', path.join(__dirname, 'views'));
12.在 Express 中 获取表单 get 请求参数
在 Express 中内置了一个 API,可以直接通过 req.query 来获取
app.get('/item', function(req, res){
res.send(req.query);
});
// req.query => /item?name=1(浏览器) => {"name":"1"}
req.query
// Restful 形式的路由
// 号分隔路径,这些路径将被以对象的形式保存到req.params中
app.get('/item/:name', function(req, res){
res.send(req.params);
});
// req.params => /item/1(浏览器) => {"name":"1"}
13.在 Express 中获取表单 post 请求体数据
在 Express 中没有设置内置表单 post 请求的 API,这里我们需要使用一个第三方插件 body-parse
安装
npm install body-parser -S
配置
var express = require('express')
// 0 引包
var bodyParser = require('body-parser')
var app = express()
// 配置 body-parse
// 只要 加入这个配置。则在 req 请求对象上会多出一个属性: body
// 就可以用 req.body 来获取表单请求的数据了
// 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')
res.end(JSON.stringify(req.body, null, 2))
})
13.1 在 Express 中配置使用 express-session
插件
参考文档:https://www.npmjs.com/package/express-session
安装:
npm install express-session
配置
app.use(session({
secret: 'itcast', // 配置加密字符串, 它会在原有加密基础之上和这个字符串拼接去加密 增加安全性
resave: false,
saveUninitialized: true // 无论是否使用 Session ,默认给你一把钥匙。 false 时 往 Session 存储数据时在发一把钥匙
}))
使用
// 添加 Session 数据
req.session.foo = 'bar'
// 获取 Session 数据
req.session.foo
提示: 默认 Session 数据时内存存储的,服务器一旦重启就会丢失,真正的生产环境会将 Session 持久化处理
14.异步编程
- 回调函数
// 不成立的情况
function add() {
console.log(1)
setTimeout(function() {
console.log(2)
var ret = x + y;
return ret;
}, 1000)
console.log(3) // 到这里就执行结束了,不会等待前面的定时器,所以直接返回默认值 undefined
}
console.log(add(10, 20)) ==> undefined
// 回调函数
function add(x, y, callback) {
// callback 就是回调函数
// var x = 10
// var y = 20
// var callback = function (ret) { console.log(ret) }
console.log(1)
setTimeout(function () {
var ret = x + y;
callback(ret)
}, 1000)
}
add(10, 20, function (ret) {
console.log(ret)
})
- 基于原生 XMLHttpRequest 封装 get 方法
function get(url, callback) {
var oReq = new XMLHttpRequest();
oReq.onload = function(e) {
var arraybuffer = oReq.response; // not responseText
/* ... */
callback(oReq.responseText)
}
oReq.open("GET", url, true);
oReq.send();
}
get('url', function() {})
15.MongoDB
-
可以有多个数据库
-
一个数据库中可以有多个集合(表)(数组)
-
一个集合中可以有多个文档(表纪录)(对象)
-
文档结构灵活,没有任何限制
-
关系型数据库和非关系型数据库
- 表就是关系
- 表与表之间存在关系
- 所有的关系型数据库都需要通过 sql 语言来操作
- 所有的关系型数据库都需要在操作之前设计表结构
- 而且数据表还支持约束
- 唯一的
- 主键
- 默认值
- 非空
- 非关系型数据库非常的灵活
- 有的非关系型数据库就是 key-value 对儿
- 在 MongoDB 是长的最像关系型数据库的非关系型数据库
- 数据库 -》数据库
- 数据表 -》集合(数组)
- 表纪录 -》(文档对象)
- MongoDB 不需要设计表结构
- 也就是可以任意往里面存数据,没有结构性这么一说
-
安装
-
配置环境变量
- 小朱变量 加入 bin
查看是否成功
mongo --version
-
1.启动和关闭数据库
-
# mongodb 默认使用执行 mongod 命令所处盘目录下的 /data/db 作为自己的数据存储目录 # 所以在第一次执行该命令之前先自己手动建一个 /data/db mongod
如果想要修改默认的数据存储路径,可以:
mongod --dbpath=数据存储目录路径
停止
ctrl + c 或者点 关闭
-
2.连接数据库
#默认连接本机的服务
mongo
- 退出
#在连接状态输入 exit
exit
-
3.基本命令
show dbs
- 查看显示所有数据库
db
- 查看当前操作的数据库
use 数据库名称
- 切换到指定的数据库(如果没有会新建)
- 插入数据
-
4.在 Node 中如何操作 MongoDB 数据
- 使用官方包来操作
-
5.使用 mongoose 来操作 MongoDB 数据库
- 第三方包 mongoose 基于 MongoDB
16.mongoose
官方指南
设计 Schema 发布 Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// 1. 连接数据库
// 指定连接的数据库不需要存在,当你插入第一条数据后会自动创建
mongoose.connect('mongodb://localhost/test')
// 2.设计文档结构(表结构)
// 字段名称就是表结构中的属性名称
// 约束是为了保证数据的完整性,不要有脏数据
var userSchema = new Schema({
username: {
type: String,
require: true // 必须有
},
password: {
type: String,
require: true
},
email: {
type: String,
require: true
}
});
// 3.将文档结构发布为模型
// mongoose.model 方法就是同来将一个架构发布为 model
// 第一个参数:传入一个大写名词单数字符串用来表示你的数据库名称
// mongoose 会自动将大写的名词字符串生成 小写复数 的集合名称
// 例如 User 会变成 users 集合名称
// 第二个参数: 架构 Schema
// 返回值:模型构造函数
var User = mongoose.model('User', userSchema)
添加数据
// 4.当我们有了模型架构函数之后,就可以用这个构造函数对 users 集合中的数据去操作
var admin = new User({
username: 'admin',
// password: '123456',
email: 'admin@admin.com'
})
admin.save(function (err, res) {
if (err) {
console.log('保存失败');
} else {
console.log('保存成功');
}
})
查询数据
查询所有
// 按条件查 find 返回的是一个数组
User.find(function (err, res) {
if (err) {
console.log('查询失败');
} else {
console.log(res);
}
})
按条件查询所有
// 按条件查 find 返回的是一个数组
User.find({
username: 'zs'
}, function (err, res) {
if (err) {
console.log('查询失败');
} else {
console.log(res);
}
})
查询单个
// 按条件 查单个 findOne 返回的是一个对象
User.findOne({
username: 'zs'
}, function (err, res) {
if (err) {
console.log('查询失败');
} else {
console.log(res);
}
})
// 没接条件默认返回 第一个
User.findOne(function (err, res) {
if (err) {
console.log('查询失败');
} else {
console.log(res);
}
})
删除数据
根据条件删所有
User.remove({
username: 'zs'
}, function (err, res) {
if (err) {
console.log('删除失败');
} else {
console.log('删除成功');
}
})
根据条件删一个
Model.findOneAndRemove(conditions, [options], [callback])
根据 id 删一个
Model.findByIdAndRemove(id, [options], [callback])
更新数据
更新
Model.update(conditions, doc, [options], [callback])
根据指定条件更新一个
// 根据指定条件更新一个
Model.findOneAndUpdate([conditions], [update], [options], [callback])
根据 id 更新一个
User.findByIdAndUpdate('5e51cb6a9bcc0044c8d66e35', {
password: 123
}, function (err, res) {
if (err) {
console.log('更新失败');
} else {
console.log('更新成功');
}
})
17.node 操作 MySQL 数据库
安装
npm install mysql
模板
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret',
database : 'my_db'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
connection.end();
18.Promise 回调
callback hell 回调地狱
无法保证代码执行的顺序
通过回调保证代码执行的顺序
为了解决以上编码方式带来的问题(回调地狱),ES6 新增一个 API(Promise)
- ES6Promise
- callback hell 回调地狱
- 回调函数中套了回调函数
- Promise(EcmaScript 6 中新增了一个语法 API)
- 容器
- 异步任务(pending)
- resolve
- reject
- then 方法获取容器的结果(成功的,失败的)
- then 方法支持链式调用
- 可以在 then 方法中返回一个 promise 对象,然后在后面的 then 方法中获取上一个 then 返回的 promise 对象的状态结果
Promise 基本语法
// 在 ES6 中新增了一个 API Promise
// Promise 是一个构造函数
var fs = require('fs')
// console.log(1)
// 创建一个 Promise 容器
// 1. 给别人一个承诺
// Promise 容器一旦创建,就开始执行里面的代码
// 承诺本身不是异步,往往都是封装一个异步任务
var p1 = new Promise(function (resolve, reject) {
// console.log(2)
fs.readFile('./data/a.txt', 'utf8', function (err, data) {
if (err) {
// 失败了, 承诺容器中的任务失败了
// console.log(err)
// 把容器的 pending状态变为 rejected
// 调用 reject 就相对于调用了 then 方法的第二个参数函数
reject(err)
} else {
// console.log(3)
// 承诺容器中的任务失败了
// console.log(data)
// 把容器的 pending 状态改为成功
// 也就是说这里调用的 resolve 方法实际上就是 then 方法传递的那个 function
resolve(data)
}
})
})
// p1就是那个承诺,
// 当 p1 成功了 然后 then() 做指定的操作
// then 接收的 function 就是容器中的 resolve 函数
p1
.then(function (data) {
console.log(data)
}, function (err) {
console.log('读取文件失败了', err)
})
封装 Promise readFile
API
var 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(data)
}
})
})
}
pReadFile('./data/a.txt')
.then(function (data) {
console.log(data);
return pReadFile('./data/b.txt')
})
.then(function (data) {
console.log(data);
})
开启数据接口
json-server --watch data.json
19.Express 中间件
1.中间件
中间件的本质就是一个请求处理方法,我们把用户从请求到响应的整个过程分发到多个中间件中去处理,这样的目的是提高代码的灵活性,动态可扩展的。
中间也是一个方法,它有三个参数 req, res, next
- 同一个请求所经过的中间件都是同一个请求对象和响应对象
1.1 应用程序级别中间件
万能匹配(不关心任何请求路径和请求方法)
app.use(function (req, res, next) {
console.log('time', Date.now())
next() // 执行下一个中间件,也是有匹配规则的
})
只是以 '/xxx/'开头的
app.use('/a', function (req, res, next) {
console.log('time', Date.now())
next()
})
1.2 路由级别中间件
get:
app.get('/', function (req, res, next) {
res.send('hello world')
})
post:
app.post('/', function (req, res, next) {
res.send('Got a post request')
})
put:
app.put('/user', function (req, res, next) {
res.send('Got a put request')
})
delete:
app.delete('/user', function (req, res, next) {
res.send('Got a delete request')
})
1.3 错误处理中间件
app.use(function (err, req, res, next) {
console.log(err.stack)
res.status(500).send('Something broke!')
})
1.4 内置中间件
- express.static
- express.json
- express-urlencoded
1.5 第三方中间件
- body-parser
- compression
- cookie-parser
- morgan
- response-time
- serve-static
- session