NodeJS 全栈开发
渡一袁老师“nodejs集训营”笔记
时间 2020年5月24日
一、初识nodejs
设置 VScode 终端
1. 文件 - 首选项 - 设置 - 输入 terminal:explo(terminal 终端的意思)
2. 选择 external 表示外部终端(默认的是集成终端)
3. 然后右键“在终端中打开”
1、node
袁老师课程里面的node版本
node -v 12.14..0
npm -v 6.13.4
浏览器端的全局对象是 window,
nodejs 的全局对象是 global,该对象上面有很多属性
console.log(global);
下面这些不属于 ES 语言标准,是 node 提供的一些和 window 里面一模一样的全局对象,全局对象都是可以直接用的
global.console
global.setTimeout
global.setInterval
2、初始 CommonJS
CommonJs 规范的内容
1. 一个js文件就是一个模块
2. 模块内所有变量均为局部变量,不会污染全局
比如,
定义一个全局变量a var a = 3;
不会污染 global 全局变量 console.log(global.a) 输出 undefined
3. 导出使用 exports.xxx = xxx 或 module.exports.xxx = xxx 或 this.xxx = xxx
4. require 函数导入其他模块
文件结构
|- my-first-node
|- a.js
|- index.js
导出
exports
// a.js exports.a = 3; // 相当于导出一个对象 {a:3} exports.b = 123; // 再导出一个,相当于 {a:3, b:123}
module.exports
// a.js module.exports = 3; // 导出的是一个数字3
module.exports 往往只写一次,比如又重新赋值导出一个对象
// a.js module.exports = 3; module.exports = { // 相当于导出一个对象 a: "hello" }
还有一种方式 this
this 跟 exports 差不多,它很少很少使用
// a.js this.a = 3; // 导出一个对象 {a:3} this.b = 123; // 上面导出的a不变,又导出一个b {a:3, b:123}
导入
require 把 a.js 模块执行一次,把导出的返回
// index.js var result = require("./a.js"); var a = 3; // index.js模块使用自己的变量,定义相同的变量名a,不会互相干扰 console.log(result);
a.js 模块导出一个函数,函数名是 sum
index.js 模块导入时,接收的变量可以是任何名字
// a.js console.log("a模块执行了"); module.exports = function sum(a ,b){ return a + b; } // index.js var result = require("./a"); console.log(result);
3、node 实现 commonJS 规范的原理
node 实现 commonJS 规范的原理,是通过 require 把每个模块的内容放到一个函数环境中执行
// 下面是 requre 的伪代码 function require(modulePath){ // modulePath模块路径 var moduleId = getModuleId(modulePath); // 获取模块的绝对路径 // 是否有缓存(缓存是模块导出的结果) if(cache[moduleId]){ return cache[moduleId]; // 有缓存直接返回缓存结果,就什么也不做了 } // 没有缓存 // 辅助函数,用于执行一个模块 function execModule(exports, require, module, __filename, __dirname){ // module 用户导出的对象 // exports 用户导出的对象 // 导入的模块所在目录的绝对路径 // 导入的模块的绝对路径 // require 是函数本身 // 这里是导入模块a.js的代码 } var module = { exports: {}, // 把这个对象当做下面call里面的对象 } // 使用call执行辅助函数 execModule.call( module.exports, module, module.exports, 模块目录的绝对路径, // d:\xx\xxx 模块绝对路径 // d:\xx\xxx\.a.js ); // 模块执行完 cache[moduleId] = module.exports; // 把module.exports保存在缓存里 return module.exports; }
require 函数原理
1. require(modulePath){} 首先根据 require 的参数,获取模块的id
id就是模块的绝对路径 "C:\Users\电脑名\Desktop\nodeStu\a.js"
2. 接下来看一个全局的对象,它是一个缓存,
var cache = {
"C:\\Users\\电脑名\\Desktop\\nodeStu\\a.js": {a:3, b:123}
}
模块的 id 作为该对象的属性名,属性名是模块路径,斜线 \ 需要转义一下 \\,
属性值是模块导出的结果
3. 看一下模块的 id 有没有缓存( if(cache[moduleId]) )
如果有缓存直接导出缓存的结果,什么都不做了,
但是一开始没有缓存的
4. 没有缓存,进行下面的逻辑,先不看辅助函数 execModule
创建一个 module 的对象,该对象里面有一个 exports 的属性是一个 {}
5. 接下来使用 call 方式调用辅助函数 execModule
execModule.call(
把 module.exports 对象作为this传进去了
第一个参数是整个 module
第二个参数是 exports(第二个参数和 this 是一样的)
第三个参数是模块目录的绝对路径 C:\Users\summer\Desktop\nodeStu
第四个参数是模块绝对路径 "C:\Users\summer\Desktop\nodeStu\a.js"
)
6. 辅助函数 execModule 里面执行的,就是导入的模块 a.js 的代码
这就解释了,
模块里面为什么可以使用 module,exports,和this(this是call绑定的),
而且模块里面还可以使用 __filename, __dirname
模块里面的代码,实际是在一个函数里面执行,
而函数环境里面有 exports, module, __filename, __dirname 这四个参数
模块里面的代码,实际是在在一个函数里面执行,而函数里面就有上面说的四个参数
console.log(__dirname); console.log(__filename);
如果在模块里面输出 arguments 长度的结果是5,实际还有一个参数,袁老师忘了
console.log(arguments.length); // 5
依次打印出的是 execModule 函数的参数,袁老师忘的参数是 require 函数本身
// a.js console.log(arguments[4]); console.log(arguments[3]); console.log(arguments[2]); console.log(arguments[1]); // require函数本身 console.log(arguments[0]);
为什么要把 require 函数传进去呢?
因为a.js模块里面有可能要导入别的模块,要用到require函数,不传进去,函数找不到 require 函数
入口模块 index.js 也是当成一个模块执行的,也是在一个函数环境里面执行的,也是通过这种方式,把require传给我们的,是nodejs底层的处理方式
require、__filename 这些都不是全局函数,
不在全局对象 global 里面,为什么这些不是全局变量,却能够直接执行呢?就是因为这些在一个函数函数里面,这是全是函数的参数
console.log(global.require); // undefined console.log(global.__dirname); // undefined
7. 模块执行完后
把 module.exports 保存到缓存 cache[moduleId] 里面
模块的绝对路径就是模块 id,作为缓存的属性
值是模块导出的结果
下一次在执行 require 导入的时候,在一开始就直接进入判断了,有缓存就直接返回缓存的结果
4、面试题
1. require导入了6次,123输出几次?
// a.js console.log(123); // index.js require('./a.js'); require('./a.js'); require('./a.js'); require('./a.js'); require('./a.js'); require('./a.js');
只打印一次,第二次有缓存了,同一个模块是有缓存结果的
2. 导入a模块的有几个属性?
// a.js exports.a = 3; exports.b = 4; module.exports.c = 3; // index.js var result = require("./a"); console.log(result);
有三个属性 {a:3, b:4, c:5}
开始给 exports 对象加了 a, b 两个属性,module.exports 是一个对象
var module = {
exports: {a:3, b:4}
}
又通过 module.exports 给对象加了一个属性 c
var module = {
exports: {a:3, b:4, c:5}
}
最终返回的,缓存都是 module.exports
3. 导出几个属性?
// a.js exports.a = 3; exports.b = 4; module.exports = { c: 5 }; // index.js var result = require("./a"); console.log(result);
导出一个 {c:5}
开始给对象里面加了 a, b 两个属性,
然后通过 module 找到 exports 给它重新赋值了一个新的对象,
之前加的两个属性 a b 就没有用了
4. 导出几个属性?
// a.js module.exports = { c: 5 }; exports.a = 3; exports.b = 4; // index.js var result = require("./a"); console.log(result);
导出的是一个对象 {c:5}
因为,一开始 module.exports 就指向了一个新对象,
exports 指向的还是之前的对象,
而最终导出的是 modules.exports
总结
只要给 module.exports 重新赋值,就跟 exports 毫无关系了,
execModule 函数的 this 绑定的也是 exports,所以跟 this 也没有关系了
5. result.a 输出的是什么?
// a.js文件 module.exports = function(){} exports.a = 2; exports.b = 3; // index.js const result = require('./a'); console.log(result.a);
输出的是 undefined
因为导出的是 module.exports 是一个函数,和之前的 exports 没有关系了
6. a.js文件里面什么都没有,index.js里面导出的什么?
// a.js文件 // module.exports = function(){} // exports.a = 2; // exports.b = 3; // index.js const result = require('./a'); console.log(result);
输出的是空对象 {}
5、模块的查找
1. 模块路径以 ./ 或 ../ 开头,是从当前的模块路径查找
2. 如果模块路径不是以 ./ 或 ../ 开头,有两种情况
第一种 看一下是不是内置模块
第二种 是否在 node_modules 目录中
第一种情况看一下是不是内置模块,比如 fs 是 nodejs 自带的内置模块
var fs = require("fs"); console.log(fs); // 打印一个对象,对象里面有很多属性
第二种情况看是否在 node_modules 目录中
require("abc");
首先 abc 不是内置模块,会当做第三方模块,
所有的第三方模块,统一在 node_modules 目录里面
abc.js 文件 console.log("abc");
先找一下 node_modules 里面没有 abc.js ,如果有 node_modules/abc.js 就找到输出 "abc"
abc/index.js 文件 console.log("abc index");
如果没有 abc.js 会找 node_modules下面 abc 的目录,
找到 node_modules/abc 目录下面的 index.js 文件,
这叫缺省文件名,就是不写文件的名字,写目录的名字,会自动去找目录下面的 index.js
如果都同时存在
node_modules/abc.js
node_modules/abc/index.js
一定是文件优先 abc.js,这是一个软件设计的原则,更加精确的东西,优先级是更高的
6、省略
如果模块路径中省略了后缀名,默认后缀名就是 .js,可以不写.js
如果模块路径中省略了文件名,文件名默认是 index.js
比如 require("abc")
找不到 abc.js,就找 abc 目录下面的 index.js
7、npm 的使用
首先修改源
npm config set registry https://registry.npmmirror.com
npm init 初始化,生成package.json 文件
npm init -y 如果目录没有中文空格,就是纯英文加短横线,也可以用这种方式,所有的保持默认
初始化其实就是帮我们生成一个 package.json,我们手动加这个文件也可以
npm i lodash 安装依赖
npm i 还原依赖
npm i -D 表示开发依赖
npm i --production 安装 package.json 记录的第三方库,但不安装 devDependencies
为了让 vscode 更好的对 node 代码进行智能提示,建议安装第三方库 @types/node
npm i -D @typs/node
8、内置模块
|- nodeStu
|- sub
| |- a.js
|
|- test.txt 《邓哥是个好人》
|- index.js
path.resolve(...pathsegments);
1. path 模块返回一个对象,该对象里有很多方法,
2. 其中 resolve() 方法可以把多个路径拼接成一个路径,返回一个字符串
var path = require("path"); var result = path.resolve("./sub", "a.js") console.log(result); // C:\Users\summer\Desktop\nodeStu\sub\a.js
注意
path.resolve("./sub", "a.js")
注意 "./sub" 这个 ./ 的含义是当前活动目录,就是命令行的路径,
比如在 Desktop 运行 C:\Users\summer\Desktop>node nodeStu/index.js
返回的路径和上面不一样了 C:\Users\summer\Desktop\sub\a.js
获取某一个模块的绝对路径,比如,找 a.js 的绝对路径
__dirname 表示当前 index.js 所在的目录,
然后从该目录出发去找 sub/a.js
var path = require("path"); var result = path.resolve(__dirname, "sub/a.js"); // "./sub/a.js" 这个 ./ 是以 __dirname 为出发点,当然也可以不写这个 ./ console.log(result); // C:\Users\summer\Desktop\nodeStu\sub\a.js
这样的好处是,
1. 在任何目录运行,
C:\Users\summer\Desktop\nodeStu>node index
C:\Users\summer\Desktop>node nodeStu/index.js
2. 都得到一样的结果
C:\Users\summer\Desktop\nodeStu\sub\a.js.js
内置模块fs
fs.readFile(path, callback) 读取文件内容
path 文件的绝对路径
callback 因为读文件有一个过程,要等读硬盘,所以用回调函数的方式,而不是返回值
err 表示读的文件有没有错误
content 读取文件的内容
var fs = require("fs"); var path = require("path"); var filename = path.resolve(__dirname, "test.txt"); fs.readFile(filename, "utf-8", function(err, content){ console.log(content); })
如果不写第二个参数文件编码 utf-8,输出的是一个Buffer 二进制格式
二、mongodb
数据分为三大类
1. 关系型数据库 mysql、sqlserver、oracle等
2. 非关系型数据库 mongodb、redis等,最近几年慢慢流行起来的
3. 面向对象数据库 db4o,非常非常少见,一般超大型的系统会用的
非关系型数据库的特点
1. 大量数据的存取速度快,
速度是关系型数据库的好几倍,甚至几十倍,
存数据,取数据的速度是非常快的,效率非常高
2. 使用简单,学习成本低
3. 难以表达复杂的数据关系
非关系型数据库又分为很多子类别
比如键值对型,文档型等,mongodb是非关系型数据库中的文档型数据库
1、安装 mongodb
https://www.mongodb.com
Community Server 下载社区免费版本
Enterprise Server 商业版本
安装过程中把下面这个钩去掉
Install MongoDB Compass 这是一个UI可视化管理器,不然下载会非常慢
默认安装目录 c:/Program Files/MongoDB/Server/4.2/bin
mongo.exe 双击运行,在弹出窗口就可以写代码了
>show dbs 查看数据库,安装后默认有三个数据库
amdin
config
local
我用的是绿色版 32位 v2.4.6
下载地址 https://xiazai.zol.com.cn/detail/44/433223.shtml
启动方式
mongod.exe --install --logpath=D:\SoftWare\mongodb\mongodb2.4.6\log\log.txt --dbpath=D:\SoftWare\mongodb\mongodb2.4.6\datadb
Tue Oct 08 06:39:16.231 Tue Oct 08 06:39:16.231 warning: 32-bit servers don't have journaling enabled by default. Please use --journal if you want durability. Tue Oct 08 06:39:16.231 Tue Oct 08 06:39:16.231 Trying to install Windows service 'MongoDB' Tue Oct 08 06:39:16.231 Service 'MongoDB' (Mongo DB) installed with command line 'D:\SoftWare\mongodb\bin\mongod.exe --logpath=D:\SoftWare\mongodb\log\log.txt --dbpath=D:\SoftWare\mongodb\datadb --service' Tue Oct 08 06:39:16.231 Service can be started from the command line with 'net start MongoDB'
net start mongodb 启动
net stop mongodb 停止
mongedb 管理工具 Robo 3T
https://robomongo.org/
以前点击 Download Robo 3T 下载,现在变成 Download Studio 3T today
也可以不安装管理工具,找到 mongedb/bin 目录下运行 mongo.exe 文件,直接输入 mongodb 语句
use test; --查看当前数据库的集合 show collections -- 插入文档 -- db.集合名称.insert(document); -- db.stu.insert({name:'gj',gender:1}); -- 查看 -- db.getCollection(集合名).find({}); db.getCollection('users').find({}); db.getCollection('news').findById('6678be660da3271fdcf49149'); -- 删除users集合 -- db.集合名.drop(); db.user.drop();
附:
https://blog.csdn.net/weixin_43512977/article/details/131777349 增删改查基本操作
https://blog.csdn.net/u012130609/article/details/76228518 基本增删改查语法,写的比较简洁
https://www.jianshu.com/p/cf0c616e6189 使用手册
https://blog.csdn.net/qq_38280242/article/details/123851413 文章里面管理工具的ui设计感不错
https://blog.csdn.net/weixin_54607676/article/details/124541427 官方的mongDB连接
解决无法启动的问题
https://developer.aliyun.com/article/1086736
不同的 Mongoose 版本,对应不同的依赖
https://www.midwayjs.org/en/docs/2.0.0/extensions/mongo
PHP下的管理工具 phpMoAdmin-MongoDB
https://blog.csdn.net/gitblog_00315/article/details/141881343
2、核心概念
db:数据库
collection:集合,就一个仓库里面可以存放各种集合
document:文档,每个集合中的文档,类似于js中的对象,而且每个对象不一定是一样的
Primary key 主建,每个文档的唯一编号,类似于身份证
field:字段,文档中的字段,类似于对象中的属性
db数据库、collection集合、document文档,它们之间的关系
|- mongodb 里面可以创建很多数据库
|
| db 数据库,可以创建很多数据库
|- movie 电影数据库
|- schooll 学校数据库
|
| collections 集合,一个数据库里面可能会有很多的集合
|- books 书籍的集合
|- teachers 老师的集合
|
| documents 文档,每一个集合里面有很多的文档
|- {
name: "成哥",
age: 10,
students: [...],
address: {...},
}
{
name: "邓哥",
age: 90,
students: [...],
address: {...},
loves: ["香菜", "秋葵"] 每个文档不一定是一样的,邓哥就多了loves
}
3、在 node 中使用 mongodb
在node中使用mongodb需要安装一个第三方库 mongoose
npm i mongoose@4 mongoeb v2.4.6 必须安装 mongoose4 的版本
袁老师的课程中的版本
mongodb 4.2.7
mongoose 5.9.15
我用的版本
node v12.22.12
mongodb v2.4.6
mongoose ^4.13.21
Ps:
cmd控制台有卡住的情况,右击菜单【属性】-【选项】-【快速编辑模式】去掉勾
通过mongoose 操作数据库有三个步骤
1. 创建连接
2. 定义 Schema 和 Model
3. CRUD 操作数据库,增删改查
示例代码
转 https://blog.51cto.com/u_16213417/7419089
const mongoose = require('mongoose'); // 建立连接 mongoose.connect('mongodb://localhost/my_database', { useNewUrlParser: true }); // 定义数据模型 const UserSchema = new mongoose.Schema({ name: String, age: Number, }); // 创建数据模型 const User = mongoose.model('User', UserSchema); // 查询数据 User.find({}, (err, users) = > { if (err) throw err; console.log(users); } ); //关闭连接 mongoose.connection.close();
4、下面是课程中实现的功能
|- project
|- models 跟数据库相关的模型
| |- createConnection.js 创建连接,执行一遍就完事了,不要导出任何东西
| |- News.js
| |- User.js
| |- index.js
|
|- services 封装一些函数,可以用这些函数方便的操作数据库
| |- serviceUser.js
| |- serviceNews.js
| |- index.js
|
|- index.js
1. 创建连接
主要这两句代码
mongoose.connect("mongodb://localhost/test") 连接数据库test,如果没有会创建一个
mongoose.connection.on("open", callback) 当连接成功的时候运行回调函数,表示连接已经打
该模块不需要导出任何东西
models/createConnection.js
var mongoose = require("mongoose"); mongoose.set("useCreateIndex", true); // 新版本对索引的处理方式有所变化,无此代码会有警告 mongoose.Promise = global.Promise; // 自己加的 mongoose.connect("mongodb://localhost/test", { useMongoClient: true, // 自己加的 useNewUrlParser: true, // 新版本对连接字符串的解析有更好的支持,无此代码会有警告 useUnifiedTopology: true, // 新版本对数据库的监事引擎有更好的支持,无此代码会有警告 }); mongoose.connection.on("open", () => { console.log("连接已打开"); });
2. 定义 Schema 和 Model
mongodb 里面有这两个概念,只不过比较弱,在 mongoose 库里面把这两个概念加强了
1. Schema 大概是结构的意思
2. Model 模型
Schema 和 Model 的关系
通过一个 Schema 结构去组成一个 Model 模型,这个模型 Model 对应的是文档 document
什么是模型 Model?
1. 有哪些属性
2. 属性的类型
3. 属性有什么样限制
先通过 Schema 定义模型
1. mongoose 里面的一个构造函数 Schema,
通过 new mongoose.Schema({...}) 创建一个结构对象,
参数里面是结构的配置 loginId、loginPwd、name、age...
2. 定义好结构,返回一个结构对象 userSchema,使用该结构对象定义一个模型,
mongoose.model("模型的名字", 结构对象) 模型的名字就是集合的名字,会自动变成副数,
返回一个结果,后面增删改查都要通过这个结果,这个结果是一个构造函数
3. 后续数据库操作,只需要 User 和 News 这两个模型,所以需要导出两个模型
有 用户 和 新闻 两个模型
定义用户的模型
models/User.js
var mongoose = require("mongoose"); // 这里又导入了mongoose,因为有缓存,不会导致这个第三方库执行两次 // 定义一个用户结构 var userSchema = new mongoose.Schema({ loginId: { // 登陆账号 type: String, // 类型是字符串 required: true, // 必填 unique: true, // 属性值是唯一的,比如用户的登陆账号是唯一的 trim: true, // 写入数据时,自动的去掉首位空格 minlength: 3, // 约束:字符串的最小长度为3 maxlength: 18, // 约束:字符串的最大长度为18 }, loginPwd: { // 登陆密码 type: String, required: true, trim: true, minlength: 6, maxlength: 18, select: false, // 后续查询数据的时候,默认情况下一般不要查询密码字段,所以设置不查询该字段 }, name: { // 用户名称 type: String, required: true, trim: true, minlength: 2, maxlength: 10, }, age: { // 用户年龄 type: Number, // 类型是数字 required: true, min: 1, // 年龄的最小值为1 max: 100, // 年龄的最大值为100 }, role: { // 用户角色:管理员 / 普通用户/ VIP 这三个之一 type: String, required: true, trim: true, enum: ["管理员", "普通用户", "VIP"], // 用户角色是String字符串,值必须是这三个之一 }, }); // var User = mongoose.model("User", userSchema); // 通过定义的结构产生一个User模型,有些模型可以共用一个结构 // 由于后续用到的是模型,所以把定义的模型,该表达式的返回结果导出 module.exports = mongoose.model("User", userSchema);
袁老师说了一个问题,
用户模块 User.js 导入了一次 mongoose,
连接模块 createConncetion.js 也导入了一个mongoose ,会不会导致这第三方库执行两次?
不会的,因为有缓存,所以它只会执行一次
models/News.js
定义新闻模型
var mongoose = require("mongoose"); // 创建一个新闻的结构 var newsSchema = new mongoose.Schema({ title: { type: String, required: true, trim: true, }, content: { type: String, trim: true, }, pubDate: { type: Date, // 类型的Data required: true, default: Date.now, // 默认值,可以是一个值,也可以是一个函数 }, channel: { type: String, required: true, }, link: { type: String, required: true, }, }); // var News = mongoose.model("News", newsSchema); module.exports = mongoose.model("News", newsSchema);
default 表示默认值,可以是一个值或一个函数
default: Date.now
这是一个函数,这样写功能上,由于 default 是一个函数,
比如添加一篇新闻的时候,会调用函数,取出添加新闻时候的时间
default: Date.now()
这样写是一个值,就是把调用函数后返回的值,作为默认值
功能上,这样写时间是定义模型的事件,相当于把定义模型的时间放到默认值这里了
models/index.js
为了使用更加方便,开发的时候通常会进行一下汇总,使用模型只需要导入这个默认的文件就可以了
1. 模型要创建连接,所以肯定要执行一下连接
2. 导出 News 和 User 两个东西
require("./createConnection"); // 执行一次连接 // const News = require("./News"); 先导入 // exports.News = News; 在作为这个汇总模块导出 // 导出的exports.News,来自于 require("./News"); exports.News = require("./News"); exports.User = require("./User");
用的时候默认导入的是index.js,导入的是一个对象,可以获取 新闻 和 用户模型
/index.js
var models = require("./models"); console.log(models.User); console.log(models.News);
3. CRUD
CRUD(Create, Retrieve, Update, Delete)简称为增删改查,是对数据库最基本的操作
新增
模型.create(对象)
1. 函数的参数可以是一个对象,可以传一个对象的数组,是新增一个和多个的区别
2. 因为js代码运行在内存,写入到磁盘拖慢了速度,所以是异步的
3. 新增的对象会自动添加两个属性
_id:自动生成,用于表示文档的主键,全球唯一
__v:自动生成,用于表示文档的版本,内部维护,不需要开发者处理
create() 函数创建一个用户信息,当创建完成之后,会运行一个回调函数,该函数有两个参数
err 表示创建中有没有错误
result 表示创建成功的用户对象
/index.js
var models = require("./models"); var mongoose = require("mongoose"); models.User.create( { loginId: "abcabcefg", loginPwd: "1234567", name: "Monica", age: 18, role: "普通用户", }, function(err, result){ if(err){ console.log(err); mongoose.connection.close(); }else{ console.log("创建完成", result); mongoose.connection.close(); } } );
创建用户成功,返回用户对象,
{
_id: 6677ae7d45313d27a85a8d28,
role: '普通用户',
age: 18,
name: 'Monica',
loginPwd: '1234567',
loginId: 'abcabcefg',
__v: 0
}
自动生成的两个属性
_id: 6677ae7d45313d27a85a8d28 表示文章的主键,根据 时间戳 + MAC + 进程编号 + 自增量 保持全球唯一,绝对不会重复
__v mongoDB 内部它自己控制的,新增是0,后面有了一些操作1 2 3...
ES7 的方式
var models = require("./models"); var mongoose = require("mongoose"); async function test(){ try{ var result = await models.User.create({ loginId: "chenggee", loginPwd: "123456", name: "Monica", age: 18, role: "普通用户", }); console.log(result); mongoose.connection.close(); }catch(err){ console.log('错误代码:' , err); mongoose.connection.close(); } } test();
MongooseError [ValidationError]: User validation failed: role: Path `role` is required
ValidationError 表示验证错误,
定义模型的时候,有一个大堆规则,role 必须要填写
Error [MongoError]: E11000 duplicate key error index: test.users.$loginId_1 dup key: { : "abcabce" }
重复提交报错
因为 loginId 字段是唯一的
批量导入测试数据,先导入两个 json 文件
var models = require("./models"); var mongoose = require("mongoose"); var users = require("../db/users.json"); var news = require("../db/news.json"); async function addUsers(){ try{ var result = await models.User.create(users); // 参数是一个数组,数组的每一位是对象 console.log('添加用户测试数据成功'); mongoose.connection.close(); }catch(err){ console.log('添加用户测试数据失败'); mongoose.connection.close(); } } async function addNews(){ var result = await models.News.create(news); // 同上 console.log('添加新闻测试数据成功'); mongoose.connection.close(); } // addUsers(); addNews();
查询
袁老师讲了三种查询方式
1. 模型.findById(id)
据id字符串查询单个文档,查询不到返回null
var models = require("./models"); var mongoose = require("mongoose"); async function test(){ var result = await models.News.findById('6678be660da3271fdcf4913d'); console.log(result._doc); mongoose.connection.close(); } test();
2. 模型.find(filter, [projection], [options])
根据 投影、配置 条件过滤,可以查询多个
过滤的条件是一个对象,下面是常见的几种写法
// 查询“财经焦点”的频道的新闻 { channel: "财经焦点" } // 频道是“财经焦点”,并且要title中包含的“中国”的新闻 { channel: "财经焦点", title: /中国/, // 这是正则表达式,模糊查询 } // 查询“财经焦点”的频道的新闻,或者title中包含的“中国”的新闻 { $or:[ { channel: "财经焦点", }, { title: /中国/, }, ] } // 查询所有 发布日期 大于等于 昨天此时 的新闻 // $gt大于 $gte大于等于 $lt小于 $lte小于等于 $ne不等于 // $in其值在某个数组中 $nin其值不在某个数组中 { pubDate: { $gte: Date.now() - 3600 * 24 * 1000, } }
查询结果是一个数组,数组里面是一个一个的对象
var models = require("./models"); var mongoose = require("mongoose"); async function test(){ var result = await models.News.find({ channel: "财经焦点", }); console.log(result); console.log(result.length); mongoose.connection.close(); } test();
或者关系(默认是并且关系),channl:财经焦点 或者 title包含“中国”,要么是财经焦点,要么是标题里面包括中国
async function test(){ var result = await models.News.find({ $or:[ { channel: "财经焦点", }, { title: /中国/, }, ] }); console.log(result); console.log(result.length); mongoose.connection.close(); }
$gte大于等于,
Date.now() - 3600 * 24 * 1000 当前时间戳减一天的毫秒数,
就是发布日期要大于昨天的时间,昨天之后发布的
async function test(){ var result = await models.News.find({ pubDate: { $gte: Date.now() - 3600 * 24 * 1000, } }); console.log(result); console.log(result.length); mongoose.connection.close(); }
第一个参数 filter 是对象,
第二个参数 [projection] 是字符串,表示在查询结果中进行投影投影,就是获取想要的字段
"title pubDate" // 仅查询_id title pubDate "-content" // 除了 content 都要查询
下面只查询两个字段的数据,id会自动加进了
async function test(){ var result = await models.News.find({ $or:[ { channel: "财经焦点", title: /中国/, }, ] }, "title pubDate" ); console.log(result); console.log(result.length); mongoose.connection.close(); }
[options]一些额外的配置,相对于mysql的 by pubDate desc limit 0, 2
async function test(){ var result = await models.News.find( {}, "title pubDate", { skip: 0, // 跳过0条 limit: 2, // 取出两条,如果值为1就是最新的一条新闻 sort: "-pubDate", // 按照发布时间排序,+pubDate生序(默认),-pubDate降序 } ); console.log(result); console.log(result.length); mongoose.connection.close(); }
3. 模型.count() 查数量
袁老师的版本里面改成 countDocuments()
async function test(){ var result = await models.News.count(); console.log(result); // 120 mongoose.connection.close(); }
可以跟条件,比如财经焦点频道的新闻数量
async function test(){ var result = await models.News.count({ channel: "财经焦点", }); console.log(result); // 25 mongoose.connection.close(); }
更新
模型.updateOne(filter, doc) 更新单个文档
模型.updateMany(filter, doc) 更新多个文档
filter 和查询中filter含义完全一样
doc 更新的新文档的属性
更新 id 为 '6678be660da3271fdcf4918e' 这篇新闻的 title 标题
返回 {n:1, nModified:1, ok:1}
var models = require("./models"); var mongoose = require("mongoose"); async function update(){ var result = models.News.updateOne( { _id: '6678be660da3271fdcf4918e', }, { title: "oo《信条》主演:剧本太复杂真的看不懂oo", } ); console.log(result); mongoose.connection.close(); } update();
updateOne 条件匹配多个,它也更新也
updateMany 是条件匹配多少,就更新多少了
删除
模型.deleteOne(filter) 删除单个
模型.deleteMany(filter) 删除多个
4、练习
编写下面两个模块,实现对应的函数
userService 模块
newsService 模块
services/userService.js
const User = require("../models").User; // 注册一个用户 // userObj:用户对象 // 返回:新注册的用户对象 exports.reg = async function(userObj){ const result = await User.create(userObj); return result; } // 登录 // loginId: 账号 // loginPwd: 密码 // 返回:登录成功返回用户对象,登录失败返回null exports.login = async function(loginId, loginPwd){ const result = await User.find({ loginId, loginPwd }); if(result.length === 0){ return null; } return result[0]; // 返回数组的第一项,是用户对象 } // 查找用户 // id: 用户的唯一编号 // 返回:用户对象,用户不存在返回null exports.getUser = async function(id){ const result = await User.findById(id); if(result.length === 0){ return null; } return result; }
services/newsService.js
const News = require("../models").News; // 查询新闻,按照发布日期降序排序 // page: 页码 // limit: 页容量 // keyword: 关键字,标题、内容、频道包含该关键字均可 // 返回:查询结果对象 { total: 总数据量, datas: 新闻数组 } exports.getNews = async function(page, limit, keyword){ // 正则表达式是一个对象,这种方式可以用字符串作为正则表达式的内容 let reg = new RegExp(keyword); // 查询条件 const filter = { $or: [{title: reg}, {content: reg}, {channel: reg}] }; // 新闻数组 let datas = await News.find(filter, "title channel content", { sort: "-pubDate", skip: (page - 1) * limit, limit: limit, }); let arr = []; for(item of datas){ // console.log(item._doc) arr.push(item._doc); } // 总数量 let total = await News.count(filter); // 高版本中用 countDocuments 方法 return {total, datas:arr} } // 分页 // 1 10 skip: 0 limit: 10 // 2 10 skip: 10 limit: 10 // 3 10 skip: 20 limit: 10 // 4 10 skip: (4 - 1 ) * 10 limit: 10
services/index.js
exports.userService = require("./userService"); exports.newsService = require("./newsService");
三、express
1、模板字符串
es6模板字符串解决了两个问题
换行
拼接数据
var a = 3; var b = 4 var str = "agas" + a + "dfa\nsdf" + b + "asdf"; var es6Str = `agas${a}dfa sdf${b}asdf`; console.log(str); console.log(es6Str);
2、http协议
“服务器程序”是一个应用程序,跟QQ、微信、浏览器没有本质的区别,它就是一个程序而已,所以我们平时说的“服务器”不是一台计算机,说的是一个程序
服务器是一个程序,浏览器也是一个程序,
两个程序之间进行数据传输,我们称之为网络通信,通信的时候需要满足一个协议
什么是协议?
协议是一个标准,规定了双方怎么说话,就是双方约定的一种格式,这个格式的标准就是协议
http是大部分场景下客户端和服务器的通信协议,它规定了双方传输的内容格式和方式