跳至主要內容

NodeJs

SunSeekerX大约 12 分钟

NodeJs

更新时间:2020-05-08 14:47:44

1:模块化编程

node 的 require 用来引入模块,没有全局作用域,只有模块作用域

require

  • 用来加载执行模块里面的代码

  • 拿到被加载文件的模块导出的对象

  • 每个模块都提供了一个对象:exports

  • 外部需要访问的变量都需要挂载到 exports 对象中

  • 核心模块

  • 用户自己编写的模块

//通过require引入其他的js文件,可以省略文件后缀名
//相对路径中的./不能省略
require('./2')

2:响应类型

  • 服务器默认发送的是 utf8,但是浏览器不知道是 utf8 会按照操作系统默认的编码去解析,这样就会乱码

  • 中文操作系统默认是 bgk 编码

  • 在 http 协议中 Content-Type 告诉浏览器发送的数据时什么类型的

  • 一般是指字符编码,图片视频等资源一般不用设置编码

//设置响应头为utf8解决乱码
res.setHeader('Content-Type', 'text/plain; charset=utf-8')

3:更换淘宝镜像

npm config set registry http://registry.npm.taobao.org/

#2.换成原来的

npm config set registry https://registry.npmjs.org/

4:模块的 exports

  • exports 默认为 module.exports 的引用

5:Express

封装了 http 常用的接口

5.1:开放静态资源

//当没有第一个参数的时候,访问的时候可以通过去掉这个路径直接使用资源名来访问
app.use('/public/', express.static('./public/'))

5.2:在 express 中获取 post 请求数据

在 express 没有内置 post 请求 api,这里使用一个中间件body-parser

安装:

$ npm install body-parser

实例:

var express = require('express')
//引包
var bodyParser = require('body-parser')

var app = express()

//配置bodyParser,配置完了在res请求对象上对多出来一个属性:body
//可以直接通过req.body来获取请求体数据
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))

// parse application/json
app.use(bodyParser.json())

app.use(function (req, res) {
  res.setHeader('Content-Type', 'text/plain')
  res.write('you posted:\n')
  res.end(JSON.stringify(req.body, null, 2))
})

6:使用 nodemon 修改代码自动重启服务器

  • 全局安装 nodemon
npm install -g nodemon
  • 使用 nodemon 文件名启动服务器

7:使用模板引擎

  • 安装
npm install --save art-template
npm install --save express-art-template
  • 配置
//配置模板引擎,第一个参数表示,渲染什么后缀名使用模板引擎,一般为art或者html
//不需要引入art-template因为express-art-template依赖art-template
app.engine('art', require('express-art-template'))
  • 使用
//express为相应的对象提供了一个方法 render
//render默认不可用,如果配置了模板引擎就可以使用了

//第一个参数不能写路径,默认会去项目的views文件夹中寻找模板文件
res.render('index.html', {})

8:开放静态目录

//开放静态目录
app.use('/public/', express.static('./public/'))

9:路由模块的提取

//router.js
const express = require('express')
//创建一个路由容器
const router = express.Router()
//把路由都挂载到router容器上
router
  .get('/', function (req, res) {
    res.send('hello world' + 'nodemon')
  })
  .get('/login', function (req, res) {
    res.send('login page')
  })
//把router导出
module.exports = router

//app.js使用
const express = require('express')
const router = require('./router')
//创建app

const app = express()

//当没有第一个参数的时候,访问的时候可以通过去掉这个路径直接使用资源名来访问
app.use('/public/', express.static('./public/'))

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

app.listen(3000, function () {
  console.log('express app is running...')
})

10:文件结构

  • project/views:默认 html 视图文件

  • project/public:开放的静态资源目录

  • app.js:入口模块(

    • 启动服务,
    • 做一些服务相关的配置,
    • 例如模板引擎,body-parser 解析表单的 post 请求体
    • 挂载路由
    • 提供静态资源服务)
  • router.js:路由模块

11:重点:回调函数

在异步操作中想要得到异步操作的结果对象必须通过回调函数,没有其他方法

12:mongodb

12.1:启动和关闭数据库

启动:

#mongodb默认使用执行命令行所处盘符下的/data/db作为自己的数据存储目录
# 第一次执行先新新建一个/data/db 目录
#命令行输入
mongodb

如果想要修改数据存储路径

mongod --dbpath=数据目录存储路径

关闭

直接 X 掉

12.2:链接和退出数据库

#直接新建命令行输入mongo就可以了,默认链接本机的mongo服务
mongo

#退出,输入exit回车
exit

12.3:基本命令

  • show dbs

    • 查看当前数据库列表
  • db

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

    • 切换到指定的数据库(没有回新建)

12.4:在 node 中操作 mongo 数据库

  • 使用官方的 mongodb 包来操作 mondb 数据
  • 使用第三方 mongoose 来操作 mongodb,mongoose 所有 api 都支持 promise
  • 指定的数据库不需要存在,插入第一条数据的时候自己会创建

mongoose基于官方的进行了封装

地址:https://mongoosejs.com/open in new window

12.4:增加数据

  • 建立模型
const mongoose = require('mongoose')
const Schema = mongoose.Schema
//链接mongo数据库

//指定的数据库不需要存在,插入第一条数据的时候自己会创建
mongoose.connect('mongodb://localhost:27017/demo', { useNewUrlParser: true })

//设计集合结构(表结构)
//字段名称就是表结构中的属性名称
//值
// 约束的目的是为了保证的数据的完整性,不要有脏数据
var userShema = new Schema({
  username: {
    type: String,
    required: true, //必须有
  },
  password: {
    type: String,
    required: true,
  },
  email: {
    type: String,
  },
})
  • 增加数据
//将文档架构发布为模型
//  mongoose.model 方法就是用来将一个架构发布为model
// 第一个参数:传入一个大写的名词单数字符串用来表示你的数据库名称
//            mongoose 会自动将大写的名词的字符串生成小写复数的集合名称
//              例如这里的User最终会变为users集合名称
let User = mongoose.model('User', userShema)

// 有了模型构造函数,就可以对集合中的数据中进行操作

const admin = new User({
  username: 'admin',
  password: '123123',
  email: 'admin@admin.com',
})

admin.save(function (err, ret) {
  if (err) {
    console.log('保存失败')
  } else {
    console.log('保存成功')
    console.log(ret)
  }
})

12.5:查询数据

//通过模型查询数据,查出所有
User.find(function (err, data) {
  if (err) {
    console.log('查询失败')
  } else {
    console.log(data)
  }
})

//按条件查询数据
User.find(
  {
    username: 'admin',
  },
  function (err, data) {
    if (err) {
      console.log('查询失败')
    } else {
      console.log(data)
    }
  }
)

12.6:删除数据

// 删除数据
User.deleteMany(
  {
    name: 'admin',
  },
  function (err, data) {
    if (err) {
      console.log('err')
    } else {
      console.log(data)
    }
  }
)

12.7: 更新数据

// 更新数据
User.findByIdAndUpdate(
  '5c52f00c1c2c490414c2e025',
  {
    username: 'admin1',
  },
  function (err, data) {
    if (err) {
      console.log('err')
    } else {
      console.log(data)
    }
  }
)

13:使用 node.js 操作 mysql 数据库

var mysql = require('mysql')
//创建数据库链接
var connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: '12345678900',
  database: 'nodejstest',
})

//链接数据库
connection.connect()

// 执行查询
connection.query('SELECT * FROM USERS', function (error, results, fields) {
  if (error) throw error
  console.log(results)
})

//关闭链接
connection.end()

13.1 node.js mysql 的封装(事务异步 BUG)

// 数据库查询模块

const mysql = require('mysql')
const pool = mysql.createPool({
  host: 'localhost',
  port: 3306,
  database: 'test',
  user: 'root',
  password: '12345678900',
  acquireTimeout: 15000, // 连接超时时间
  connectionLimit: 100, // 最大连接数
  waitForConnections: true, // 超过最大连接时排队
  queueLimit: 0, // 排队最大数量(0 代表不做限制)
})

// 基本查询
exports.query = function (sql, param) {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      if (err) {
        reject(err)
        return
      }
      connection.query(sql, param, (error, result) => {
        if (error) {
          reject(error)
        } else {
          resolve(result)
        }
        pool.releaseConnection(connection)
      })
    })
  })
}

// 开启事物查询
exports.transaction = function (sqlArr) {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      if (err) {
        reject(err)
        return
      }
      connection.beginTransaction((err) => {
        if (err) {
          reject(err)
        }
        for (let i = 0; i < sqlArr.length; i++) {
          connection.query(sqlArr[i].sql, sqlArr[i].param, (err, result) => {
            if (err) {
              connection.rollback(() => {
                reject(err)
              })
            }
          })
        }
        connection.commit((err) => {
          if (err) {
            connection.rollback(() => {
              reject(err)
            })
          }
        })
        // 事务执行成功,释放连接
        resolve('Transaction compolete')
        pool.releaseConnection(connection)
      })
    })
  })
}

13.2 node.js mysql 的封装(普通操作)

// 数据库查询模块
const mysql = require('mysql')
const pool = mysql.createPool({
  host: 'localhost',
  port: 3306,
  database: 'test',
  user: 'root',
  password: '12345678900',
  acquireTimeout: 15000, // 连接超时时间
  connectionLimit: 20, // 最大连接数
  waitForConnections: true, // 超过最大连接时排队
  queueLimit: 0, // 排队最大数量(0 代表不做限制)
})

// 基本查询
exports.query = function (sqlObj) {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      if (err) {
        return reject(err)
      }
      connection.query(sqlObj.sql, sqlObj.params, (error, result) => {
        if (error) {
          reject(error)
        } else {
          resolve(result)
        }
        connection.release()
      })
    })
  })
}

// 事务封装存在异步bug

13.3 node.js mysql 的封装(比较好)

/*
 *  name:node.js操作mysql数据库模块
 *  author:SunSeekerX
 *  time:2019年3月19日23点04分
 * */
const mysql = require('mysql'),
  config = require('./config'),
  pool = mysql.createPool(config.mysqlConfig)

// 基本查询
exports.query = function (sqlObj) {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      // 从连接池获取连接
      if (err) {
        return reject(err)
      } // 获取连接失败,返回错误
      connection.query(sqlObj.sql, sqlObj.params, (error, result) => {
        connection.release() //释放连接
        error ? reject(error) : resolve(result) //查询结果
      })
    })
  })
}

// 开启事物查询
exports.transaction = function (sqlArr) {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      // 从连接池获取连接
      connection.beginTransaction(async (err) => {
        //开始事务
        if (err) {
          return reject(err)
        } // 获取连接失败,返回错误
        let result = [], // 结果集
          errInfo = null //错误对象
        for (let i = 0; i < sqlArr.length; i++) {
          // 循环查询
          try {
            result.push(
              await new Promise((resolve, reject) => {
                //将查询结果放进结果集
                connection.query(sqlArr[i].sql, sqlArr[i].params, (err, result) => {
                  //查询
                  err ? reject(err) : resolve(result)
                })
              })
            )
          } catch (e) {
            // sql语句执行出错,跳出循环,不继续执行
            errInfo = e
            break
          }
        }
        pool.releaseConnection(connection) // 释放链接
        if (errInfo) {
          connection.rollback(() => {
            //有数据条目执行失败, 回滚代码
            reject(errInfo)
          })
        } else {
          connection.commit((err) => {
            // 语句全部执行成功,commit提交
            err ? reject(err) : resolve(result)
          })
        }
      })
    })
  })
}

14:promise (异步编程才会有)

  • 原因:如果异步操作很多需要嵌套,这样就形成了代码嵌套非常深的情况

  • 解决:为了解决异步编码嵌套很深的问题,EcmaScript 6 新增了一个 api:promise

  • 说明:Promise 本身不是异步的,但是里面往往封装了一个异步任务

  • 封装的 Promise 版本的读文件方法

var fs = require('fs')

function pReadFile(filePath) {
  return new Promise(function (resolve, reject) {
    fs.readFile(filePath, 'utf8', function (err, data) {
      if (err) {
        //promise容器中的任务失败了
        //console.log(err)
        //把容器的pending 状态变为rejected 状态
        // 调用rejected 方法就是相当于调用了them方法的第二个参数函数
        reject(err)
      } else {
        // promise容器中的任务成功了
        // console.log(data);
        // 把容器的pending 状态变为resolve 状态
        // 这里调用的resolve 方法实际上就是then方法 传递的那个function
        resolve(data)
      }
    })
  })
}

pReadFile('./data/a.txt')
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/b.txt')
  })
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/c.txt')
  })
  .then(function (data) {
    console.log(data)
  })
const mysql = require('mysql')
const config = require('./config')
const pool = mysql.createPool({
  host: config.host,
  port: config.port,
  database: config.database,
  user: config.user,
  password: config.password,
})

function query(sql) {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      if (err) {
        console.log('数据库连接失败')
        return
      }
      connection.query(sql, (error, result) => {
        if (error) {
          console.log('执行sql语句失败')
          reject(error)
        } else {
          resolve(result)
        }
        connection.release()
      })
    })
  })
}

15:关于 es6

地址:http://es6.ruanyifeng.com/open in new window

16: 在 node.js 中使用 token 认证

  • 时间为一串数字的格式化
/*
需求

   js中接收到后台返回的json字符串中的日期类型的字段都变成了一串数字,形如:1500341149000。所以我们需要将这个串格式化形如:2017-07-18 09:25:49.

   直接上代码:

    1、先把字符串进行日期的封装 var date = new Date(1500341149000);

    2、封装转换函数

*/

Date.prototype.Format = function (fmt) {
  //author:wangweizhen
  var o = {
    'M+': this.getMonth() + 1, //月份
    'd+': this.getDate(), //日
    'h+': this.getHours(), //小时
    'm+': this.getMinutes(), //分
    's+': this.getSeconds(), //秒
    'q+': Math.floor((this.getMonth() + 3) / 3), //季度
    S: this.getMilliseconds(), //毫秒
  }
  if (/(y+)/.test(fmt))
    fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
  for (var k in o)
    if (new RegExp('(' + k + ')').test(fmt))
      fmt = fmt.replace(
        RegExp.$1,
        RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
      )
  return fmt
}

// 3、调用函数格式化:date.Format("yyyy-MM-dd hh:mm:ss");
  • 设置过期时间为 1970 年问题

17 : nodeJS 中 express 框架设置全局跨域请求头

18: 在服务端使用 session 来保存登陆状态

  • express 默认不支持 session 和 cookie,需要通过第三方中间件 express-session 来解决
  • 1:下载
npm i express-session
  • 2:配置, 一定要放在路由之前
app.use(
  session({
    secret: 'keyboard cat', // 配置加密字符串,增加安全性
    resave: false,
    saveUninitialized: true, // 无论你是否使用session,默认分配一把钥匙
  })
)
  • 3:使用
// 添加session数据
req.session.object = 'hello'

// 获取session数据
req.session.object

19::中间件

// 应用程序级别中间件
// 不关心路径和请求的方法中间件,任何请求都会进入这个中间件
// 中间件本身是一个方法,接收三个参数{Request:请求对象, Response:相应对象, next:下一个中间件}
// 如果不调用next() 方法,则请求会停留在这个中间件
app.use(function (req, res) {
  console.log('请求进来了')
})

// 以/xxx开头的路径中间件
app.use('/a', function (req, res) {
  console.log('请求进来了')
})

// 路由级别中间件
// 严格匹配请求方法和请求路径的中间件
app.get('/a', function (req, res) {
  console.log('请求进来了')
})

// 错误中间件
// 当调用next(err),传递了参数,将直接找到带有4个参数的应用级别的中间件
// 配置404中间件
app.use(function (req, res, next) {
  res.status(404).json({
    resultCode: 404,
  })
})
// 配置错误处理中间件
app.use(function (err, req, res, next) {
  res.status(500).json({
    resultCode: 500,
    message: err.message,
  })
})

19.1 图片上传中间件

// 需要设置上传类型为multipart/form-data

const multer = require('multer')
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    // 接收到文件后输出的保存路径(若不存在则需要创建)
    cb(null, './public/upload/')
  },
  filename: (req, file, cb) => {
    // 将保存文件名设置为 时间戳 + 文件原始名,比如 151342376785.jpg
    const fileformat = file.originalname.split('.')
    cb(null, Date.now() + '.' + fileformat[fileformat.length - 1])
  },
})
const upload = multer({ storage: storage })

router.post('/uploadFile', upload.single('file'), (req, res) => {
  const file = req.file
  // 接收文件成功后返回数据给前端
  res.json({ resultCode: 40000, file: file })
})
<form method="post" action="http://localhost:3000/uploadFile" enctype="multipart/form-data">
  <input type="file" name="file" />
  <button type="submit">提交</button>
</form>

20 Api 接口文档编写模板

获取制定项目的分类信息(/api/index)
接口功能

获取指定项目的分类信息

URL

/api/index

支持格式

JSON

HTTP 请求方式

GET

请求参数
参数类型必选说明
namestringture请求的项目名
typeinttrue请求项目的类型。1:类型一;2:类型二 。
返回字段
返回字段字段类型说明
statusint返回结果状态。0:正常;1:错误。
companystring所属公司名
categorystring所属类型
接口示例

地址:/api/index?name="可口可乐"&type=1

{
    "statue": 0,
    "company": "可口可乐",
    "category": "饮料"
}
上次编辑于:
贡献者: SunSeekerX