node.js阶段总结

什么是node.js

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。

Node.js和浏览器区别

node.js没有BOM和DOM

简单理解下BOM和DOM

  • BOM (browser object model) 浏览器的对象模型
    • node.js不能通过js控制浏览器(因为node.js不是浏览器)
  • DOM (document object model) 文档的对象模型
    • node.js不能直接渲染页面(因为不是浏览器,没有html文档)
  • 浏览器不认识node.js内置模块的代码
常见全局变量
__dirname // 执行代码所在的目录
__filename // 执行代码的文件名称
console // 控制台对象
exports // 导出方法
require // 导入方法
global === globalThis // 全局对象,类似浏览器上的 window 对象

node.js模块

什么是模块

独立的高度封装的node.js指令集,就是一个模块。

我们常写的

const fs = require('fs')

上述代码就在引入一个叫 fs的模块

在 node.js 看来,我们每个 .js文件都是一个模块

模块规范CommonJs

CommonJs规定了如何引用和导出模块(导出:可以理解成借出代码给其他模块,导入:可以理解成借入代码给自己这个模块用)
- 语法:const 自定义变量名 = require('模块名')
- 作用:
1. 执行被加载模块中的代码
2. 得到被加载模块中 exports接口对象

  • 导出模块 exports
    • 语法:module.exports = 要导出的对象
    • 作用:为了让其他js文件能够引用到(那为什么要让其他js文件引用呢?为了内聚,减少代码量)

来实际使用一下,见《1.模块的导入导出》

exports 与 module.exports
  • exports: 希望一个模块导出多个内容时使用,exports 只是 module.exports 的引用
  • module.exports: 希望导出单个内容时使用
核心模块:

简单理解模块的概念:一个封装好的软件包,开箱即用。

fs(file system)文件系统模块(重点)

node.js中的文件系统,是一个工具,用于在操纵系统上对文件和目录进行操作,基本操作如:创建文件夹,删除文件,读取文件,写入文件等。

let fs = require('fs')
fs.readFile('./a.txt', (err, data)=>{
    if(err) { 
        console.error(err)
    } else {
        console.log(data)
        let str = data.toString()
        console.log(str)
    }
})
http协议通信模块(重点)
let http = require('http')
let server = http.createServer()
os(operating system)操作系统模块
let os = require('os')
// 获取当前几期的CPU信息的
console.log(os.cpus())
path 路径模块
let path = require('path')
//获取一个路径中的扩展名部分  extname 扩展名
console.log(path.extname('./data/hello.txt'))
event 事件模块

该模块可以创建收发事件的实体

assert 断言

定义一个笃定的言论就叫断言,多用于做函数的参数判断,例如:加函数function add(x, y) 需要两个参数,那么就可以使用断言判断调用函数时参数的合法性

cluster 集群模块

集群模块可以让js代码在多个cpu核心上运行多个副本

es模块化

es 模块化的语法 浏览器天然支持

node.js 需要较新的版本才能支持

导出模块

// export 导出时要同时进行声明
export let a = 1
export function fn(){}


// export default 导出一个内容 且 必须写在代码最下面
export default { a, fn }

导入模块
// 导入模块语法 path 路径 可以是一个文件路径 也可以是一个模块名称
// import ... from <path>

// 导入 export 的内容
import {age, sex} from './module.js'

// 导入 export default 的内容
import m1 from './module1.js'

// 同时导入 export default 和 export 的内容
import m1, {age, sex} from './module1.js'

// 使用 通配符 * 导入模块的所有内容
// as 就是 “当作” 的意思
// 下面的引入语句相当于: 将模块 m1.js 当作 m1 变量导入
import * as m1 from './m1.js'

// 若已有一个变量和模块中的内容同名
// 那么导入的时候需要添加别名
import {sex as se, age as ag} from './m1.js'

let age = 40
let sex = 'female'
node.js 上使用 es 模块化的方法

修改 package.json 文件,添加 "type": "module"

浏览器上使用 es 模块化的方法

浏览器上使用 es 模块化的条件有两个

  1. script 标签需要添加 type 属性, 如: <script type="module"></script>
  2. 网页需要通过服务器访问

express服务器

静态资源服务器

什么是静态资源?
不会发生变化的资源就是静态资源,静态资源一般指不会变化的文件

静态资源服务器:用于访问静态资源的服务器

const http = require('http');
const path = require('path');
const fsp = require('fs/promises');
const app = http.createServer();


// 定义静态资源文件夹
const staticDirPath = path.join(__dirname, 'public')

app.on('request', (req, res) => {
    // 拼接资源路径
    let assetPath = path.join(staticDirPath, `.${req.url}`)

    // 判断文件是否存在
    fsp.stat(assetPath).then((stat) => {
        // 判断当前路径是否是一个目录
        if (stat.isDirectory()) {
            res.setHeader('Content-Type', 'text/plain; charset=utf-8')
            res.end('无效文件')
            return
        }
        // 读取文件
        fsp.readFile(assetPath).then(data => {
            // data: 读出来的文件数据
            res.write(data)
        }).finally(() => {
            res.end()
        })
    }).catch(reason => {
        console.error(reason)
        res.setHeader('Content-Type', 'text/plain; charset=utf-8')
        res.end('404资源未找到')
    })
})

app.listen(80, () => {
    console.log('server start on: http://127.0.0.1')
})
重定向
const http = require('http');
const fsp = require('fs/promises');
const path = require('path')
const app = http.createServer();

app.on('request', async (req, res) => {
    if (req.url === '/') {
        // 重定向
        // 添加重定向的状态码
        res.statusCode = '302'
        // 添加重定向的路径
        res.setHeader('Location', '/1.png')
        res.end()
    } else if (req.url === '/1.png') {
        const data = await fsp.readFile(path.join(__dirname, 'public', '1.png'))
        res.end(data)
    } else {
        res.end('404')
    }
})

app.listen(80, () => {
    console.log(`server start on: http://127.0.0.1`)
})

http协议

什么是http协议

http (Hypertext transfer protocol)全称 超文本传输协议

分析理解:

  • 超文本:比文本更多的信息(图片,超链接,媒体资源等等)
  • 传输:运送
  • 协议:服务端客户端共同认可的规则

连起来理解:运送比文本更多信息的 “服务器客户端共同认可的” 规则

协议就像游戏规则,规定了服务器和客户端怎么互相聊天,怎么一起愉快的玩耍

通过浏览器直观看看http协议

打开chrome浏览器,f12或ctrl+shift+i,在network下查看请求

常用请求头和响应头

请求头就是浏览器上的 request header

响应头就是浏览器上的 response header

  • Request Method:请求方法
名称特点服务器取值方式
get传递参数在url上可见通过url.parse(req.url).query取值
post传递参数在url上不可见通过req.body取值
  • Content-Type:内容类型

有常见的以下几种类型

名称含义
text/htmlhtml文档
text/plain纯文本
image/jpeg.jpg图片
image/png.png图片
text/xmlxml文档
application/jsonjson数据
application/x-www-form-urlencoded表单url编码数据,例如:a=1&b=2&c=3
multipart/form-data上传文件时常看到
charset=utf-8指定编码集
  • Status Code:状态码

状态码只指示请求或响应状态,不对业务负责

常见状态码

代码含义
200请求成功
302资源重定向
304资源未改变,使用浏览器缓存
400客户端请求的语法错误,服务器无法理解
403权限不足 限制访问
404资源未找到
500服务器内部错误
503请求超时
  • User-Agent:访问代理

意思是什么东西访问了服务器,通常可以用作平台判断。例如:不同浏览器,该值不一样

TCP/IP协议

TCP(translate controll protocol)传输控制协议

TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议

TCP/IP 主要规定链路层、网络层、传输层、应用层

重点:

  • 网络层规定了ip地址
  • 传输层规定了端口号 port

通过ip + 端口的组合,可以找到互联网上的一个应用服务。如果在使用http协议访问网络时,不加端口号,则默认访问 80 端口。例如:访问 http://127.0.0.1/ 等价于访问 http://127.0.0.1:80/

ajax 异步请求

什么是 ajax

ajax (async javascript and xml) 翻译过来就是:异步js和xml

通常我们把 ajax 理解成:异步的网络请求

ajax 的应用场景

两种情况可以考虑使用 ajax

  1. 网页中为了发送请求的同时,允许用户继续和页面进行交互
  2. 不希望使用 form 表单,导致页面跳转
如何发起 ajax 网络请求

发起 ajax 请求的工具 我们称为 Http Client(http客户端)

使用浏览器自带的 ajax
XMLHttpRequest
// 创建 xhr
const xhr = new XMLHttpRequest()
// 打开连接
// xhr.open(requestMethod, url, async)
// requestMethod 请求方法 get 或 post
// url 请求地址
// async 是否异步 默认为 true,若为 false 则,xhr.send 在收到服务器响应后才会返回
xhr.open('post', '/play', true)
// 监听收到服务器响应事件
xhr.addEventListener('load', ev=>{
    ev.currentTarget // 该对象中可以获取服务器响应数据
})
// 发送请求
xhr.send()
fetch
// fetch api: https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch#%E6%94%AF%E6%8C%81%E7%9A%84%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0

// fetch 是一个 promise 函数
// fetch(url, options)
// url 请求路径
// options 配置对象
fetch(`/doGet?name=${fd.get('name')}&age=${fd.get('age')}`, {
    method: 'GET', // 请求方法 *GET, POST, PUT, DELETE, etc.
    mode: 'same-origin', // 跨域策略 no-cors(不跨域), *cors(跨域), same-origin(同源)
    // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    // body: JSON.stringify(data) // 数据体 body data type must match "Content-Type" header
}).then(res => {
    // res 服务器的响应对象
    console.log(res)
    // res.text() 用字符串格式读取服务器返回的结果
    // 该函数返回的是 promise
    // return res.text()
    return res.json()

    // res.json() 用json对象读取返回值
    // res.blob() 用blob对象(二进制数据对象)读取返回值
}).then(text => {
    console.log(text)
})
使用一些 http clint 框架

经典的 http client 框架有:

  • jquery
  • axios

jquery 已过时,可以自行查找文档,方法非常类似 fetch

axios 比较主流的 http client

mongodb安装

MongoDB Server下载地址

MongoDB Shell下载地址

MongoDB Compass下载地址

下载 MongoDB Server
  • .msi 微软安装向导
  • .zip 压缩绿色版

区别:

  • .msi 会添加注册表并自动添加windows服务,开机自动启动 mongodb
  • .zip 程序绿色环保,但需要通过命令行启动
通过命令行启动 mongodb

启动 mongodb 前,需要先创建 mongodb 的启动配置文件

mongodb 的配置文件格式为 yaml,名称通常叫 mongod.conf

给配置文件添加内容如下:

# 网络配置
net:
   # 绑定ip
   bindIp: 0.0.0.0
   # 绑定所有可用ip
   bindIpAll: true
   # 数据库服务的端口号
   port: 27017
# 存储配置
storage:
   # 数据库保存路径
   dbPath: D:/xxx/xxx/db
# 系统日志
systemLog:
   # 目标格式
   destination: file
   # 日志存放路径
   path: "D:/xxx/xxx/log/mongod.log"
   # 追加日志
   logAppend: true
storage:
   journal:
      enabled: true
# 安全选项
security:
   # 是否开启权限校验
   authorization: disabled

关于配置文件的其他配置,可以查看此详情

配置完成后,使用命令行启动数据库

mongod --config <配置文件路径>
# 或
mongod -f <配置文件路径>
添加用户并开启权限检测
添加用户

mongodb 默认没有用户,也不会校验数据库权限

为了安全起见,需要添加用户,例如: root权限用户、数据库管理员和普通用户

请使用 mongo shell 或 mongo compass 执行以下命令

// 切换到管理员数据库
use admin
// 创建用户
db.createUser({
    user: 'root',
    pwd: 'xxxxxx',
    roles: [
        'root'
    ]
})

db.createUser详情请见

创建用户的内置角色详情请见

可以在 mongodb shell 中使用 db.createUser.help() 查看帮助信息,createUser 方法名替换成其他方法就能查看其他方法的帮助信息了

开启权限检测

mongod.conf 配置文件中添加如下配置

# 安全选项
security:
   # 是否开启权限校验
   authorization: enabled

开启权限后,若要操作数据库,需要在对应数据库下登录用户

// 切换数据库到创建用户的数据库下
use admin
// 登录
db.auth('root', 'xxxxxx')

登录完后可以操作任意数据库的数据了,例如

db.getCollection('stu').insert({name: '张三', sex: 'male'})

Mongoose

简介

官网:https://mongoosejs.com/

mongoose 是 mongodb 的 node.js 的数据库驱动

安装
npm i --save mongoose
连接数据库
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test2', { useNewUrlParser: true })

const db = mongoose.connection; // 获取数据库连接对象
db.on('error', console.error.bind(console, 'connection error:')); // 绑定连接错误事件
db.once('open', function () { // 绑定一次打开数据库连接事件
    // we're connected!
    console.log("we're connected!")
})

其中 mongodb://localhost:27017/test2是连接字串

  • localhost是数据库地址,可以换成对应的ip地址
  • 27017是数据库端口
  • test2是数据库库名

也可以追加登录数据库的用户名和密码,如:mongodb://admin:111111@localhost:27017/test2

admin: 账号
111111: 密码

连接字串,可以参考 mongobooster 的是怎么写的。见图《如何查看数据库连接字串.png》

创建数据库表结构图

表结构图是用来设计表的对象,其定义了表字段的属性。

就像是一个工程图纸,根据图纸创建出来的就是具体的表。

语法:

const { Schema } = require('mongoose')

const gameSchema = new Schema({
    name: { type: String, index: true, unique: true }, // 创建唯一标识
    platform: { type: String, index: true }, // 创建索引
    price: { type: Number, index: true },
    createTime: { type: Date, default: Date.now(), index: true },
    updateTime: { type: Date, default: Date.now(), index: true }
    // 试试将时间的类型改成数字
    // createTime: { type: Number, default: Date.now(), index: true },
    // updateTime: { type: Number, default: Date.now(), index: true }
})

表结构中,通常都会有 createTimeupdateTime用来描述记录创建事件和修改事件

如何定义字段:

  • 直接指定类型,字段类型是必须的,所以这种是最基础的定义方法,例如:
const gameSchema = new Schema({
    name: String // 创建一个叫name的字段,类型是 String
})
  • 指定字段初始值,例如:
const gameSchema = new Schema({
    createTime: { type: Date, default: Date.now() }
})
  • 创建索引 index
    通常在建表的时候,请将需要用于查询的字段添加上索引 index

什么是索引呢?数据库有一个字典,如果创建索引,数据库会在字典上做记录,那么查询的时候,数据库会查询得更快。

const gameSchema = new Schema({
    name: { type: String, index: true } // 创建一个叫name的字段,类型是 String
})
  • 创建唯一索引 unique
    唯一索引是指数据库的某个字段,不接受重复的值。例如有个 name字段添加了唯一索引,那么这个表里面,name是不能重复的。
const gameSchema = new Schema({
    name: { type: String, unique: true } // 创建一个叫name的字段,类型是 String
})
创建数据模型

数据模型对应的就是数据库中的表,当我们基于数据模型进行数据保存的时候,如果数据库还不存在这张表,那么就会自动创建表。

所有数据库操作,都可基于数据库模型进行操作的

const { model } = require('mongoose')

const Game = model('game', gameSchema) // 第一个参数影响表名,且大小写不敏感

数据库基础操作

保存 save

保存操作用于对整个对象进行修改并保存。

代码里一顿操作猛如虎,但是不保存,就不会进入数据库

先创建用于保存的数据对象

const tlou = new Game({ // 直接 new 一个之前创建的数据模型
    name: 'The Last Of Us',
    platform: 'PS',
    price: 400
})

如果创建对象的时候有 _id字段,那么保存功能会覆盖数据库中有指定id的数据

如何理解呢?平时保存文件是,文件名相同的就会被覆盖掉。这里的 _id相当于数据对象的“文件名”,如果数据库有这个id的数据,那么保存的时候就会被覆盖掉。

保存:

tlou.save((err, tlou) => {
    if (err) console.error(err)
    else console.log(tlou)
})

保存方法,接受一个 callback: (err, savedModel) => {},其中 savedModel是保存后数据库返回的数据对象,如果是新增数据,会包含一个数据库分配的id

删除 delete

删除符合条件的数据,通常以id为查询条件进行删除,当然也能以其他字段为查询条件进行删除。

// 单个删除
Game.deleteOne({ _id: '5f0aa810a5770b0960a33314' }, (err) => {
    if(err) console.error(err)
})

// 批量删除
Game.deleteMany({ _id: { $in: ['5f0aac77c783dc380cf15eef', '5f0aac77c783dc380cf15eee'] } }, (err) => {
    if (err) console.error(err)
})
修改 update

修改能找到与指定条件相匹配的数据,修改指定的字段为指定的值

与save的区别,在于,save是整个覆盖数据,而update是修改指定的字段

  • 普通修改
    最为普通,没有特点的update
Game.updateOne({ _id: '5f0aadc5dd9d313ee070fcee' }, {
    name: 'TheLastOfUs' // 要修改的字段和值
}, (err, raw) => {
    if (err) console.error(err)
    else {
        console.log('修改成功')
        console.log(raw) // 修改结果文档
    }
})

通常update的查询条件是id,updateOne的第二个参数就是指定要修改的字段和值

  • 修改并查询
    这是个可以应对并发的方法,是查询和修改两个功能的合体。

并发:同一段代码,同时被调用多次。

高并发:同一段代码,同时被调用N次,就是非常非常多次

并发时最容易出的错误就是,值被覆盖了

常见例子:钱包余额,抢红包等。但凡和钱有关,就要格外注意。

这种场合下如果要在修改后获取其数据,就需要一个具备数据库原子性的操作。

什么时原子性?过去科学家认为原子是最小的物质,不可分割的物质。所以原子性的意思就是具备 不可分割的特性。

语法:

// 查询并修改:具备数据库原子性
Game.findOneAndUpdate({ name: 'Super Mario Bros' }, {
    platform: 'SFC',
    price: 300,
    updateTime: Date.now()
}, {
    new: true, // 是否返回修改后的数据
    upsert: true, // 是否不存在就增加一条新的
    // 注意:upsert的时候不会去修改 updateTime 和 createTime 这种创建时自动赋值的数据,所以需要设置setDefaultsOnInsert
    setDefaultsOnInsert: {
        createTime: Date.now()
    }
}, (err, doc, res) => {
    if (err) console.error(err)
    else {
        console.log('查询并修改完成')
        console.log(doc) // 返回查询到的数据
        console.log(res)
    }
})
  • 第一个参数是查询条件
  • 第二个参数是要修改的字段和值
  • 第三个是可选附加功能,可以不填
  • 第四个是callback,用来获取查询到的数据
对数字进行加减

如果说对数字进行直接覆盖,可以使用 updatefindOneAndUpdate。但之前例子中,如:从钱包中扣钱或存钱,抢红包,这些场景就不能直接覆盖数字,而是在其现有基础上进行加减。

以update为例,语法:

// 单独对数字的修改
Game.updateOne({ name: 'Super Mario Bros' }, {
    platform: 'SFC', // 这里依然可以修改其他字段的值
    $inc: { price: -20 } // 使用$inc,增加数字的值,可以是负数
}, (err, raw) => {
    // err 如果数据库报错会有err,否则就不存在
    // raw 修改结果
    // 修改的callback方法,基本没有用
})

第二个参数是修改的字段和值,其中只要声明一个 $inc就可以了 inc是 increase(增加) 的缩写

查询 exists & find

  • exists 查询指定条件的数据是否存在
  • 语法:
// 查询 name 字段为 The Last Of Us 的数据是否存在
Game.exists({ name: 'The Last Of Us' }, (err, res) => { 
    if (err) console.error(err)
    console.log('exists: ' + res) // 这里的res是个boolean值,true代表存在,false代表不存在
})
  • find 条件查询,如果只查询一个值,可以使用 findOne方法
基础用法

语法:

// 查询 name 中包含 “2” 的数据
Game.find({ name: { $regex: /^(\s|\S)*2(\s|\S)*$/ } }, (err, docs) => {
    if (err) console.error(err)
    else console.log(docs)
})
  • 第一个参数是查询条件
  • 第二个参数是 callback: (err, games)=>{}
    • err:如果数据库报错,会包含错误信息
    • docs:查询出来的数据库数据,是个数组;如果用的 findOne方法,docs是一个数据库数据对象,而不是数组
使用$where做查询条件

语法:

Game.find({
        name: { $regex: /^(\s|\S)*2(\s|\S)*$/ }
        $where: 'this.createTime.getTime()!=this.updateTime.getTime()'
    }, (err, docs) => {
    if (err) console.error(err)
    else console.log(docs)
})
  • 参数与基础用法相同
  • 在第一个参数,查询条件中,多了一个 $where指令。该指令接收一个字符串参数,该字符串是一个js表达式。表达式中的 this代表正被查找的当前数据对象。所以可以通过 this.field来引用自身数据的字段来进行逻辑判断。
  • 用处:如果需要取表里两个及其以上字段来做逻辑判断时,就需要用 $where
  • 举例:以 game 表为例。如果要查找 createTimeupdateTime相同的数据(也就是查找从来没被修改过的数据);或者查找 updateTime - createTime小于三天的数据(也就是查找自创建以来,3天内都未作修改的数据)
查询排序 sort 与分页 skip & limit (size)

排序:

Game.find({}, 
null, // '-updateTime' 排除updateTime字段 'name'选择 name字段
{
    sort: {
        price: 1, // 按价格降序和最新数据排序
        updateTime: -1 // 大于零 升序;小于零 降序
    }
}, (err, docs) => {
    console.log(docs)
})

注意:该方法参数与基础查询方法不一样

  • 第一个参数:查询条件
  • 第二个参数:选择返回的字段,类似sql中的select,null的话就返回所有表数据
    • 参数值是个字符串,例如 'name platform price',字段间用空格隔开
    • 字段名前加上 -号,代表排除某字段,如:'-updateTime -createTime',这样数据库就不会返回 updateTimecreateTime的值。
    • 选择字段和排除字段不能同时存在,也就是不能这么写 name -updateTime
  • 第三个参数:可选配置
    • sort:按字段排序,值大于零升序,小于零降序
  • 第四个参数:callback

分页 skip & limit

  • skip:查询时候,跳过多少条数据
  • limit:返回数据的总数

举例:如果我们按每页5条数据进行取值,第二页的数据该怎么查询呢?
语法:

Game.find({}, // 分页的时候也可以加查询条件
null,
{
    sort: {
        price: 1, // 按价格降序和最新数据排序
        updateTime: -1 // 大于零 升序;小于零 降序
    },
    skip: (2-1)*5, // 公式:skip:(page-1)*size
    limit: 5 // 公式:limit:size
}, (err, docs) => {
    console.log(docs)
})

写法基本和排序相同,但多了两个字段 skiplimit

如果令当前页为 page;每页记录数为 size;那么参数 skiplimit就应该是:

skip: (page-1)*size,
limit: size

mongoose的curd操作

const mongoose = require('mongoose');
const {model, Schema} = mongoose;

// 数据结构
const schema = new Schema({
    name: {type: String, index: true},
    platform: {type: String, index: true},
    price: {type: Number, index: true},
    // default: 字段的默认值
    // createdAt: {type: Date, default: new Date(), index: true},
    // updatedAt: {type: Date, default: new Date(), index: true},
}, {
    // 添加时间戳
    timestamps: true
});

// 构建数据模型Model
const Game = model('game', schema);

(async () => {
    const db = mongoose.connection
    db.on('open', () => {
        console.log('打开数据库连接')
    })
    db.on('error', err => {
        console.error('数据库异常:"', err)
    })
    await mongoose.connect('mongodb://admin:111111@127.0.0.1:27017/test')
    console.log('连接成功')


    // let r = await Game.insertMany([
    //     {
    //         name: 'Super Mario Bros',
    //         price: 320,
    //         platform: 'FC'
    //     },
    //     {
    //         name: 'The Last Of Us',
    //         price: 400,
    //         platform: 'PS'
    //     },
    //     {
    //         name: 'Mather',
    //         price: 250,
    //         platform: 'FC'
    //     },
    //     {
    //         name: 'Dead Space',
    //         price: 300,
    //         platform: 'XBOX'
    //     },
    //     {
    //         name: 'Final Fantasy 10 Remaster',
    //         price: 280,
    //         platform: 'PS'
    //     },
    //     {
    //         name: 'Halo',
    //         price: 366,
    //         platform: 'XBOX'
    //     },
    //     {
    //         name: 'Gears Of War',
    //         price: 333,
    //         platform: 'XBOX'
    //     },
    //     {
    //         name: 'God Of War',
    //         price: 420,
    //         platform: 'PS'
    //     },
    //     {
    //         name: 'Legend Of Zalda',
    //         price: 460,
    //         platform: 'FC'
    //     }
    // ])


    // 新增数据
    // let doc = new Game({
    //     name: 'FEZ',
    //     platform: 'PC',
    //     price: 100
    // })
    // await doc.save()

    // await Game.create({
    //     name: 'Start Craft',
    //     platform: 'PC',
    //     price: 230
    // })


    // 删除数据
    // let r = await Game.deleteOne({_id: '63e4bfd89b9de0343e09b9d0'})
    // let r = await Game.deleteMany({_id: {$in: ['63e4c0169b9de0343e09b9d2', '63e4c01e9b9de0343e09b9d3']}})


    // 修改数据
    // let r = await Game.updateOne({_id: '63e4bfba36568e3647fa9cb9'}, {
    //     price: 130
    // }, {
    //     // 是否修改时间戳
    //     timestamps: true
    // })

    // 查询并修改数据
    // let r = await Game.findOneAndUpdate({name: 'Diablo'}, {
    //     price: 500,
    //     platform: 'PC'
    // }, {
    //     // 是否修改时间戳
    //     timestamps: true,
    //     // 是否没有数据就插入数据
    //     // 当数据查询不到时就添加新数据
    //     upsert: true,
    //     // 返回修改后的数据
    //     new: true,
    //     // 若添加数据的话就要设置一些默认数据
    //     setDefaultsOnInsert: true
    // })


    // 查询
    // $where 添加一个约束条件,值是一个用于数据库查询的js脚本,脚本里写一个布尔表达式
    // let r = await Game.find({/*$where: `this.updatedAt === this.createdAt`*/}, '-platform -price', {
    //     sort: {updatedAt: -1},
    //     skip: 1,
    //     limit: 2
    // })

    // 判断是否存在符合条件的数据
    // let r = await Game.exists({name: 'Start Craft'})

    // 查询数量
    // let r = await Game.count({platform: 'PC'})

    let r = await Game.aggregate([
        // $match 用来过滤数据
        {$match: {price: {$gt: 300}}},
        // 数组中每个对象是一个聚合的条件
        // 其中 $group 必填
        // _id: 代表用什么字段进行分组,可以写 null 代表不分组
        // $avg: 是内置的命令,用来求字段的平均值
        {$group: {_id: "$platform", avgPrice: {$avg: "$price"}}},
        // $count 用于统计数量
        // { $count: 'total' },
    ])

    console.log(r)
})()

ts配置文件

部分参考文献

官网说明

推荐配置

webpack的ts配置

什么是 tsconfig

tsconfig.json 文件存在的目录表明该目录是 TypeScript 项目的根目录。该 tsconfig.json 文件指定了编译项目所需的根文件和编译器选项。

当成功配置了 tsconfig.json 后,就可以在项目根目录下运行 tsc 了,此时编译器会根据配置对项目进行编译

配置文件选项

顶级选项有:

  • files 编译文件的入口文件列表
  • extends 扩展继承其他的 tsconfig
  • include 指定要包含在项目编译中的文件名或文件模式字符串(和files选项作用类似)
  • exclude 指定 include 中的一些不需要被编译的文件或文件模式字符串
  • references 引入外部项目的选项
  • compilerOptions 编译选项
  • typeAcquisition 设置自动引入库类型定义文件(.d.ts)相关
compilerOptions 常见配置
{
  // ...
  "compilerOptions": {
    "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
    "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
    "diagnostics": true, // 打印诊断信息 
    "target": "ES5", // 目标语言的版本
    "module": "CommonJS", // 生成代码的模板标准
    "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
    "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
    "allowJS": true, // 允许编译器编译JS,JSX文件
    "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
    "outDir": "./dist", // 指定输出目录
    "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
    "declaration": true, // 生成声明文件,开启后会自动生成声明文件
    "declarationDir": "./file", // 指定生成声明文件存放目录
    "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
    "sourceMap": true, // 生成目标文件的sourceMap文件
    "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
    "declarationMap": true, // 为声明文件生成sourceMap
    "typeRoots": [], // 声明文件目录,默认时node_modules/@types
    "types": [], // 加载的声明文件包
    "removeComments":true, // 删除注释 
    "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
    "noEmitOnError": true, // 发送错误时不输出任何文件
    "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
    "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
    "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
    "strict": true, // 开启所有严格的类型检查
    "alwaysStrict": true, // 在代码中注入'use strict'
    "noImplicitAny": true, // 不允许隐式的any类型
    "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
    "strictFunctionTypes": true, // 不允许函数参数双向协变
    "strictPropertyInitialization": true, // 类的实例属性必须初始化
    "strictBindCallApply": true, // 严格的bind/call/apply检查
    "noImplicitThis": true, // 不允许this有隐式的any类型
    "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
    "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
    "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
    "noImplicitReturns": true, //每个分支都会有返回值
    "esModuleInterop": true, // 允许export=导出,由import from 导入
    "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
    "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
    "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
    "paths": { // 路径映射,相对于baseUrl
      // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
      "jquery": ["node_modules/jquery/dist/jquery.min.js"]
    },
    "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
    "listEmittedFiles": true, // 打印输出文件
    "listFiles": true// 打印编译的文件(包括引用的声明文件)
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值