Go to comments

Node.js全栈开发

2020年5月24日渡一袁老师“nodejs集训营”笔记


一、初识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使用自己的变量,定义相同的变量名,不会互相干扰

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

path.resolve(...pathsegments);


path 模块返回一个对象,该对象里有很多方法,其中 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

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连接


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是大部分场景下客户端和服务器的通信协议,它规定了双方传输的内容格式和方式







Leave a comment 0 Comments.

Leave a Reply

换一张