node,express,mongodb笔记

385 阅读15分钟

NodeJs就是一个可以让JavaScript脱离浏览器还能执行的平台,并且这个平台对JavaScript功能进行了增强。 具有特定功能的文件就是一个模块(把特定的功能写在一个js文件里面,这个文件就是一个模块) 导出模块 exports.属性名 = 函数名; 引入模块 let 变量名 = require('./相对路径');

  1. 模块内的变量都是局部变量 模块内的函数 都是局部函数 所有的模块(.js文件), 都被一个函数包裹起来 function (exports, require, module, __filename, __dirname) { // 实际上我们写的代码,都被包裹在这个函数里

    }

  2. require函数:主要用来引入模块,引入模块有三种方式 a) 引入自定义模块(自己写) var 变量名 = require('./相对路径')

    b) 引入系统模块,不需要下载和安装 var 变量名 = require('模块名')

    c)引入第三方模块 下载第三方模块 npm install 模块名 // 缩写 npm i 模块名 备注: 所有通过npm下载的第三方模块, 都被放入一个 node_modules文件夹里。 引入模块 var 变量名 = require('模块名')

  3. exports导出(只是指向导出对象的一个引用,真正的导出对象是 module.exports) exports.属性名 = 函数名; exports.属性名 = 变量名;

补充: 变量不写 var关键字 就是全局变量(不需要导出,只要引入这个模块就能使用) global,全局对象,(相当于window,nodejs没有window对象)

  1. exports 和 module.exports的区别: exports只是一个指向 module.exports 的一个地址(指针),真正的导出对象是 module.exports;

  2. node_modules文件夹: 所有的node第三方模块(需要下载),都自动存入 node_modules文件夹里面 补充: 引入模块,可以不写后缀;

  3. 一个文件目录(文件夹)就是一个包,文件夹里面的模块,可以使用包管理 需要一个包管理文件 pakage.json

  4. 包管理工具的使用:(***** ) 一个文件目录 就是一个包

    a) 生成一个包描述文件 pakage.json 从这个目录进入cmd,运行 : npm init

    b) 下载 模块 npm install 模块名 // 缩写 npm i 模块名

    c) 引入模块 var 变量名 = require('模块名')

    本地安装和全局安装: 本地安装,装在当前目录下 npm i 模块名 全局安装: 装在 c盘 C:\Users\Administrator\AppData\Roaming\npm 目录下 npm i 模块名 -g

8 npm常用命令 1) 安装包,并且添加到依赖列表 npm i 模块名 --save ( cnpm i 模块名 --save )

2) 全局安装包
    npm i 模块名 -g

3) 删除包
    npm remove 模块名   // npm remove 模块名 -g (删除安装在全局的包)

4) 更新包(更新到最新版本)
    npm update 模块名

5) 查看可用版本 
    npm view 模块名 versions 

6) 安装指定版本的包(模块, 并且添加到依赖列表
    npm i 模块名@版本号 --save 

    补充: npm i 或者 cnpm i 
    可以依次安装完pakage.json依赖列表(dependencies)里面的所有 模块

同步和异步: a) 同步阻塞代码 b) 异步不阻塞代码

Promise(承诺),es6新增的一个对象,主要用来控制异步操作 (*****) a) Promise有三个状态: a) 进行中 pendding
b) 已完成 resolved c) 已失败 rejected

    Promise的状态:  只会从 进行中(pendding)  变成  已完成(resolved)
                    或者从  进行中(pendding) 变成  已失败(rejected)
b) 基本使用方法
    // 创建promise实例
    var promise = new Promise((resolve, reject) => {
        // 这里写你的逻辑代码
        if (true) {
            resolve(data)
        } else {
            reject(err)
        }
    });
    promise.then((data) => { 这里获取的是成功的数据 }, (err) => { 失败信息 })

###fs模块 这个一个主要对文件进行操作的模块 封装了很多关于文件操作的api(方法和属性) 1)使用fs模块步骤: 引入模块 var fs = require('fs'); 2) fs模块 读取文件 a) 异步读取 fs.readFile('要读取的文件名', func9tion (err, data) { if (err) { throw err } else { console.log('文件读取成功:', data) } })

    b) 同步读取:
        var data = readFileSync('要读取的文件名')
        console.log(data); // 同步读取的结果

3) 文件信息
    // 异步获取文件信息
    fs.stat('文件的路径和名字', function (err, stats) {
        // 这里面 的 stats 包含了文件的所有信息
    })

    // 同步获取文件信息
    var stats = fs.statSync(path)

4) fs写文件
    // 异步写文件
    fs.writeFile('要写入的文件的路径', '写入的内容', function (err) {
        if (err) {
            console.log('写入文件失败')
        } 
        console.log('写入文件成功')
    })
    // 同步写
    fs.writeFileSync()


5) fs删除文件
    // 异步删除
    fs.unlink('要删除的文件的路径和名字', function (err) {
        if (err) {
            throw err;
        } else {
            console.log('删除成功')
        }
    })
    // 同步删除
    fs.unlinkSync(path)


6) 获取目录(文件夹)里面的文件和文件夹
    fs.readdir('文件目录', function (err, files) {
        // 读取出来的结果 是一个数组(files是数组)
    })
    // 同步读取
    fs.readdirSync('目录')

7) 创建文件夹
    fs.mkdir('要创建的文件夹', function (err) {
        if (err) {
            throw err;
        } else {
            console.log('文件夹创建成功')
        }
    })


8)删除文件夹
    fs.rmdir('要删除的文件夹的路径', function (err) {
        if (err) {
            throw err;
        } else {
            console.log('文件夹删除成功')
        }
    })
  1. 流读取 readFile(readFileSync)读取文件的时候,都会把一个文件完成的放入Buffer缓存区, 电脑需要准备同样大小的内存空间,可能造成内存溢出。 所以需要,流读取

    1) 创建读取流 var 变量 = fs.createReadStream('文件路径')

     // 事件类型
     变量.on('data',callback)  // 读取过程中触发
     变量.on('end',callback) // 读取结束触发
     变量.on('error',callback) // 错误触发
    

    2) 创建写入流 var 变量 = fs.createWriteStream('文件路径')

     // 写入数据
     变量.write('要被写入的数据')
    
     // 写入结束
     变量.end();
    
     // 监听写入完成的事件 finish
     变量.on('finish', callback)
    
     // 监听出错事件
      变量.on('error', callback)
    
    1. 管道流方式 a) 创建读取流 var readStream = fs.createReadStream('读取的文件目录')

      b) 创建写入流 var writeStream = fs.createWriteStream('写入的文件的目录')

      c) 使用管道流 readStream.pipe(writeStream)

    4) 链式流 需要引入压缩处理模块 var zip = require("zlib");

     a) 创建读取流
         var readStream = fs.createReadStream('读取的文件目录')
    
     b) 创建写入流
         var writeStream = fs.createWriteStream('写入的文件的目录')
    
     c) 使用链式流
         readStream.pipe(zip.createGzip()).pipe(writeStream);  
    
url 全球统一资源定位符,是可以在互联网上访问到资源的唯一地址
https:  协议 常见的有http https
    www: 主机名
    itsource.cn  域名
    upload/class/20180806/  文件路径
    80  端口号(默认是80 可以不写 其他的都要写)
http状态码
    2xx
    200  请求成功
    3XX
    304 未修改(缓存)
    301 请求的 URL 已移走
    4XX
    404 找不到
    403 服务器拒绝请求(没有权限)
    5XX
    500 内部服务器错误
    备注:
        在响应头设置状态码
        res.writeHead(状态码, {"Content-Type":"text/html; charset=utf-8"})
http模块
    搭建服务器 前后端数据传递 (请求数据 响应请求)
        
    http模块get方法:
    http.get('请求的地址url', (res) => {
        // res 就是请求获取的数据对象

        // 监听data事件
        res.on('data', (data) => {
            // 这里可以获取data(buffer类型)
        })

        // 监听数据请求结束事件
        res.on('end', () => {
            console.log('获取数据完毕')
        })
    }).on('error', (err) => {
        console.log(err);
    })

请求的两种方式的区别(get/post)
    1) get
        a) 请求的数据了比较少
        b) 参数暴露在url上,不安全
        c) 请求的参数 放在 url?参数1=值1&参数2=值2
        e) get方式传参由于参数在url上,浏览器url长度有限制

    2) post
        a) 请求的数据库比较多
        b) 相对安全
        c) 请求的参数放入请求体 不会暴露出来

Express 是一个基于 Node.js 平台的开发框架 项目快速生成工具 express-generator,全局安装express-generator npm i express-generator -g 新建一个文件夹, 就可以在这个文件夹里面使用 express命令创建项目 express 项目的名字 -e // -e 是使用nodejs的ejs模板,例子: express app -e 安装所有依赖npm i
启动项目 A) 自带方式: npm run start npm start 备注: 这两个方式回去运行 bin目录下的www文件,默认启动端口是3000 B) 推荐方式: a) 找到 app.js 在导出之前(module.exports = app; 之前) 监听端口 app.listen(80, () => { console.log('服务器启动成功...') }) b) 运行app.js node app 项目目录详解: pakage.json 包描述文件 里面可以看到依赖的模块有哪些? app.js 是主模块 里面可以启动服务器 定义路由的对应关系 (其他的配置) views 里面放的是nodejs的后端模板文件 =》 ejs模板文件 routes 里面放的是 路由文件 public 里面放的是 静态文件(html/css/js/iamges前端文件) 说明: 如果有一个页面 index.html 前端会先请求到这个页面 否则就找路由index.js node_modules 放的是安装的node的第三方模块 bin 放的是默认启动的文件

路由详解:
    所有项目根目录路由 交给index路由处理
    所有项目users路由 交给users路由处理
    如果要新建路由:
        1)在router里面创建一个 路由名.js 文件
        2)在app.js里面 改造两个地方
            a) 引入自己写的路由
                var 变量名 = require('./routes/路由名');
                例如: var vipRouter = require('./routes/vip');

            b) 设置处理url和路由的对应关系
                app.use('/url', 变量名)
                例如: app.use('/vip', vipRouter);

    说明: 一个请求 一个响应
        前端: 在浏览器输入  localhost:666   相当于请求首页根目录
        后端: 在路由index.js里面:
               router.get('/', (req, res) => {
                   // 响应前端发送的请求,返回数据给前端
                   res.send('响应给前端的数据')
               })
前端: 在浏览器输入localhost:666/news.html   相当于请求更目录下的 /news.htm文件
        后端: 在路由index.js里面:
               router.get('/news.html', (req, res) => {
                   // 响应前端发送的请求,返回数据给前端
                   res.send('响应给前端的数据')
               })

响应对象res(response),主要用来响应前端发送的请求,把响应数据发送给前端
    备注: 如果不响应,前端请求会一直挂起(打圈圈);
        1) res.send()   (*****)
            a) res.send(json数据对象)
            b)res.send(字符串)
            c) res.send(html)
            说明: res.send() 在一个逻辑里面只能用一次
                  不能发送数字 会被当成状态码
        2) res.json()  响应json数据给前端 
            res.josn(JSON对象)
        3) res.jsonp 用于响应前端的跨域访问
            res.jsonp(json对象)
        4) res.render(参数1, 参数2)  (*****)  
            作用: 数据和模板合并渲染 生成html 发送(响应)给前端
            参数1: 使用的ejs模板文件的名字
            参数2: json数据 必须是 key:value 这种形式

请求对象req(request),主要用来接收前端(浏览器)发送的数据。
    a) 如果请求是get方式 (*****)
        req.query.参数名
    b)如果请求是post方式(*****)
        req.body.参数名
    c) 获取ip
        req.ip 

###mongodb 配置环境变量(让mongo命令可以在任何目录使用) a) 复制目录C:\Program Files\MongoDB\Server\4.0\bin地址 b) 计算机右键 =》 属性 =》 高级系统设置 =》 环境变量 =》 系统变量path => 加个英文分号 粘贴: C:\Program Files\MongoDB\Server\4.0\bin 备注: 如果是window10 直接新建一个 粘贴进去

MongoDB的基本使用:
    1) 常用命令:
        a) 显示当前数据库列表(当前有哪些数据库, 只有数据库里面有数据才能显示)
             show dbs

        b) 显示当前你在哪个数据库里面
             db

        c) 创建数据库(如果这个数据已经存在 那么就直接使用, 如果不存在,就会创建)
            use  数据库的名字

        e) 显示数据库中的集合
            show collections 

6. 数据库的增删改查(CRUD)操作(*****)
   <-------------------------- 增加数据    ------------------------->
    语法: 
        1) db.集合名.insert({json结构的数据})   // 集合名是自己取的 如果存在 
直接往里插入数据 如果不存在, 会自动创建集合
            示例: db.student.insert({"name":"小张", "age":18})
        2) db.集合名.save({json结构的数据})
            示例:db.h50528.save({"name":"小李", "age":28})           
            区别: insert() 插入数据 如果有相同的(id一样的数据)会报错 插入失败
                   save() 插入数据 如果有相同的(id一样的数据)会覆盖 插入成功
        <-------------------------- 查询数据    ------------------------->
        语法:
            db.集合名.find()     // 查询集合中的所有数据
            db.集合名.findOne()  // 查询集合中的第一条数据
            db.集合名.find().pretty()   // 查询集合中的所有数据  查询的结果要格式化
    <-------------------------- 删除数据    ------------------------->
        1. 删除文档 
            db.集合名.remove({})    删除集合下所有数据
            db.集合名.remove({"name": "张三"})   删除集合下name=”张三”的数据,按条件删除
        2. 删除集合 
            db.集合名.drop() 或 db.runCommand({"drop":"集合名"})    删除集合
        3. 删除数据库(先确定当前在哪个数据库里面)
            db.runCommand({"dropDatabase": 1})    删除当前数据库,注意 此处的1没加双引号
    
    <-------------------------- 修改数据    ------------------------->
        语法:db.web.update({"name":"a1"}, {"age":10})   // 第一个括号是查找的条件 
第二个括号是修改后的新内容
        示例:db.stu.update({"name":"张三"}, {"age": "5"})
       补充: db.stu.update({"name":"王五"}, {$set: {"age":28}})


7. 高级条件查询
    语法1:db.collection.find({ "key" : value })    查找key=value的数据.
    例1:查找女歌星。
    思路:查找sex=”女”的歌星。
    示例: db.list.find({"sex":"女"})

    语法2: db.collection.find({ "key" : { $gt: value } })    key > value
    例2:查找年龄大于53的歌星。
    示例: db.list.find({"age": { $gt: 53 }})

    语法3:db.collection.find({ "key" : { $lt: value } })    key < value
    例3:查询年龄小于35岁的歌星。
    示例: db.list.find({"age": { $lt: 35 }})

    语法4:db.collection.find({ "key" : { $gte: value } })    key >= value
    例4:查询成绩大于等于95的歌星。
    示例: db.list.find({"score": { $gte: 95 }})

    语法5:db.collection.find({ "key" : { $lte: value } })    key <= value
    例5:查询年龄在小于等于32岁的歌星。 
     示例: db.list.find({"age": { $lte: 32 }})

    语法6:db.collection.find({ "key" : { $gt: value1 , $lt: value2 } })   value1 < key <value2
    例6:查找年龄在30-40岁之间的歌星。
    示例: db.list.find({"age": { $gt: 30 , $lt: 40 }})
     
    语法7:db.collection.find({ "key" : { $ne: value } })    key <> value
    例7:查询外国歌手。
     示例: db.list.find({"country": { $ne: "中国" }})

    语法8:db.collection.find({ "key" : { $mod : [ 10 , 1 ] } })    取模运算,
条件相当于key % 10 == 1 即key除以10余数为1的
    例8:查询成绩为5 、15、 25、。。。。95的歌星。
    示例: db.list.find({"score" : { $mod : [10, 5] }})

    语法9:db.collection.find({ "key" : { $in: [ 1, 2, 3 ] } })    属于,
条件相当于key等于[ 1, 2, 3 ]中任何一个.
    例9:查询序号(num)为3或者6或者9的歌星。
    示例: db.list.find({ "num" : { $in: ["3", "6", "9"] } })

    语法10:db.collection.find({ "key" : { $nin: [ 1, 2, 3 ] } })    不属于,
条件相当于key的值不属于[ 1, 2, 3 ]中任何一个。
    例10:查询国籍不为美国和韩国的歌手。
    示例: db.list.find({"country" : { $nin: ["美国", "韩国"] }})

    语法11:db.collection.find({ "key" : { $size: 1 } })   
     $size 数量、尺寸,条件相当于key对应的值的数量是1(值必须是数组)
    例11:查询有3个代表作品的歌手
    示例: db.list.find({"works" : { $size: 3 }})

    语法12:db.collection.find({ "key" : { $exists : true|false } })    
    例12-1:查询包含name字段的数据。
    示例: db.list.find({"name" : { $exists : true }})

    语法13:db.collection.find({ $or : [{a : 1}, {b : 2} ] })  
    例13:某个娱乐公司15个人,资料都在数据库里面,某个活动必须要刘德华参加,
    另外需要团队的全部女歌手配合演出,领导安排你帮忙打印歌手的资料。
    示例: db.list.find({$or : [{"name":"刘德华"}, {"sex":"女"}]})

    语法14: 如果值是对象
    db.list.insert({"name":"test", "score":{"yy":80, "sx":79, "wy":95}})
    查询语文成绩为80的同学
    示例: db.list.find({"score.yy": 80})

8. 其他高级条件语法(***)
    1) 排序:
    db.collection.find().sort({ "key1" : -1 ,"key2" : 1 })    这里的1代表升序,-1代表降序
    例1:对所有歌星安年龄排序。
    示例: db.list.find().sort({"age": 1})

    2)limit(n) 限制输出多少条  
       实现输出5条
       示例:db.list.find().limit(5)

    3) skip(n) 跳过多少条再输出
       跳过5条再输出
       示例: db.list.find().skip(5)

    4) 综合使用:
        跳过5条 输出5条
        db.list.find().skip(5).limit(5)

        分页数据输出思路
            假如103 条数据 每页显示10条 一共分 11页
            var pagesize = 10;
            var n = (currentpage - 1)*pagesize;
            db.list.find().skip(n).limit(pagesize)

###mongoose

1. 它是nodejs的一个第三方模块,主要是用来操作MongoDB数据库的。
2. mongoose入门步骤:
    1) 新建一个项目目录(文件夹),初始化项目目录,安装mongoose
        npm init     // 生成一个pakage.json
        cnpm i mongoose --save    // 安装mongoose 并且添加到依赖列表里面去

    2) 新建一个.js文件, 引入mongoose
        var mongoose = require('mongoose');

    3) 使用mongoose连接数据库
        连个前提条件:
            a) 确保你的mongodb正在运行(已经安装为window服务 已经在提供服务)
            b) 确保你的mongoose模块已经安装
        /*
        mongoose.connect(参数1, 参数2)
            参数1:mongodb://127.0.0.1:27017/web
                  mongodb://  协议
                  127.0.0.1   本地地址 相当于 localhost
                  27017      mongodb的默认端口好
                  web   想要连接的数据库的名字(自己取,如果有 直接连接 没有 会创建一个)

            参数2:就是一个回调函数  
        */
        mongoose.connect('mongodb://127.0.0.1:27017/web', (err) => {
            if (err) {
                throw err;
            } else {
                console.log('连接数据库成功')
            }
        })


3. 以上步骤完成以后,想要操作数据库,还得完成以下三步:
    1) 定义骨架(需要操作数据库的哪些字段 都需要先定义骨架)
        var userSchema = new mongoose.Schema({
            name: String,
            age: Number
        })
        备注: 骨架本身不具备操作数据库的能力


    2) 发布模型
        var userModel = mongoose.model('userModel', userSchema, 'user')
        model(参数1, 参数2, 参数3)
            参数1: 基本不用 模型名
            参数2: 发布这个模型 需要使用的骨架 
            参数2: 连接这个数据库 索要操作的里面的集合名(集合最好先创建好 如果不创建 会自动创建)

        备注:
            模型主要用来 查询 数据 

    3) 创建实体
        var intance = new userModel();  
        备注: 实体具备新增数据的能力 主要用来 新增 删除 修改
              实体: 什么地方使用  就在什么地方创建


1. 使用mongoose新增数据
    1)完成以上几步
    2)把需要新增的数据 挂在实体上面
    3)实体执行保存操作(save()方法),在save()里面传入回调
        如果有错 抛出错误
        否则 打印新增数据成功

2. 使用mongoose删除数据
    1)以上准备步骤
    2)删除数据 得根据id删除 所有 需要一个id
        var id = '5b6f9968c83affe9e1a9eeeb';
    3) 根据id 查询出这条数据
        userModel.findById(id, (err, data) => {
            console.log(data);  // 这里能够把 data查询出来
            if (data) {
                data.remove((err) => {
                    if (err) {
                        throw err;
                    } else {
                        console.log('删除成功 gg')
                    }
                })
            }
        })

3. 使用mongoose修改数据
首先根据id查询出数据数据,然后把新数据保存即可
router.post("/doedit.html",(req,res)=>{
  let id=req.query.id;
  console.log(id);
  listModel.findById(id,(err,doc)=>{
    doc.title=req.body.title;
    doc.author=req.body.author;
    doc.time=new Date().toLocaleString();
    doc.from=req.body.from;
    doc.content=req.body.content;
    doc.hits=1;
    doc.save();
  })
  res.redirect("/list.html");
});

4. 使用mongoose查询数据
router.get("/view.html", (req, res) => {
  id = req.query.id;
  //console.log(id);
  listModel.findById(id, (err, data) => {
    if (err) {
      throw err;
    }
    if (data) {
      res.render("view.ejs", {
        newsdetail: data
      });
    }
  })
});
以上案例是根据id查询到数据,docs表示查询的结果,可用于渲染到ejs模板
如果不加条件,表示查询全部,结果是一个数组
listModel.find({},(err,doc)=>{
  console.log(doc);
})