Node知识详解(适合初学者)包含express

* 1. 文件
1.1. 文件的读取与写入
  • // 1.使用rquire方法加载fs(fileststem)核心模块
    let fs = require('fs');
    // 2.读取文件
    fs.readFile('./data/hello.txt', function(error, data) {
    	if(error) {
    		console.log("文件读取失败!")
    	} else {
    		console.log(data.toString());
    	}
    });
    // 第一个参数:文件路径  第二个参数:文件内容  第三个参数是回调函数  
    fs.writeFile('./data/你号.txt.txt', 'node.js号啊', function(error) {
    	if(error) {
    		console.log("文件写入失败!" + error);
    	} else {
    		console.log("文件写入成功!");
    	}
    })
    
1.2. 文件流
  • 文件写入流

    let fs = require('fs')
    
    // 创建写入流
    // 语法:fs.createWriteStream(文件路径, [可选的配置操作])
    let ws = fs.createWriteStream('hello.txt', {flags: 'w', encoding: 'utf-8'})
    
    // 监听文件打开事件
    ws.on('open', () => {
        console.log('1. 文件打开了')
    })
    // 监听准备事件
    ws.on('ready', () => {
        console.log('2. 文件写入已进入准备状态')
    })
    // 监听文件关闭事件
    ws.on('close', () => {
        console.log('5. 文件完成, 关闭')
    })
    
    ws.write('1. hello world给\n', (err) => {
        if (err) {
           return console.log(err)
        }
        console.log('3. 内容1流入完成!')
    })
    ws.write('2. hello world给', (err) => {
        if (err) {
           return console.log(err)
        }
        console.log('3. 内容2流入完成!')
    })
    // 文件写入完成
    ws.end(() => console.log('4. 文件写入关闭!'))
    
  • 文件输入流 (将读取的数据写入到新文件中)

    let fs = require('fs')
    
    // 创建读取流
    // 语法;fs.createReadStream(路径, [可选配置项])
    // let rs = fs.createReadStream('../1.mp4', {flags: 'r', encoding: 'utf-8})
    // 以二进制的形式读取视频
    let rs = fs.createReadStream('../1.mp4', {flags: 'r'})
    
    let ws = fs.createWriteStream('./2.mp4', {flags: 'w'})
    
    // 监听流的打开
    rs.on('open', () => console.log('读取文件打开!'))
    // 监听流的关闭
    rs.on('close', () => {
        console.log('读取流结束!')
        // 当读取流完毕后再关闭写入流
        ws.end()
    })
    
    // 每一批次数据流入的完成
    rs.on('data', (chunk) => {
        console.log(chunk)
        ws.write(chunk, () => console.log('单批数据流入完成'))
        // console.log('每一次读取的长度' + chunk.length)
    })
    
  • 管道流 (高度封装的一个方法 读取写入速度很快)

    let fs = require('fs')
    
    let rs = fs.createReadStream('../1.mp4', {flags: 'r'})
    
    let ws = fs.createWriteStream('./3.mp4', {flags: 'w'})
    
    // 监听流的打开
    rs.on('open', () => console.log('读取文件打开!'))
    // 监听流的关闭
    rs.on('close', () => console.log('close!'))
    
    // rs.pipe(ws) 高度封装的将读取的数据写入到新的文件中
    // 速度很快
    rs.pipe(ws)
    
* 2. HTTP
2.1 HTTP. 使用案例
   // 加载http核心模块
let http = require('http')

// 使用http.createServer() 方法创建一个Web服务器  返回一个Server实例
let server = http.createServer()
// 绑定端口号 启动服务器
server.listen(9919, function() {
    console.log('服务器启动成功了... 通过 http://localhost:9919/ 来访问吧')
});
server.on('request', function(request, response) {
    console.log('收到客户端的请求了... 请求的路径是' + request.url)
    response.write('hello')
    response.write('nodejs简介')
    // 告诉客户端 输完了
    response.end();
})
2.2. 表单同步提交和异步提交
  • 表单具有默认的提交行为,默认是同

  • 步的,同步表单提交,浏览器会锁死(转圈儿)等待服务端的响应结果。

  • 表单的同步提交之后,无论服务端响应的是什么,都会直接把响应的结果覆盖掉当前页面。

  • 阻止浏览器的默认提交行为示例代码

  • // 异步提交
        $('#register_form').on('submit', function (e) {
          e.preventDefault()
          var formData = $(this).serialize()
          $.ajax({
            url: '/register',
            type: 'post',
            data: formData,
            dataType: 'json',
            success: function (data) {
              let error_code = data.error_code
              if (error_code === 0) {
                window.alert('注册成功!')
              } else if (error_code === 1) {
                window.alert('邮箱已存在!')
              } else if(error_code === 2) {
                window.alert('用户名已存在!')
              }
                else if(error_code === 500) {
                window.alert('服务器忙, 请稍后重试!')
              }
            }
          })
        })
    
2.3. 服务端重定向针对异步请求无效
  • 服务器重定向只能针对同步请求有效,针对异步请求无效

  • 只能客户端自己跳转

*3. npm 常用命令
  • npx http-server

    • 开启一个服务
    // //开一个端口为 9000 的服务
    npx http-server -p 9000 
    
  • npm init
  • npm init -y 可以跳过向导,快速生成
  • **npm install **

    • 一次性把dependencies 选项中的依赖全部安装
  • npm install 包名

    • 只下载
  • npm install --save 包名

    • 下载并且保存依赖项(package.json 文件中的dependencies 选项)
  • 可以简写成 npm i -S 包名
  • npm uninstall 包名
  • 只删除,如果有依赖项会依赖保存 可以简写成 npm un 包名
  • npm uninstall --save 包名
  • 删除的同时也会把依赖信息也去除 可简写成 npm un -S 包名
  • **npm help **
  • 查看使用帮助
  • npm 命令 --help
  • 查看指定命令的使用帮助 例如忘记uninstall 命令的简写了,这个时候可以通过 npm uninstall --help 来查看使用帮助
    • 解决npm被墙问题
  • 下载淘宝镜像 通过cnpm 来下载插件和库包 通过命令: npm install -g cnpm --registry=https://registry.npm.taobao.org 或者: npm install --global cnpm
3.1. Node 相关工具
  • // 这样设置可以使用 npm 的方式进行下载
    npm config set registry https://registry.npm.taobao.org
    // 可以查看 npm 怕配置信息
    npm config list
    
  • Windows 安装 nvm

    • nvm-windows
    • nodist
  • nvm for windows是一个命令行工具,在控制台输入nvm,就可以看到它的命令用法。基本命令有:

    • nvm arch [32|64] : 显示node是运行在32位还是64位模式。指定32或64来覆盖默认体系结构。
    • nvm install [arch]: 该可以是node.js版本或最新稳定版本latest。(可选[arch])指定安装32位或64位版本(默认为系统arch)。设置[arch]为all以安装32和64位版本。在命令后面添加–insecure ,可以绕过远端下载服务器的SSL验证。
    • nvm list [available]: 列出已经安装的node.js版本。可选的available,显示可下载版本的部分列表。这个命令可以简写为nvm ls [available]。
    • nvm on: 启用node.js版本管理。
    • nvm off: 禁用node.js版本管理(不卸载任何东西)
    • nvm proxy [url]: 设置用于下载的代理。留[url]空白,以查看当前的代理。设置[url]为none删除代理。
    • nvm node_mirror [url]:设置node镜像,默认为https://nodejs.org/dist/.。我建议设置为淘宝的镜像https://npm.taobao.org/mirrors/node/
    • nvm npm_mirror [url]:设置npm镜像,默认为https://github.com/npm/npm/archive/。我建议设置为淘宝的镜像https://npm.taobao.org/mirrors/npm/
    • nvm uninstall : 卸载指定版本的nodejs。
    • nvm use [version] [arch]: 切换到使用指定的nodejs版本。可以指定32/64位[arch]。nvm use 将继续使用所选版本,但根据提供的值切换到32/64位模式的
    • nvm root [path]: 设置 nvm 存储node.js不同版本的目录 ,如果未设置,将使用当前目录。
    • nvm version: 显示当前运行的nvm版本,可以简写为nvm v
    nvm list  //查看目前已经安装的版本
    nvm list available //显示可下载版本的部分列表
    nvm install 10.15.0 //安装指定的版本的nodejs
    nvm use 10.15.0 //使用指定版本的nodejs
    
* 4. Express
安装:npm i --save express
4.1. 初步express
let express = require('express') // 引入express

// 创建服务器应用程序
let app = express();

// 公开指定目录 可以直接通过public/...方式访问 public 目录下面的资源
app.use('/public/', express.static('./public/')) // 公开资源目录 public 下面的
app.use('/static/', express.static('./static')) // 公开资源目录 static 下面的 

app.get('/', function (req, res) {
  res.send('hello express!')
})

app.get('/about', function (req, res) {
  res.send('hello 你好啊?')
})

// 相当于 server.listen()
app.listen(9919, function() {
    console.log('running...')
})

*修改代码后自动重启服务器

  • nodemon 是一个基于Node.js开发的一个第三方命令行工具 使用时需要独立安装

    npm i --global nodemon // 在任意目录执行都行
    
    // 安装完毕后 使用
    nodemon app.js // 只要通过nodemon app.js/app  启动服务器,则会监听文件的变化,变化时会自动重启服务器
    
    • 路由
  • 在express使用art-templat
    • 1. 下载
    npm i -S art-template // 安装
    npm i -S express-art-template
    
    • 2. 配置
      let express = require('express')
      let app = express() 
      
      // 第一个参数表示当渲染以 .art 结尾的文件的时候 使用art-template 模板引擎
      // express-art-template 是专门用来在Express 中把art-template 整合到Express中
      // 原因就是express-art-template 依赖于art-template
      *****app.engine('html', require('express-art-template')) // 核心配置  第一个参数是模板引擎的后缀名
      
      // *****如果要修改默认的views 目录 则可以
      // app.set('views', 'render 函数的默认路径')
      // 如 将默认views 修改成 public   app.set('views', 'public')
      
      // Express 为Response 相应对象提供了一个方法:render
      // render 方法默认是不能使用 但配置了模板引擎就可以使用了
      // res.render('html模板名', {模板数据})
      // 第一个参数不能写路径,默认会去views 目录查找改模板文件
      // *****就是说Express 有一个约定:开发人员把所有视图文件都放在 views 目录中
      
      app.use('/public/', express.static('./public/')) // 公开public下面的资源
      
      app.get('/', function (req, res) {
          res.render('404.html', {
              title: '你好?'
          })
      })
      
      app.listen(9919, function () {
          console.log('running...')
      })
      
  • 用express写留言本

    let express = require('express')
    let app = express() 
    
    let comments = []
    // 第一个参数表示当渲染以 .art 结尾的文件的时候 使用art-template 模板引擎
    // express-art-template 是专门用来在Express 中把art-template 整合到Express中
    // 原因就是express-art-template 依赖于art-template
    app.engine('html', require('express-art-template'))
    
    // 如果要修改默认的views 目录 则可以
    // app.set('views', 'render 函数的默认路径')
    // 如 将默认views 修改成 public   app.set('views', 'public')
    
    // Express 为Response 相应对象提供了一个方法:render
    // render 方法默认是不能使用 但配置了模板引擎就可以使用了
    // res.render('html模板名', {模板数据})
    // 第一个参数不能写路径,默认会去views 目录查找改模板文件
    // 就是说Express 有一个约定:开发人员把所有视图文件都放在 views 目录中
    
    app.use('/public/', express.static('./public/'))
    
    app.get('/', function (req, res) {
        res.render('index.html', {
            comments: comments
        })
    })
    
    app.get('/post', function (req, res) {
        res.render('post.html')
    })
    
    app.get('/pinglun', function (req, res) {
        let comment = req.query
        let date = new Date();
        comment.dateTime = `${date.getFullYear()}/${date.getMonth()+1}/${date.getDate()}  ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`
        comments.push(comment)
        // res.statusCode = 302 老方法
        // res.setHeader('Location', '/')
        res.redirect('/')
        
    })
    
    app.listen(9919, function () {
        console.log('running...')
    })
    
  • throw err

    • 抛出异常

      • 1、阻止程序的执行

      • 把错误的消息打印到控制台

4.2. 在Express 获取表单POST请求体数据
  • 在express 中没有内置获取表单POST请求体的API , 需要使用第三方包:[body-parser]
  • 使用GET 获取数据 只需 req.query就ok
npm i -S body-parser
// 配置 
let express = require('express')
***let bodyParser = require('body-parser')
let app = express()
// 加入配置后 请求对象上会多出来一个属性 :body  可以通过req.body 来获取POST 请求数据
***app.use(bodyParser.urlencoded({extended: false}))
***app.use(bodyParser.json())
// 使用req.body
app.use(function(req, res) {
    let comment = req.body // 通过req.body 来获取 POST请求数据
})
// 解决异步获取不到数据的方法  ====》 使用回调函数
// 获取异步操作的结果
function fn (callback) {
    setTimeout(function () {
        let data = 'hello'
        callback(data)
    }, 1000)
}

fn(function (data) {
    console.log(data)
})
4.6. 解析 get 提交的链接
let url = require('url')
let objUrl = url.parse('请求的地址')
4.3. 完整案例
4.3.1. app.js
// app.js 启动服务 做一些服务相关的配置 模板引擎
// body-parser 解析表单POST 请求体  提供静态资源服务
// 挂载路由 监听端口启动服务
let express = require('express')
let bodyParser = require('body-parser')
// 导入自己写的 router.js 文件
let router = require('./router')
let app = express()
// 配置模板引擎
app.engine('html', require('express-art-template'))

// 允许访问以/node_modules/ 和 /public/开头的静态资源
app.use('/node_modules/', express.static('./node_modules/'))
app.use('/public/', express.static('./public/'))

// 配置body-parser 解析器
app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())

// router(app)
// 把路由容器挂载到 app 服务中
app.use(router)

app.listen(9919, function () {
  console.log('running')
})

module.exports = app

4.3.2. router.js
// router.js 路由模块   处理路由 根据不同的请求方法 设置具体的路径
// 模块职责要单一 不能混用 目的:为了增强项目代码可维护性
let fs = require('fs')
let Student = require('./students')
// Express 提供了一种更好的方式  专门用来包装路由的
let express = require('express')

// 创建一个路由容器
let router = express.Router()

// 将路由都挂载到router 路由容器中
router.get('/', function (req, res) {
    // 第二个参数是可选项 可以指定编码
    // fs.readFile('./db.json', 'utf8', function (err, data) {
    //     if (err) {
    //     return res.status(500).send('Server error.')
    //     }
    // })
    Student.find(function (err, students) {
        if(err) {
            return res.status(500).send('Server error.')
        }
        res.render('index.html', {
            // 从文件读取到的数据一定是字符串 需要通过 JSON.parse() 解析
            students: students
        })
    })
})

router.post('/students/new', function (req, res) {
    // 获取表单数据 并将数据存入文件中 在读取文件显示到页面中
    let student = req.body
    Student.save(student, function (err) {
        if (err) {
            return res.status(500).send('Server error. 保存routerr.js中35行出错' )
        }
        console.log('保存新用户成功了...')
        res.redirect('/')
    })
})

router.get('/students/delete', function (req, res) {
    let id = req.query.id
    Student.delete(id, function (err) {
        if (err) {
            return res.status(500).send('Server error. 删除中出错routerr.js中47行出错' )
        }
        console.log(`id为:${id}成了`)
        res.redirect('/')
    })
})

router.get('/students/new', function (req, res) {
    // 获取表单数据
    res.render('new.html')
})

router.get('/students/edit', function (req, res) {
    let id = req.query.id
    Student.findById(id, function (err, student) {
        if (err) {
            return res.status(500).send('Server error. 根据id查找中出错routerr.js中62行出错' )
        }
        res.render('edit.html', {
            student: student
        })
    })
})

router.post('/students/edit', function (req, res) {
    let student = req.body
    Student.updateById(student, function (err) {
        if (err) {
            return res.status(500).send('Server error. 保存routerr.js中35行出错' )
        }
        console.log('修改新用户成功了...')
        res.redirect('/')
    })
})

// 把router 导出
module.exports = router

// 原始的包装路由
/* module.exports = function (app) {
    app.get('/', function (req, res) {
        // 第二个参数是可选项 可以指定编码
        fs.readFile('./db.json', 'utf8', function (err, data) {
            if (err) {
            return res.status(500).send('Server error.')
            }
            res.render('index.html', {
            // 从文件读取到的数据一定是字符串 需要通过 JSON.parse() 解析
            students: JSON.parse(data).students
            })
        })
    }) 
} */
4.3.3. student.js 处理数据代码
// student.js 数据操作文件模块
// 操作文件中的数据 只处理数据 不关心业务
// 导入文件模块
let fs = require('fs')

let dbPath = './db.json'
let student = []
// 获取所有学生
// callback 中的参数
//   第一个是 err 成功是null  错误是错误对象
//   第二个参数是结果   成功是 数组  错误是undefined
exports.find = function (callback) {
    fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
           return callback(err)
        }
        callback(null, JSON.parse(data).students)
    })
}


// 添加学生
exports.save = function (student, callback) {
    fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
           return callback(err)
        }
        let students = JSON.parse(data).students
        // 处理id 问题
        if (students.length === 0) {
            student.id = 0
        } else {
            student.id = students[students.length-1].id + 1
        }
        // 将新数据放入students数组中
        students.push(student)
        // 将学生数据重新转成字符串
        let fileData = JSON.stringify({
            students: students
        })
        // 重新将数据写入文件中
        fs.writeFile(dbPath, fileData, function (err) {
            if (err) {
                // 将错误对象传回去
                return callback(err) 
            }
            // 成功就没有错误 所以 null
            callback(null)
        })
    })
}


// 更新学生
exports.updateById = function (student, callback) {
    fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
           return callback(err)
        }
        let students = JSON.parse(data).students
        // 根据id 修改   ES6中的方法 find    stu 是修改的对象
        let stu = students.find(item => item.id === parseInt(student.id)) // 短路操作查找
        console.log(stu)
        for (let i in student) { 
            if (student[i] !== '') {
                if (i === 'id') {
                    stu[i] = parseInt(student[i])
                } else {
                    stu[i] = student[i]
                }
            }
        }

        let fileData = JSON.stringify({
            students: students
        })

        fs.writeFile(dbPath, fileData, function (err) {
            if (err) {
                return callback(err)
            }
            callback(null)
        })
    })
}

exports.findById = function (id, callback) {
    fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
           return callback(err)
        }
        let students = JSON.parse(data).students
        let stu = students.find(item => item.id === parseInt(id))
        callback(null, stu)
    })
}

// 删除学生
exports.delete = function (id, callback) {
    fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
            return callback(err)
        }
        let students = JSON.parse(data).students
        // *****findIndex 方法专门用来***根据条件查找元素的下标
        let index = students.findIndex(item => item.id === parseInt(id))
        // let index = students.remove(id)
        // *****splice 方法 splice(index, 1) 注:两个形参 用来删除  从第几个位置删除 删除几个
        students.splice(index, 1)

        let fileData = JSON.stringify({
            students: students
        })

        fs.writeFile(dbPath, fileData, function (err) {
            if (err) {
                return callback(err)
            }
            callback(null)
        })
    })
}

// 扩展数组方法 根据id 查找当前的记录
/* Array.prototype.remove = function(value) {
    for (let i = 0; i < this.length; i++) {
        if(this[i].id === parseInt(value)) {
            return i
        }
        console.log(this[i].id)
    }
    return -1
} */
4.4. 使用原生js回调函数简单封装ajax中的get方法
 function get (url) {
     let xhr = new XMLHttpRequest()
     // 当请求加载成功之后要调用指定的函数
     xhr.onload = function (callback) {
         callback(xhr.responseText)
     }
     xhr.open('get', url , true)
     xhr.send()
 }
get('./data.json', function(data) {
    console.log(data)
})
  • 在浏览器中也可以像在 Node 中的模块一样来进行编程

    通过 <script> 标签来引用加载,而且必须考虑加载的顺序问题

    require.js 第三方库 AMD

    sea.js 第三方库 CMD

4.5. ES6中find 方法底层代码
// 为Array 扩展方法
Array.prototype.myfind = function (callback) {
    for (let i = 0; i < this.length; i++) {
        if(callback(this[i], i)) {
            return this[i]
        }
    }
}

// 使用自定义find 
let ret = users.myfind((item, index) => {
    return item.id === 2
})
*5. MongoDB
5.1. 关系型数据库和非关系型数据库
  • 表就是关系 或者说表与表之间的关系

  • 非关系型数据库非常灵活

  • 有的非关系型数据库就是 key-value 结构

  • MongoDB 数据库是长的最像关系型数据库的

  • 启动(打开两个dos界面先后敲 12 :mongodb :1、mongod4

    ​ 然后输入: 2、mongo 会默认启动本地数据库

  • 如果要修改默认的数据存储目录:mongod --dbpath 数据存储目录路径

  • 停止:Ctrl + C

5.2. 基本命令
  • show dbs

    • 查看数据库列表
  • db

    • 查看当前操作的数据库
  • use 数据库名称

    • 切换到指定的数据(如果没有会新建)
  • 插入数据

    use student

    db.student.insertOne({“name”:“Jack”})

    db.student.find() 查找student集合

  • show collections => 查询所有集合

  • db.collection名称.drop() 删除集合
  • db.dropDatabase() 删除数据库
5.3. 在Node 中如何操作 MongoDB 数据库
  • 使用第三方 mongodb 来操作 MongoDB 数据库
    • 第三方包:mongoose 基于MongoDB 官方的mongodb 包再一次做了封装。
  • 查询所有

    User.find((err, ret) => {
        if (err) {
            return console.log(err)
        } else {
            console.log(ret)
        }
    })
    
  • 根据条件查询所有

    User.find({ // 根据条件查询  查询到的数据放到数组中
        username: 'Bright',
        password: '123456'
    }, (err, ret) => {
        if (err) {
            return console.log(err)
        } else {
            console.log(ret)
        }
    })
    
  • 根据条件查询一个

    User.findOne({ // 根据条件查询满足条件的一个  查询到的数据放到数组中
        username: 'Bright'
    }, (err, ret) => {
        if (err) {
            return console.log(err)
        } else {
            console.log(ret)
        }
    })
    
  • 删除数据

    Use deleteOne, deleteMany, or bulkWrite.

  • 更新

    • findOneAndUpdate()` findByIdAndUpdate and `findOneAndDelete()
5.4. mongodb 操作crud-express
  • students.js 文件(连接mongodb 和 设计schema 以及导出model 以供 router.js使用
let mongoose = require('mongoose')

mongoose.connect('mongodb://localhost:27017/test', {
    useNewUrlParser: true,
    useUnifiedTopology: true
})

let Schema = mongoose.Schema

let studentSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    gender: {
        type: Number,
        enum: [0, 1],
        default: 0
    },
    age: {
        type: Number,
    },
    hobbies: {
        type: String,
    }
})

//直接导出模型构造函数
module.exports = mongoose.model('Student', studentSchema)
  • router.js 文件 路径模块
// router.js 路由模块   处理路由 根据不同的请求方法 设置具体的路径
// 模块职责要单一 不能混用 目的:为了增强项目代码可维护性
let Student = require('./students')
// Express 提供了一种更好的方式  专门用来包装路由的
let express = require('express')

// 创建一个路由容器
let router = express.Router()

// 将路由都挂载到router 路由容器中
router.get('/', function (req, res) {
    // 第二个参数是可选项 可以指定编码
    // fs.readFile('./db.json', 'utf8', function (err, data) {
    //     if (err) {
    //     return res.status(500).send('Server error.')
    //     }
    // })
    Student.find(function (err, students) {
        if(err) {
            return res.status(500).send('Server error.')
        }
        res.render('index.html', {
            // 从文件读取到的数据一定是字符串 需要通过 JSON.parse() 解析
            students: students
        })
    })
})

router.post('/students/new', function (req, res) {
    // 获取表单数据 并将数据存入文件中 在读取文件显示到页面中
    let student = req.body
    new Student(student).save(function (err) {
        if (err) {
            return res.status(500).send('Server error. 保存routerr.js中35行出错' )
        }
        console.log('保存新用户成功了...')
        res.redirect('/')
    })
    /* Student.save(student, function (err) {
        if (err) {
            return res.status(500).send('Server error. 保存routerr.js中35行出错' )
        }
        console.log('保存新用户成功了...')
        res.redirect('/')
    }) */
})

router.get('/students/delete', function (req, res) {
    let id = req.query.id.replace(/"/g, '')
    Student.findByIdAndDelete(id, function (err) {
        if (err) {
            return res.status(500).send('Server error. 删除中出错routerr.js中47行出错' )
        }
        console.log(`id为:${id}成了`)
        res.redirect('/')
    })
})

router.get('/students/new', function (req, res) {
    // 获取表单数据
    res.render('new.html')
})

router.get('/students/edit', function (req, res) {
    let id = req.query.id.replace(/"/g, '')
    Student.findById(id, function (err, student) {
        if (err) {
            return res.status(500).send('Server error. 根据id查找中出错routerr.js中62行出错' )
        }
        res.render('edit.html', {
            student: student
        })
    })
})

router.post('/students/edit', function (req, res) {
    let student = req.body
    Student.findByIdAndUpdate(student.id.replace(/"/g, ''), student, function (err) {
        if (err) {
            return res.status(500).send('Server error. 保存routerr.js中35行出错' )
        }
        console.log('修改新用户成功了...')
        res.redirect('/')
    })
})

// 把router 导出
module.exports = router
* 6. 使用Node 操作MySQL数据库
  • 安装
cnpm i -S mysql
  • 示例代码
let mysql = require('mysql')

// 创建连接
let connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'AI123456',
    database: 'yyy'
})

// 连接数据库
connection.connect(function(err) {
    if (err) {
        console.log('链接失败!')
    } else {
        console.log('链接成功!')
    }
})

// 执行数据操作
connection.query('SELECT * FROM `students`', (err, results, fields) => {
    if(err) throw err
    console.log(results)
})


// 关闭连接
connection.end()

* 7. Promise
  • callback 回调地狱

    • 嵌套 callback
  • 为了解决回调地狱嵌套 ES6新增 Promise API

  • Promise 容器中 存放了一个异步任务 但是内部都是封装一个异步任务

    • 有三种状态: Pending Resolved =》成功了 Rejected =》 失败了
  • **Promise 使用 案例 **

    let fs = require('fs')
    
    // ES6 中新增一个API Promise
    // Promise 是一个构造函数
    
    // console.log(1)
    
    // 创建 Promise 容器
    // 给别人一个承诺 I promise you
    //      Promise 容器一旦创建 就开始执行里面的代码
    let p1 = new Promise(function (resolve, reject) {
        // console.log(2)
        fs.readFile('./a.txt', 'utf8', (err, data) => {
            if (err) {
                // 失败了,承诺容器中的任务失败了
                // 把容器的 Pedding 状态改为 Rejected
                // 调用 reject 就相当于调用了 then 方法的第二个参数函数
                reject(err)
            } else {
                // console.log(3)
                // 承诺容器中的任务成功了
                // console.log(data)
                // 把容器中的 Pedding 状态改为成功, Resolved
                // 这里调用的 resolve 方法实际上就是 then 方法传递的那个 function 
                resolve(data)
            }
        })
    })
    
    let p2 = new Promise(function (resolve, reject) {
        fs.readFile('./b.txt', 'utf8', (err, data) => {
            if (err) {
                reject(err)
            } else { 
                resolve(data)
            }
        })
    })
    
    let p3 = new Promise(function (resolve, reject) {
        fs.readFile('./c.txt', 'utf8', (err, data) => {
            if (err) {
                reject(err)
            } else { 
                resolve(data)
            }
        })
    })
    
    // p1 是那个承诺的实例
    // 当p1 成功了 然后(then)做指定的操作
    // then方法接收的就是 resolve 函数  相当于调用 resolve 方法就是调then里面的
    // 可以 then 到底 上面的 return 作为下面新的 then
    p1.then((data) => {
        console.log(data) // 先执行
        // return 了一个 Promise 对象的时候 后面的 then 中的方法就是第一个参数 p2
        return p2
    }, (err) => {
        console.log('读取文件失败', err)
    }).then((data) => { // p2 读取到的数据
        console.log(data)
        return p3
    }, (err) => {
        console.log(err)
    }).then((data) => {
        console.log(data)
    }, (err) => {
        console.log(err)
    })
    
    // console.log(4)
    
  • **使用 Promise 封装 fs **

let fs = require('fs')
const { fileURLToPath } = require('url')

// Promise 版本的封装读取文件
function pReadFile (FilePath) { 
    // return 一个 Promise 对象 调用此方法时直接 then 就 ok
    return new Promise(function (resolve, reject) {
        fs.readFile(FilePath, 'utf8', (err, data) => {
            if (err) {
                reject(err)
            } else {
                resolve(data)
            }
        })
    })
}

// 一直 then 就可以按顺序读取 a.txt   b.txt    c.txt
pReadFile('./a.txt').then((data) => {
    console.log(data)
    return pReadFile('./b.txt')
}, (err) => {
    console.log(err)
}).then((data) => {
    console.log(data)
    return pReadFile('./c.txt')
}, (err) => {
    console.log(err)
}).then((data) => {
    console.log(data)
}, (err) => {
    console.log(err)
})

  • ** 使用 Promise 封装 ajax 中的 get 方法
        // 使用 Promise 封装 ajax 的 get 方法
        function pGet(url) {
            return new Promise((resolve, reject) => {
                // XMLHttpRequest 在 node 环境下不支持使用
                let xhr = new XMLHttpRequest()
                xhr.onload = function () {
                    resolve(xhr.responseText)
                }

                xhr.onerror = function (err) {
                    reject(err)
                }

                xhr.open('get', url, true)
                xhr.send()
            })
        }

        pGet('./c.txt').then((data) => {
            console.log(data)
        }, (err) => {
            console.log(err)
        })
7.1 Promise 数据库操作案例
let mongoose = require('mongoose')

mongoose.connect('mongodb://127.0.0.1:27017/test', {
    useNewUrlParser: true,
    useUnifiedTopology: true
})

mongoose.Promise = global.Promise

// 设计集合结构(表结构)
let userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true // 必须有
    },
    password: {
        type: String,
        required: true
    },
    email: {
        type: String
    }
})

// 将文档结构发布为模型
let User = mongoose.model('User', userSchema)

User.findOne({
    username: '张三'
}, (err, ret) => {
    if (err) {
        console.log('出错啦...')
    } else {
        console.log('可以执行下面的保存操作啦...', ret)
    }

}).then((user) => {
    if (user) {
        console.log('用户已存在!')
    } else {
        // 用户不存在 可以注册
        return new User({
            username: '张三',
            password: '147258369',
            email: '无'
        }).save((err) => {
            if (err) {
                console.log('保存失败!')
            } else {
                console.log('保存成功!')
            }
        })
    }
}).then((ret) => {
    
})

* 8. path模块
  • path.basename

    • 获取一个路径的文件名(默认包含扩展名)
  • **path.resolve(…arr) **

    • 自动将数组解析成一个绝对的路径 其中数组需要解构
  • path.dirname

    • 获取一个路径中的目录部分
  • path.extname

    • 获取一个路径中的扩展名部分
  • path.parse

    • 把一个路径转为一个对象
      • 包含:root 根路径
      • dir 目录
      • base 包含后缀名的文件名
      • ext 后缀名
      • name 不包含后缀名的文件名
  • path.join

    • 当你需要进行路径拼接的时候,推荐使用该方法
  • path.isAbsoulte

    • 判断一个路径是否是绝对路径
  • Node 中的其他成员

  • 在每个模块中,除了 requireexports 等模块相关API 之外,还有两个特殊的成员

    • _dirname 可以动态的用户获取当前文件所属目录的绝对路径
    • _filename 可以动态的用来获取当前文件的绝对路径
      • 在文件操作中,使用相对路径是不可靠的,因为在 Node 中文件操作的路径被设计为相对于执行 node 命令所处的路径(不是错误 而是 node 是这样设计的
  • _dirnamefilename 是不受执行 node 命令所属路径影响的

    • 推荐多使用这两个动态的绝对路径
* 9. MD5加密和系统模块
9.1. MD5加密

* 导入包: npm install blueimp-md5

let md5 = require('blueimp-md5')
let password = md5(md5(body.password))
9.2. 系统模块
let os = require('os')
// 查看 CPU 信息
console.log(os.cpus())
// 查看整个内存大小
console.log(os.totalmem())
// 查看系统架构 x64
console.log(os.arch())
// 查看剩余内存量
console.log(os.freemem())
// 查看系统平台
console.log(os.platform())
*10. Session 保存登录状态
  • 在Express 这个框架中 默认不支持 Session 和Cookie

  • **使用第三方中间件:express-session 来解决

    • 安装第三方中间件

      npm install express-session
      
    • 配置

      // 该插件会为 req 对象添加一个成员 req.session 默认是一个对象
      // 导入模块
      let session = require('express-session')
      // 配置(一定要在 app.use(router) 之前)
      // 当把这个插件配置好之后 可以提供 req.session 来访问和配置 Session 成员
      // 添加 Session 数据 req.session.foo = 'bar'
      // 访问 Session 数据 req.session.foo
      app.use(session({
        // 配置加密字符串, 它会在原有加密的基础上加上这个字符串拼起来去加密
        // 目的是为了增加安全性, 防止客户端恶意伪造
        secret: 'bright',
        resave: false,
        saveUninitialized: true // 设置为 true 后, 无论你是否使用 Session 都会生成 一把钥匙
      }))
      
      • 提示:默认 Session 数据是内存存储的,服务器一旦重启就会丢失,真正的生产环境会把 Session 进行持久化存储
* 11. Express 中的中间件
  • 在 Express 中对中间件有几种分类
    • 不关心请求路径和请求方法的中间件
      • 也就是说任何请求都会进入这个中间件
      • app.use()
      • 一共有三个参数 request response next 方法是将控制权交给下一个 中间件
    • 关心请求路径的中间件 以 /xxx 开头的路径中间件
      • 调 next() 也是需要匹配的,并不是紧挨着的
* 12. node 的输入与输出
// 导入 readline 包
let readline = require('readline')
let fs = require('fs')
// 实例化接口对象
let rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
})

// request 方法
// 设置 rl 提问事件
/* rl.question('你的名字是?', (answer) => {
    console.log('我的名字是:' + answer)
    // 不加 close 则程序不会结束
    rl.close()
}) */

function rlQuestion (title) {
    return new Promise((resolve, reject) => {
        rl.question(title, (answer) => {
            resolve(answer)
        })
    })
}

async function createPackage () {
    let name =  await rlQuestion('你的包名?')
    let description = await rlQuestion('你的描述信息?')
    let main = await rlQuestion('主程序入口文件?')
    let author = await rlQuestion('你的包的作者?')
    let content = `{
        "name": "${name}",
        "version": "1.0.0",
        "description": "${description}",
        "main": "${main}",
        "scripts": {
          "test": "echo \\"Error: no test specified\\" && exit 1"
        },
        "keywords": [],
        "author": "${author}",
        "license": "ISC"
      }`
      console.log(content)
      let fd = fs.openSync('package0.json', 'w')
      fs.write(fd, content, () => {})
      // 最后写完关闭, 关闭输入进程
      rl.close()
}

createPackage()

// 监听 rl.close() 当调用时 结束输入输出流
rl.on('close', () => {
    process.exit(0)
})

* 13. node 事件 (是单线程的)
  • 原始的写法

    let fs = require('fs')
    
    fs.readFile('./1.txt', {flag: 'r', encoding: 'utf-8'}, (err, data) => {
        if (err) {
            console.log(err)
        } else {
            brEvent.emit('fileSuccess', data)
        }
    })
    
    let brEvent = {
        event: {
            // fileSuccess: [fn,fn]
        },
        on:function (eventName, eventFn) {
            if(this.event[eventName]) {
                this.event[eventName].push(eventFn)
            } else {
                this.event[eventName] = []
                this.event[eventName].push(eventFn)
            }
        },
        emit:function (eventName, eventMsg) {
            if (this.event[eventName]) {
                this.event[eventName].forEach(itemFn => {
                    itemFn(eventMsg)
                })
            }
        }
    }
    
    brEvent.on('fileSuccess', (eventMsg) => {
        console.log('数据库查看所有学员信息', eventMsg)
    })
    
    brEvent.on('deleteStu', (eventMsg) => {
        console.log('删库跑路', eventMsg)
    })
    
  • node提供的

    let events = require('events')
    let ee = new events.EventEmitter()
    let fs = require('fs')
    /*
    	ee.on('监听的事件的名称', '触发时传来的东西')
    	ee.emit('触发某一事件的名称', '传递的参数')
    */
    ee.on('hello', (eventMsg) => console.log(eventMsg))
    
    ee.on('world', eventMsg => console.log(eventMsg))
    
    // 封装 读取文件的 Promise 版本
    function brReadFile (path) {
        return new Promise((resolve, reject) => {
            fs.readFile(path, {encoding: 'utf-8'}, (err, data) => {
                if (err) {
                    console.log(err)
                } else {
                    // console.log(data)
                    resolve(data)
                }
            })
        })
    }
    
    brReadFile('./1.txt').then((data) => {
        ee.emit('hello', 'hello执行了' + data)
        ee.emit('world', 'world执行了' + data)
    })
    
    async function test () {
        let data = await brReadFile('./1.txt')
        ee.emit('hello', 'hello又一次执行了' + data)
    }
    
    test()
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值