NodeJs
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回车
exit12.3:基本命令
show dbs- 查看当前数据库列表
 
db- 参看当前操作的数据库
 
use 数据库名- 切换到指定的数据库(没有回新建)
 
12.4:在 node 中操作 mongo 数据库
- 使用官方的 mongodb 包来操作 mondb 数据
 - 使用第三方 mongoose 来操作 mongodb,mongoose 所有 api 都支持 promise
 - 指定的数据库不需要存在,插入第一条数据的时候自己会创建
 
mongoose基于官方的进行了封装
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()
      })
    })
  })
}
// 事务封装存在异步bug13.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)
  })- node.js 封装关于 MySQL 操作的 js
 - 地址:https://blog.csdn.net/qq_33358824/article/details/80699200
 
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
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 框架设置全局跨域请求头
Node.js 如何设置允许跨域: https://blog.csdn.net/u012149969/article/details/81145144
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.object19::中间件
// 应用程序级别中间件
// 不关心路径和请求的方法中间件,任何请求都会进入这个中间件
// 中间件本身是一个方法,接收三个参数{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
请求参数
| 参数 | 类型 | 必选 | 说明 | 
|---|---|---|---|
| name | string | ture | 请求的项目名 | 
| type | int | true | 请求项目的类型。1:类型一;2:类型二 。 | 
返回字段
返回字段 字段类型 说明 status int 返回结果状态。0:正常;1:错误。 company string 所属公司名 category string 所属类型 
接口示例
地址:/api/index?name="可口可乐"&type=1
{
    "statue": 0,
    "company": "可口可乐",
    "category": "饮料"
}