Node学习笔记 函数自调用和crud增删改查

坚强是说给别人听的谎言 提交于 2019-12-12 16:52:53

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

函数自调用

一般情况下,把函数作为参数的目的是为了获取函数内部的异步操作结果

  • 下方代码是一个同步示例

    function add (x + y) { // 函数方法
        return x + y
    }
    add(10, 20) // 调用函数
    
  • 下方代码是一个异步函数

    console.log('setTimeout external before')
    setTimeout(function () {
        console.log('setTimeout inside before')
        console.log('setTimeout inside after')
    }, 0)
    console.log('setTimeout external after')
    

    由于 setTimeout 是异步函数,所以在运行时浏览器不会等待(哪怕是执行时间为 0 )函数执行,所以最终输出结果为

    setTimeout external before
    setTimeout external after
    setTimeout inside before
    setTimeout inside after
    

    之所于得出以上结果,是由于 javascript 是单线程的

  • 获取异步函数内部的操作结果

    function add (x, y) {
        setTimeout(function () {
        	var ret = x + y
        }, 0)
    }
    

    如何能够获取函数内部 setTimeout 异步函数中 ret 的值?首先来看两个错误案例

    function add (x, y) {
        setTimeout(function () {
        	var ret = x + y
            return ret
        }, 0)
    }
    add(10, 20)
    

    运行结果为 Uncaught ReferenceError: ret is not defined

    原因在于 setTimeout 中的 ret 返回出来的值,在 add 函数内部没有变量接收

    function add (x, y) {
        var ret
        setTimeout(function () {
        	ret = x + y
        }, 0)
        return ret
    }
    add(10, 20)
    

    运行结果为 undefined

    原因在于 setTimeout 异步函数的执行顺序为 var ret return ret 最后才是 setTimeout ,所以此时结果为 undefined

    通过回调函数 callback 获得函数内部异步方法的结果

    function add (x, y, callback) {
        setTimeout(function () {
        	var ret = x + y
            callback(ret)
        }, 0)
    }
    add(10, 20, (val) => {
        console.log(val)
    })
    

    运行结果为 undefined 30

    在代码中调用方法 add() 由三个参数组成,前两个参数传递给异步方法 setTimeoutret = x + y 用于计算,第三个参数为一个函数,此函数参数为形参,接收 ret 最终的运算结果作为实参

    这样就可以将函数内部的异步方法运行结果抛出对外使用了


CRUD 静态路由搭建

  • 首先需要几个静态页面:

    index.html 用于展示首页(信息列表)展示页面

    new.html 用于新增信息页面

    edit.html 用于对现有信息修改页面

    需要将静态页面放置于 views 中,以便 express 使用

  • 创建数据 JSON 文件,命名为 db.json

    {
        "students": [{
            "id": 4,
            "name":"张三三",
            "gender":"0",
            "age":"22"
        }]
    }
    
  • 新建项目:

    npm init -y // 快速搭建项目
    
  • 创建入口文件 app.js ,引入所需组件及配置

    // npm install express --save
    var express = require('express') // 引入 express
    
    // npm i art-template
    app.engine('art', require('express-art-template')) // 配置 express-art-template
    
    // npm install --save body-parser
    var bodyParser = require('body-parser')  // 引入 body-parser
    app.use(bodyParser.urlencoded({ // 配置 body-parser
      extended: false
    }))
    app.use(bodyParser.json())
    
  • 创建路由(为了更好的体现模块化开发,现将路由模块单独提出至 router.js )

    14nodejs(7天) 中提供了两种构建方式

    1. app 作为参数传递给 router.js此方式不推荐

      // route.js
      module.expotrs = (app) => {
          app.get('/', (req, res) => {}) // 渲染首页
          app.get('/new', (req, res) => {}) // 渲染添加信息页面
          app.post('/new', (req, res) => {}) // 提交添加的信息
          app.get('/edit', (req, res) => {}) // 渲染信息编辑页面
          app.post('/edit', (req, res) => {}) // 提交编辑后的信息
          app.get('/delete', (req, res) => {}) // 删除信息
      }
      
      // app.js
      var express = require('express') // 引入 express
      var router = require('./route') // 引入 router.js
      var app = express()
      router(app) // 将 app 作为参数传递给 router.js
      
    2. 使用 Express 包装路由(推荐使用此方式

      // route.js
      var express = require('express') // 引入 express
      var router = express.Router() // 使用 express 中 router 方法,创建一个路由容器
      // 把路由挂在到 router 上
      router.get('/', (req, res) => {}) // 渲染首页
      router.get('/new', (req, res) => {}) // 渲染添加信息页面
      router.post('/new', (req, res) => {}) // 提交添加的信息
      router.get('/edit', (req, res) => {}) // 渲染信息编辑页面
      router.post('/edit', (req, res) => {}) // 提交编辑后的信息
      router.get('/delete', (req, res) => {}) // 删除信息
      module.exports = router // 导出 router 容器
      
      // app.js
      var express = require('express') // 引入 express
      var router = require('./route') // 引入 router.js
      var app = express()
      app.use(router) // 把路由容器挂在到 app 服务中
      
  • 创建 Server 服务

    app.use('/public/', express.static('./public/')) // 开放 public 目录
    app.use(router) // 挂在 router 路由文件
    app.listen('2020', () => { // 绑定服务器端口号
      console.log('服务器启动成功')
    })
    

到此为止,静态文件的跳转就已经到此为止,下面将针对增删改查功能进行逐一的开发


CRUD 增删改查

将所有涉及增删改查的方法,都封装在 student.js 文件中

所有方法通过 exports 导出

最后将 student.js 文件导入 router.js 以便路由使用

方法通过函数自调用callback 结果导出

  • // student.js
    var fs = require('fs') // 引入核心组件 fs
    var dbPath = './db.json' // 引入原始数据文件
    exports.add = (student, callback) => {
        fs.readFile(dbPath, 'utf-8', (err, data) => {
            if (err) {
                return callback(err) // 如果 db.json 文件读取失败,则向外抛出 err
            }
            var students = JSON.parse(data).students // 将 dbPath 的 JSON 文本数据转换对象
            student.id = students[students.length - 1].id + 1
            students.push(student) // 将表单数据追加到 students
            var fileData = JSON.stringify({ // 将对象重新转换为文本
                students: students
            })
            fs.writeFile(dbPath, fileData, (err) => {
                if (err) { // 如果写入文件失败,则向外抛出 err
                    return callback(err)
                }
                callback(null) // 写入成功则不作为
            })
        })
    }
    
    // router.js
    var Student = require('./student') // 导入 student.js
    Student.add(req.body, (err) => {})
    // add 方法接收两个参数
    // 参数1(Object) 接收 需要添加的数据对象
    // 参数2(Function) 该 Function 参数接收 err
    
  • // student.js
    var fs = require('fs') // 引入核心组件 fs
    var dbPath = './db.json' // 引入原始数据文件
    exports.deleteId = function (id, callback) {
        fs.readFile(dbPath, 'utf8', function (err, data) {
            if (err) {
                return callback(err) // 如果 db.json 文件读取失败,则向外抛出 err
            }
            var students = JSON.parse(data).students // 将 dbPath 的 JSON 文本数据转换对象    
            var deleteId = students.findIndex(function (item) { // findIndex 方法专门用来根据条件查找元素的下标
                return item.id === parseInt(id)
            })    
            students.splice(deleteId, 1) // 根据下标从数组中删除对应的学生对象    
            var fileData = JSON.stringify({ // 把对象数据转换为字符串
                students: students
            })    
            fs.writeFile(dbPath, fileData, function (err) {
                if (err) { // 如果写入文件失败,则向外抛出 err
                    return callback(err)
                }
                callback(null) // 写入成功则不作为
            })
        })
    }
    
    // router.js
    var Student = require('./student') // 导入 student.js
    Student.deleteId(req.query.id, function (err) {})
    // deleteId 方法接收两个参数
    // 参数1(String) 接收 用于匹配的数据 id
    // 参数2(Function) 该 Function 参数接收 err
    
  • // student.js
    var fs = require('fs') // 引入核心组件 fs
    var dbPath = './db.json' // 引入原始数据文件
    exports.edit = (student, callback) => {
        fs.readFile(dbPath, 'utf-8', (err, data) => {
            if (err) {
                return callback(err) // 如果 db.json 文件读取失败,则向外抛出 err
            }
            var students = JSON.parse(data).students // 将 dbPath 的 JSON 文本数据转换对象
            var queryId = students.find((item) => { // 通过 req.body.id 找到 dbPath 中匹配的对象
                return item.id === parseInt(student.id)
            })
            for (var key in student) { // 替换需修改的对象
                queryId[key] = student[key]
            }
            var fileData = JSON.stringify({ // 将对象重新转换为文本
                students: students
            })
            fs.writeFile(dbPath, fileData, (err) => {
                if (err) { // 如果写入文件失败,则向外抛出 err
                    return callback(err)
                }
                callback(null) // 写入成功则不作为
            })
        })
    }
    
    // router.js
    var Student = require('./student') // 导入 student.js
    Student.edit(req.body.id, (err) => {})
    // edit 方法接收两个参数
    // 参数1(String) 接收 用于匹配的数据 id
    // 参数2(Function) 该 Function 参数接收 err
    
  • 查(所有)

    // student.js
    var fs = require('fs') // 引入核心组件 fs
    var dbPath = './db.json' // 引入原始数据文件
    exports.findAll = (callback) => {
    	fs.readFile(dbPath, 'utf-8', (err, data) => {
            if (err) {
                return callback(err) // 如果 db.json 文件读取失败,则向外抛出 err
            }
            callback(null, JSON.parse(data).students) // 读取成功则抛出数据
        })
    }
    
    // router.js
    var Student = require('./student') // 导入 student.js
    Student.findAll((err, students) => {})
    // find 方法接收一个 Function 参数,该 Function 又有两个参数
    // 参数1 接收 err
    // 参数2 接收 真实数据(JSON.parse(data).students)
    
  • 查(单个)

    // student.js
    var fs = require('fs') // 引入核心组件 fs
    var dbPath = './db.json' // 引入原始数据文件
    exports.findId = (queryId, callback) => {
        fs.readFile(dbPath, 'utf-8', (err, data) => {
            if (err) {
                return callback(err) // 如果 db.json 文件读取失败,则向外抛出 err
            }
            var students = JSON.parse(data).students // 将 dbPath 的 JSON 文本数据转换对象
            var student = students.find((item) => { // 通过 queryId 找到 dbPath 中匹配的对象
                return item.id === parseInt(queryId)
            })
            return callback(null, student)
        })
    }
    
    // router.js
    var Student = require('./student') // 导入 student.js
    Student.findId(req.query.id, (err, data) => {})
    // findId 方法接收两个参数
    // 参数1(String) 接收 用于匹配的数据 id
    // 参数2(Function) 该 Function 有两个参数,其一为 err ,其二为真实数据
    

将方法应用于 router.js ,最终 router.js 页面代码为

var fs = require('fs') // 引入核心组件 fs
var Student = require('./student') // 导入 student.js
var express = require('express') // 导入 express
var router = express.Router() // express 包裹路由

router.get('/', (req, res) => { // 渲染首页
    Student.findAll((err, students) => {
        if (err) { // 如果 findAll 抛出错误,则报 Server error,状态码 500
            return res.status(500).send('Server error.')
        }
        res.render('index.html', { // 如果数据读取成功,则渲染首页
            students: students
        })
    })
})

router.get('/new', (req, res) => { // 渲染添加信息页面
    if (err) { // 如果 findAll 抛出错误,则报 Server error,状态码 500
        return res.status(500).send('Server error.')
    }
    res.render('new.html')
})

router.post('/new', (req, res) => { // 提交添加的信息
    Student.add(req.body, err => { // 将 req.body 对象添加入 ./db.json
        if (err) { // 如果 findAll 抛出错误,则报 Server error,状态码 500
            return res.status(500).send('Server error.')
        }
        res.redirect('/') // 若添加成功,则页面重定向回首页
    })    
})

router.get('/edit', (req, res) => { // 渲染信息编辑页面
    Student.findId(req.query.id, (err, student) => { // 通过 req.query.id 在 ./db.json 中匹配数据
        if (err) { // 如果 findAll 抛出错误,则报 Server error,状态码 500
            return res.status(500).send('Server error.')
        }
        res.render('edit.html', { // 渲染当前数据的编辑页面
            student: student
        })
    })
})

router.post('/edit', (req, res) => { // 提交编辑后的信息
    Student.edit(req.body, (err) => { // 通过 req.body 在 ./db.json 中匹配数据,并将其替换
        if (err) { // 如果 findAll 抛出错误,则报 Server error,状态码 500
            return res.status(500).send('Server error.')
        }
        res.redirect('/') // 若添加成功,则页面重定向回首页
    })
})

router.get('/delete', (req, res) => { // 删除信息
    Student.deleteId(req.query.id, function (err) { // 通过 req.query.id 在 ./db.json 中匹配数据,并将其删除
        if (err) { // 如果 findAll 抛出错误,则报 Server error,状态码 500
            return res.status(500).send('Server error.')
        }
        res.redirect('/') // 若添加成功,则页面重定向回首页
    })
})

module.exports = router // 导出 router 容器

文章已同步我的个人博客:《Node学习记录 函数自调用和crud增删改查


资料参考:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!