Node.js学习笔记

Node.js是什么

  • Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine.

    • Node.js不是一门语言
    • Node.js不是库、不是框架
    • Node.js是一个JavaScript代码
    • 简单点来说就是Node.js可以解析和执行JavaScript代码
    • 以前只有浏览器可以解析执行JavaScript代码
    • 也就是说现在的JavaScript可以完全脱离浏览器来运行,一切都归功于Node.js
  • 浏览器中的JavaScript

    • EcmaScript
      • 基本的语法
      • if
      • var
      • function
      • Object
    • BOM
    • DOM
  • Node.js 中的JavaScript

    • 没有 BOX DOM
    • EcmaScript
    • 在Node这个JavaScript执行环境中为JavaScript提供了一些服务器级别的操作API
      • 例如在文件读写
      • 网络服务的构建
      • 网络通讯
      • http服务器
      • 等等。。。
  • 构建在Chrome的V8引擎之上

    • 代码只是具有特定格式的字符串
    • 引擎可以认识它,引擎可以帮你去解析和执行
    • Google Chrome的V8引擎是目前公认的解析执行JavaScript代码最快的
  • Node.js uses an eventt-driven, non-blocking I/O model that makes it lightweight and efficient

    • event-driven 事件驱动
    • non-blocking I/O model 非阻塞IO模型
    • lightweight and efficient轻量和高效
  • Node.js’ package ecosystem, npm, is the largest ecosystem of open source libaraies in the world.

    • npm 是世界上最大的开源生态系统
    • 绝大多数JavaScript相关的包都放在了npm上,这样做的目的是为了让开发人员更方便的去下载使用
    • npm install jquery

Node.js能做什么

  • Web服务器后台

  • 命令行工具

    • npm(node)
    • git(c语言)
    • hexo(node)
  • 对于前端开发工程师,接触node最多的是他的命令行工具

    • webpack
    • gulp
    • npm

预备知识

  • HTML

  • CSS

  • JavaScript

  • 简单的命令行操作

    • cd
    • dir
    • ls
    • mkdir
    • rm
  • 具有服务器开发经验更佳

能学到什么?

  • B/S编程模型
    • Browser - Server
    • back-end
    • 任何服务端技术这种BS编程模型都是一样的
    • node 只是作为学习BS编程模型的一个工具
  • 模块化编程
    • RequireJS
    • SeaJs
    • @import('文件路径')
    • 在Nide中可以像@import一样来引用加载JavaScript脚本文件
  • Node常用API
  • 异步编程
    • 回调函数
    • Promise
    • async
    • generator
  • Express Web 开发框架
  • Ecmascript 6

Hello World

1.创建编写JavaScript脚本文件

2.打开终端定位到脚本文件的目录

3.输入 node 文件名 执行对应的文件

注意:文件名不要使用 node.js 命名

  • 解析执行 JavaScript

  • 读写文件

  • http

  • 最简单的 http 服务:

    // 使用 node 构建一个 Web 服务器
    // 在 node 中提供了一个核心模块:http
    // http 这个模块的职责是帮你创建编写服务器
    
    // 1.加载 http 核心模块
    var http = require('http');
    
    // 2.使用 http.createServer() 方法创建一个 Web 服务器
    // 返回一个Server实例
    var server = http.createServer();
    
    // 3.服务器提供对数据的服务
    /*发请求
    * 接收请求
    * 处理请求
    * 给个反馈(发送响应)
    * 注册 requset 请求事件
    * 当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数:回调处理函数
    */
    server.on('request', function () {
           
    	console.log('收到客户端请求');
    })
    
    // 4.绑定端口号,启动服务器
    server.listen(3000, function () {
           
    	console.log('服务器启动成功,可以通过 http://127.0.0.1:3000/ 访问')
    });
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jEpmIFDF-1575955038352)(C:\Users\89349\AppData\Roaming\Typora\typora-user-images\1574495523937.png)]

Node 中的JavaScript

  • EcmaScript
    • 没有 DOM、BOM
  • 核心模块
  • 第三方模块
  • 用户自定义模块

核心模块

node 为 JavaScript 提供了很多服务器级别的 API,这些 API 绝大多数都被包装到了一个具名的核心模块中。

例如:文件操作的 fs 核心模块,http 服务构建的 http 模块,path 路径操作模块,os 操作系统信息模块。

只要说这个模块是一个核心模块,就必须要使用它

var fs = require('fs')
var http = require('http')

Web 服务器开发

ip 地址和端口号

  • ip 地址用来地位计算机
  • 端口号用来定给我具体得应用程序
  • 一切需要联网的软件都会占用一个端口号
  • 端口号的范围是 0 - 65536
  • 在计算机中有一些默认端口号,最好不要去使用
    • http服务的 80

Content-Type

http://tool.oschina.net/

请求对象 Request

响应对应 Response

在 Node 中使用模板引擎

统一处理静态资源

服务端渲染和客户端渲染的区别

  • 客户端渲染不利于 SEO 搜索引擎优化
  • 服务端渲染是可以被爬虫抓取到的,客户端异步渲染很难被爬虫抓取到
  • 所以真正的网站既不是纯异步也不是纯服务端渲染的,两者相结合
  • 例如:京东的商品列表就是采用的服务端渲染,目的是为了 SEO 搜索引擎优化
  • 而商品评论列表是客户端端渲染,它不需要 SEO 优化,更是为了用户体验

node 中的模块系统

使用 node 编写应用程序主要使用:

  • EcmaScript 语言
    • 和浏览器不一样,在 Node 中没有 BOM、DOM
  • 核心模块
    • 文件操作的 fs
    • http 服务的 http
    • url 路径操作模块
    • path 路径操作模块
    • os 操作系统信息
  • 第三方模块
    • art-template
    • 必须通过 npm 下载
  • 自己写的模块
    • 自己创建的文件

什么是模块化

  • 文件作用域
  • 通信规则
    • 加载 require
    • 导出

CommonJS 模块规范

在 node 中的 JavaScript 还有一个重要的概念:模块系统

  • 模块作用域
  • 使用 require 方法来加载模块
  • 使用 exports 接口对象用来导出模块中的成员
加载 require

语法:

var 自定义变量名称 = require('模块');

两个作用:

  • 执行被加载模块中的代码
  • 得到被加载模块中的 exports 导出接口对象
导出 exports
  • node 中是模块作用域,默认文件中所有的成员只在当前文件

  • 对于希望可以被其他模块访问的成员,我们就需要把这些公开的成员都挂载到 exports 接口对象中就可以了

导出多个成员(必须在对象中):

exports.a = 123;
exports.b = 'hello';
exports.c = function () {
     
    console.log('ccc');
}
exports.d = {
     
    foo : 'bar'
}

导出单个成员(拿到的就是:函数、字符串):

module.exports = 'hello';

以下情况还会覆盖:

// 一个模块需要直接导出某个成员
module.exports = 'hello';
// 后者会覆盖前者
module.exports = function (x, y) {
     
	return x + y;
}

也可以这样导出多个成员:

module.exports = {
     
	add: function (x, y) {
     
		return x + y;
	},
	str: 'hello'
}
原理解析

exports 和 module.exports 的一个引用

console.log(exports === module.exports); // => true

exports.foo = 'bar';

// 等价于
module.exports.foo = 'bar';
exports 和 module.exports 的区别
  • 每个模块中都有一个 module 对象

  • module 对象中有一个 exports 对象

  • 我们可以把需要导出的成员都挂载到 module.exports 接口对象中

  • 也就是:moudle.exports.xxx = xxx 的方式

  • 但是每次都 moudle.exports.xxx = xxx 很麻烦,点儿的太多了

  • 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:exports

  • exports === module.exports 结果为 trues

  • 所以对于:moudle.exports.xxx = xxx 的方式 完全可以:expots.xxx = xxx

  • 当一个模块需要导出单个成员的时候,这个时候必须使用:module.exports = xxx 的方式

  • 不要使用 exports = xxx 不管用

  • 因为每个模块最终向外 return 的是 module.exports

  • exports 只是 module.exports 的一个引用

  • 所以即便你为 exports = xx 重新赋值,也不会影响 module.exports

  • 但是有一种赋值方式比较特殊:exports = module.exports 这个用来重新建立引用关系的

  • 之所以让大家明白这个道理,是希望可以更灵活的去用它

  • Node 是一个比肩 Java、PHP 的一个平台

    • JavaScript 既能写前端也能写服务端
moudle.exports = {
     
  a: 123
}

// 重新建立 exports 和 module.exports 之间的引用关系
exports = module.exports

exports.foo = 'bar'
Array.prototype.mySlice = function () {
     
  var start = 0
  var end = this.length
  if (arguments.length === 1) {
     
    start = arguments[0]
  } else if (arguments.length === 2) {
     
    start = arguments[0]
    end = arguments[1]
  }
  var tmp = []
  for (var i = start; i < end; i++) {
     
    // fakeArr[0]
    // fakeArr[1]
    // fakeArr[2]
    tmp.push(this[i])
  }
  return tmp
}

var fakeArr = {
     
  0: 'abc',
  1: 'efg',
  2: 'haha',
  length: 3
}

// 所以你就得到了真正的数组。 
[].mySlice.call(fakeArr)
require 方法加载规则
  • 核心模块

    • 模块名
  • 第三方模块

    • 模块名
  • 用户自己写的

    • 路径
  • 优先从缓存中加载

  • 判断模块标识符

    • 核心模块
    • 第三方模块
    • 自己写的模块
blog
	a
    	node_modules
    b
    	main.js
// b 加载不到 node_modules

npm

  • node package manager
npm 网站

npmjs.com

npm 命令行工具

npm 的第二层含义就是一个命令行工具,只要安装了 node 就已经安装了 node。

npm 也有版本概念。

可以通过在命令行输入:

npm --version

升级 npm:

npm install --global npm
npm 常用命令

  • npm init

    • npm int -y 可以跳过向导,快速生成
  • npm install

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

    • 只下载
    • npm i 包名
  • 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 被墙问题

npm 存储包文件的服务器在国外,有时候会被墙,速度很慢,所以我们需要解决。

http://npm.taobao.org/ 淘宝的开发团队把 npm 在国内做了一个备份。

安装淘宝的 cnpm:

# 在任意目录下执行都可以
# --global 表示安装到全局
# --global 不能省略
npm install --global cnpm

接下来你安装包的时候把之前的 npm 替换成 cnpm

举个例子:

# 这里还是走国外的 npm 服务器,速度比较慢
npm install jquery

# 使用 cnpm 就会通过淘宝的服务器来下载 jquery
cnpm install jquery

如果不想安装 cnpm 又想使用淘宝的服务器来下载:

npm install jquery --registry=https://registry.npm.taobao.org

每次添加参数和麻烦,可以把这个选项添加到配置文件中:

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

# 查看 npm 配置信息
npm config list

只要经历了上面的命令配置,则以后所有的 npm install 都会默认通过淘宝的服务器来下载。

package.json


我们建议每一个项目都要有一个 package.json 文件(包描述文件,就像产品的说明书一样)

这个文件可以通过 npm init 的方式来自动初始化出来。 直接生成(npm init -y

PS H:\前端-19\14nodejs(7天)\mytest_node\day03\npm-pack> npm init                                                      This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (npm-pack)
version: (1.0.0) 0.0.1
description: 这是一个测试项目
entry point: (index.js) main.js
test command:
git repository:
keywords:
author: Mr.Zhang
license: (ISC)
About to write to H:\前端-19\14nodejs(7天)\mytest_node\day03\npm-pack\package.json:

{
     
  "name": "npm-pack",
  "version": "0.0.1",
  "description": "这是一个测试项目",
  "main": "main.js",
  "scripts": {
     
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Mr.Zhang",
  "license": "ISC"
}


Is this OK? (yes) yes
PS H:\前端-19\14nodejs(7天)\mytest_node\day03\npm-pack> npm install --save jquery
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN npm-pack@0.0.1 No repository field.

+ jquery@3.4.1
added 1 package from 1 contributor and audited 1 package in 13.807s
found 0 vulnerabilities

对于我们来讲,最有用的就是 dependencies 选项,可以用来帮助我们保存第三方包的依赖信息。

如果你的 node_modules 删除了也不必担心,只需要:npm install 就会自动把 package.json 中的 dependencies 中的所有的依赖项都下载下来。

  • 建议每个项目的根目录下都有一个 package.json 文件
  • 建议执行 npm install 包名 的时候都加上 --save 用来保存依赖项信息
package.json和 package-lock.json

npm5 以前没有 package-lock,json 这个文件。

npm5 以后才加入了这个文件。

当你安装包的时候, npm 都会生成或更新 package-lock.json 这个文件。

  • npm5 以后的版本安装包不需要加 --save 参数,它会自动保存依赖信息。

  • 当你安装包的时候,会自动创建或者更新 package-lock.json 这个文件。

  • package-lock.json 这个文件会保存 node_modules 中所有包的信息(版本、下载地址)

    • 重新安装的话会加快安装速度 npm install
  • 从文件来看,有一个 lock 称之为锁

    • 这个 lock 是为了锁定版本
    • 如果项目依赖了 1.1.1 版本
    • 如果你重新 install 其实会下载最新版本
    • 我们的目的是希望可以锁住 1.1.1 这个版本
    • 所有这个 package-lock.json 这个文件的另一个左右就是锁定版本号,防止升级成新版本

path 路径操作模块


  • path.basename
    • 获取一个路径的文件名(默认包含扩展名)
  • path.dirname
    • 获取一个路径中的目录部分
  • path.extname
    • 获取一个路径中的扩展名部分
  • path.parse
    • 把一个路径转换为对象
      • root 根路径
      • dir 目录
      • base 包含后缀名的文件名
      • ext 后缀名
      • name 不包含后缀名的文件名
  • path.join
    • 路径拼接
  • path.isAbsolute
    • 判断一个路径是否为绝对路径

node 中的其他成员


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

  • __dirname 动态获取 可以用来获取当前文件模块所属目录的绝对路径
  • __filename 动态获取 可以用来获取当前文件的绝对路径
  • __dirname__filename 不受执行 node 命令所属路径影响的

在文件操作中,使用相对路径不可靠,以为在 node 中文件操作的路径被设计为相对于对于执行 node 命令所处的路径(不是 bug)。

为了解决这个问题,使用绝对路径。

使用 __dirname 或者 __filename 来帮助解决问题。

结合 path.join()

path.join(__dirname, './a.txt')

补充:模块的路径标识和这里的路径没有关系

Express


原生的 http 在某些方面表现不足以应对我们的开发需求,所以我们使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码高度统一。

在 node 中,有很多 Web 开发框架,学习 express 为主。

  • http://expressjs.com/
起步
安装:
npm install --save express
hello world
var express = require('express');

// 1. 创建 app
var app = express();

app.get('/', function (req, res) {
     
	// res.write('hello ');
	// res.write('你好');
	// res.end();
	
	// res.end('hello world');
	

	res.send('hello world');
	// res.send('hello 你好');
})

app.listen(3000, function () {
     
	console.log('express running·····');
});
基本路由

路由器

  • 请求方法
  • 请求路径
  • 请求处理函数

get:

// 当你以 get 方法请求 / 的时候,执行对应的处理函数
app.get('/', function (req, res) {
     
    res.send('Hello World!')
})

post:

// 当你以 post 方法请求 / 的时候,执行对应的处理函数
app.post('/', function (req, res) {
     
    res.send('Hello World!')
})
静态服务
// 当以 /public/ 开头的时候,去 ./public/ 目录中去找对应的资源
app.use('/public', express.static('./public/'));
// 
// 当省略第一个参数的时候,则可以通过省略 /public 的方式访问
app.use(express.static('./public/'));
// 
// 必须是 /a/public目录中的资源名称
// a 相当于 public 的别名
app.use('/a', express.static('./public/'));

app.use('/public', express.static(path.join(__dirname, 'public')))
在 Express 中配置使用 art-template 模板引擎

安装:

npm install --save art-template
npm install --save express-art-template

配置:

app.engine('html', require('express-art-template'));

使用:

app.get('/', function (req, res) {
     
    // express 默认会去项目中的 views 目录中找 index.html
    res.render('index.html', {
     
        title: 'hello'
    })
})

如果希望修改默认的 views 视图渲染存储目录,可以:

// 注意:第一个参数 views 不能写错
app.set('views', 目录路径)
在 Express 中获取表单 GET 请求参数

Express 内置了一个 API,可以直接通过 req.query 来获取。

req.query
在 Express 中获取表单 POST 请求体数据

在 Express 中没有内置获取表单 POST 请求体的 API,我们需要使用一个第三方包:body-parser

安装:

npm install --save body-parser

配置:

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

var app = express()

// 配置 body-parser
// 只要加入这个配置,则在 req 请求对象上会多出来一个属性:body
// 通过 req.body 来获取表单 POST 请求体数据
// 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')
  // 可以通过 req.body 来获取表单 POST 请求体数据
  res.end(JSON.stringify(req.body, null, 2))
})
在 Express 配置使用 express-session 插件

参考文档:https://www.npmjs.com/package/express-session

安装:

npm install express-session

配置:

// 在 Express 这个框架中,默认不支持 Session 和 Cookie
// 但是我们可以使用第三方中间件:express-session 来解决
// 1. npm install express-session
// 2. 配置(一定要在路由挂载之前)
// 3. 使用
//     当把这个插件配置好之后,我们就可以通过 req.session 来访问和设置 session 成员
//     添加 session 数据   req.session.foo = 'bar'
//     访问 session 数据   req.session.foo
app.use(session({
     
  // 配置加密字符串 --- 会在原有加密基础上和这个字符串拼接起来加密
  // 目的是为了增强安全性,防止客户端恶意伪造
  secret: 'keyboard cat',  
  resave: false,
  saveUninitialized: true   // 无论你是否使用 session,我都直接给你分配一把钥匙
}))

使用:

// 添加 session 数据
req.session.foo = 'bar'

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

提示:默认 session 数据是内存存储的,服务器一旦重启就会丢失,真正的生产环境会把 session 进行持久化存储。

CRUD案例
起步
  • 初始化
  • 安装依赖
  • 模板处理
路由设计
请求方法 请求路径 get 参数 post 参数 备注
GET /students 渲染首页
GET /students/new 渲染添加学生页面
POST /students/new name、age、gender、hobbies 处理添加学生请求
GET /students/edit id 渲染编辑页面
POST /students/edit id、name、age、gender、hobbies 处理编辑请求
GET /students/delete id 处理删除请求
提取路由模块

router.js:

/**
 * router.js 路由模块
 * 职责:
 *    处理路由
 *    根据不同的请求方法+请求路径设置具体的请求处理函数
 */

var fs= require('fs')

// Express 提供了一种更好的方式
// 专门用来包装路由的
var express = require('express')

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

// 2. 把路由都挂载到 Router 容器中
router.get('/students', function (req, res) {
     
	// readFile 的第二个参数是可选的,传入 utf8 就是告诉他把读取到的文件直接按照 utf8 编码转成我们能认识的字符
	// 除了这样来转换之外,也可以通过 data.toString() 的方式
	fs.readFile('./db.json', 'utf8', function (err, data) {
     
		if (err) {
     
			return res.status(500).send('Server error')
		}

		// 从文件中读取到的数据一定是字符串
		// 所以这里一定要手动转换成对象
		var students = JSON.parse(data).students
		res.render('index.html', {
     
			fruits: [
	          '苹果',
	          '香蕉',
	          '火龙果'
			],
			students: students
		})
	})
})

router.get('/students/new', function (req, res) {
     
	res.render('new.html')
})

router.post('/students/new', function (req, res) {
     
	// 1. 获取表单数据
	// 2. 处理
	//    将数据保存到 db.json,持久化
	// 3. 发送响应
	// 先读取出来,转成对象
	// 然后往对象中 push 数据
	// 然后把对象转为字符串
	// 然后把字符串再次写入文件
	console.log(req.body)
})

router.get('/students/edit', function (req, res) {
     
	
})


router.post('/studens/edit', function (req, res) {
     
	
})

router.get('/students/delete', function (req, res) {
     
	
})

// 3. 把 router 导出
module.exports = router

app.js:

var router = require('./router')

// 挂载路由
app.use(router)
设计操作数据的 API 文件模块

student.js:

/**
 * student.js
 * 数据操作文件模块
 * 职责:操作文件中的数据,只处理数据,不关心业务
 */


/**
 * 获取所有学生列表
 * return []
 */
exports.find = function () {
     
	
}

/**
 * 添加保存学生
 */
exports.save = function () {
     
	
}

/**
 * 更新学生
 */
exports.update = function () {
     
	
}

/**
 * 删除学生
 */
exports.delete = function () {
     
	
}

MongoDB


关系型数据库和非关系型数据库

表与表之间存在关系

  • 所有的关系型数据库都需要通过 sql 语言来操作
  • 所有关系型数据库在操作之前都需要设计表结构
  • 数据表还支持约束
    • 唯一的
    • 主键
    • 默认值
    • 非空
  • 非关系型数据库非常灵活
  • 有的非关系型数据库就是 key-value 键值对
  • MongoDB 是长得最像关系型数据库的非关系型数据库
    • 数据库 -> 数据库
    • 数据表 -> 集合(集合)
    • 表记录 -> 文档对象
  • MongoDB 不需要设计表结构
  • 可以任意的往里面存放数据,没有结构
安装
  • 64位下载地址:https://www.mongodb.com/download-center/community

  • 下载

  • 安装

  • 配置环境变量

  • 输入 mongod --version 测试是否安装成功

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p4zXKNiW-1575955038354)(C:\Users\89349\AppData\Roaming\Typora\typora-user-images\1575610504849.png)]

启动和关闭数据库

启动:

# mongodb 默认使用执行 mongod 命令所处盘符根目录下的 /data/db 作为自己的数据存储目录
# 所以在第一次执行该命令之前先自己手动新建一个 /data/db
mongod

如果想要修改默认的数据存储目录,可以:

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

停止:

在开启服务的控制台,直接 ctrl + c 即可停止
或者直接关闭开启服务的控制台
连接数据库

连接:

# 该命令默认连接本机的 mongodb 服务
mongo

退出:

# 在连接状态输入
exit
基本命令
  • show dbs

    • 查看显示所有数据库
  • db

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

    • 切换到指定的数据库(如果没有,新建一个数据库)
  • 插入数据

在 node 中如何操作 MongoDB 数据
使用官方的 mongodb 包使用

https://www.npmjs.com/package/mongodb

使用 mongoose 来操作 MongoDB 数据库

第三方包:mongoose 基于 MongoDB 官方的 mongodb 包做了封装

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

const Cat = mongoose.model('Cat', {
      name: String });

const kitty = new Cat({
      name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));
mongoose 操作数据库流程 案例
var mongoose = require('mongoose')

var Schema = mongoose.Schema

// 1. 连接数据库
mongoose.connect('mongodb://localhost/itcast');

// 2. 设计文档结构(表结构)
// 字段名称就是表结构中的属性名称
// 值
// 约束的目的是为了保证数据的完整性,不要有脏数据
var userSchema = new Schema({
     
    username: {
     
    	type: String,
    	required: true // 不能为空
    },
    password: {
     
    	type: String,
    	required: true
    },
    email: {
     
    	type: String
    }
});

// 3. 将文档结构发布为模型
// mongoose.model 方法就是用来将一个架构发布为 model
// 第一个参数:传入一个大写名词单数字符串用来表示你的数据库名称
//      mongoose 会自动将大写名称的字符串生成 小写复数 的集合名称
//      例如 User ---> users
// 第二个参数:构架 Schema
// 
// 返回值:模型构造函数
var User = mongoose.model('User', userSchema)

// 4. 当我们有了模型构造函数之后,就可以使用这个构造函数对 users 集合中的数据进行 增、删、改、查


/**
 * 新增数据
 * @type {User}
 */
var admin = new User({
     
	username: 'root',
	password: '789',
	email: '[email protected]'
})

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


/**
 * 查询所有数据
 * @param  {[type]} err  [description]
 * @param  {[type]} ret) {	if         (err) {		console.log('查询失败')	} else {		console.log(ret)	}} [description]
 * @return {[type]}      [description]
 */
User.find(function (err, ret) {
     
	if (err) {
     
		console.log('查询失败')
	} else {
     
		console.log(ret)
	}
})

/**
 * 按照条件查询
 * @param  {[type]} err  [description]
 * @param  {[type]} ret) {	if         (err) {		console.log('查询失败')	} else {		console.log(ret)	}} [description]
 * @return {[type]}      [description]
 */
User.find({
     
	username: 'root'
},function (err, ret) {
     
	if (err) {
     
		console.log('查询失败')
	} else {
     
		console.log(ret)
	}
})

/**
 * 查询第一个符合条件的数据,数据类型是----对象
 * @param  {[type]} err  [description]
 * @param  {[type]} ret) {	if         (err) {		console.log('查询失败')	} else {		console.log(ret)	}} [description]
 * @return {[type]}      [description]
 */
User.findOne({
     
	username: 'admin'
},function (err, ret) {
     
	if (err) {
     
		console.log('查询失败')
	} else {
     
		console.log(ret)
	}
})
// =====================================================


/**
 * 删除数据(按条件)
 * @param  {[type]} err  [description]
 * @param  {[type]} ret) {	if         (err) {		console.log('删除失败')	} else {		console.log('删除成功')		console.log(ret)	}} [description]
 * @return {[type]}      [description]
 */
User.remove({
     
	username: 'root'
},function (err, ret) {
     
	if (err) {
     
		console.log('删除失败')
	} else {
     
		console.log('删除成功')
		console.log(ret)
	}
})
// =====================================================


/**
 * 更新数据(按条件)
 * @param  {[type]} err  [description]
 * @param  {[type]} ret) {	if         (err) {		console.log('更新失败')	} else {		console.log('更新成功')		console.log(ret)	}} [description]
 * @return {[type]}      [description]
 */
User.findByIdAndUpdate('5deb06ead567bd38505d5db3', {
     
	password: 'what'
}, function (err, ret) {
     
	if (err) {
     
		console.log('更新失败')
	} else {
     
		console.log('更新成功')
		console.log(ret)
	}
})

Promise


callback hell

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-piwFgZUP-1575955038354)(C:\Users\89349\Desktop\u=2529250617,3283018274&fm=26&gp=0.jpg)]

无法保证顺序的代码

var fs = require('fs')

fs.readFile('./data/a.txt', 'utf8', function (err, data) {
     
	if (err) {
     
		// return console.log('读取失败')
		// 抛出异常
		//    1. 阻止程序的执行
		//    2. 把错误消息打印到控制台
		throw err
	}
	console.log(data)
})


fs.readFile('./data/b.txt', 'utf8', function (err, data) {
     
	if (err) {
     
		// return console.log('读取失败')
		// 抛出异常
		//    1. 阻止程序的执行
		//    2. 把错误消息打印到控制台
		throw err
	}
	console.log(data)
})


fs.readFile('./data/c.txt', 'utf8', function (err, data) {
     
	if (err) {
     
		// return console.log('读取失败')
		// 抛出异常
		//    1. 阻止程序的执行
		//    2. 把错误消息打印到控制台
		throw err
	}
	console.log(data)
})

通过回调嵌套的方式来保证代码的执行顺序

var fs = require('fs')

fs.readFile('./data/a.txt', 'utf8', function (err, data) {
     
	if (err) {
     
		// return console.log('读取失败')
		// 抛出异常
		//    1. 阻止程序的执行
		//    2. 把错误消息打印到控制台
		throw err
	}
	console.log(data)
	fs.readFile('./data/b.txt', 'utf8', function (err, data) {
     
		if (err) {
     
			// return console.log('读取失败')
			// 抛出异常
			//    1. 阻止程序的执行
			//    2. 把错误消息打印到控制台
			throw err
		}
		console.log(data)
		fs.readFile('./data/c.txt', 'utf8', function (err, data) {
     
			if (err) {
     
				// return console.log('读取失败')
				// 抛出异常
				//    1. 阻止程序的执行
				//    2. 把错误消息打印到控制台
				throw err
			}
			console.log(data)
		})
	})
})

为了解决回调地狱的嵌套,EcmaScript 6 中新增了一个 API:Promise

Promise 本身不是异步的。

Promise基本语法

var fs = require('fs')

// EcmaScript 6 中新增了一个 API  Promise
// Promise 是一个构造函数

// console.log(1)


// 创建 Promise 容器
//    Promise 一旦创建,就开始执行里面的代码
var p1 = new Promise(function (resolve, reject) {
     
	// console.log(2)
	fs.readFile('./data/a.txt', 'utf8', function (err, data) {
     
		if (err) {
     
			// 失败了,承诺容器中的任务失败了
			// console.log(err)
			// 把容器的 Pending 状态变成 Reject
			// 
			// 调用 reject 就相当于调用了 then 方法中的第二个函数
			reject(err)
		} else {
     
			// console.log(3)
			// 承诺容器中的任务成功了
			// console.log(data)
		    // 把容器的 Pending 状态变成 Resolved
		    // 这里调用的 resolve 方法实际上就是 then 方法传递的那个 function
			resolve(data)
		}
	})
})


var p2 = new Promise(function (resolve, reject) {
     
	fs.readFile('./data/b.txt', 'utf8', function (err, data) {
     
		if (err) {
     
			reject(err)
		} else {
     
			resolve(data)
		}
	})
})


var p3 = new Promise(function (resolve, reject) {
     
	fs.readFile('./data/c.txt', 'utf8', function (err, data) {
     
		if (err) {
     
			reject(err)
		} else {
     
			resolve(data)
		}
	})
})

// p1 就是那个承诺
// 当 p1 成功了,然后(then)做指定的操作
// then 方法接收的 function 就是容器中的 resolve
p1
    .then(function(data) {
     
        console.log(data)
        // 当 p1 读取成功的时候
        // 当前函数中的 return 的结果就可以在后面的 then 中 function 接收到
        // 当你 return 123 后面接收到的就是 123
        // 没有return 接收到的就是 undefined
        // 
        // 真正要 return 的是一个 Promise 对象
        // 当 return 一个 Promise 对象的时候,后续的 then 中的方法的第一个参数会作为 p2 的 resolve
        return p2
    }, function(err) {
     
        console.log('读取文件失败了', err)
    })
    .then(function(data) {
     
    	console.log(data)
    	return p3
    }, function(err) {
     
    	console.log('读取文件失败', err)
    })
    .then(function(data) {
     
    	console.log(data)
    }, function(err) {
     
    	console.log('读取文件失败', err)
    })

封装 Promise 版本的 readFile

var fs = require('fs')


function pReadFile(filePath) {
     
	return new Promise(function (resolve, reject) {
     
		fs.readFile(filePath, 'utf8', function (err, data) {
     
			if (err) {
     
				reject(err)
			} else {
     
				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)
	})

其他


基于原生 XMLHttpRequest 封装 get 方法

function get(url, callback) {
     
			var oReq = new XMLHttpRequest();

			oReq.onload = function(e) {
     
			  var arraybuffer = oReq.response; // not responseText
			  /* ... */
			  callback(oReq.response)
			}
			oReq.open("GET", url);
			oReq.send();
		}
		get('data.json', function(data) {
     
			console.log(data)
		})

修改完代码自动重启

可以使用第三方工具: nodemon 帮助我们解决频繁修改代码重启服务器的问题。

nodemon 是一个基于 node.js 开发的一个第三方命令行工具,我们使用的时候需要独立安装:

# 在任意目录执行该命令都可以
npm install --global nodemon

安装完毕之后,使用:

node app.js

# 使用 nodemon
nodemon app.js

只要是通过 nodemon app.js 启动的服务,则它会监视你的文件变化,当文件发生变化时,自动帮你重启服务器。

文件操作路径和模块操作路径

文件操作路径:

// 在文件操作的相对路径中
//     ./data/a.txt 相对于当前目录
//     data/a.txt   相对于当前目录
//     /data/a.txt  绝对路径,当前文件模块所处磁盘根目录
//     h:/xx/xx..   绝对路径

fs.readFile('/data/a.txt', function (err, data) {
     
	if (err) {
     
		return console.log(err);
	}
	console.log(data.toString());
})

模块操作路径:

// 这里的 / 也是磁盘根路径
require('/data/foo.js')

// 相对路径
require('./data/foo.js')

// 模块加载的路径中的相对路径 ./ 不能省略

t(err)
} else {
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)
})




### 其他

***

#### 基于原生 XMLHttpRequest 封装 get 方法

```javascript
function get(url, callback) {
			var oReq = new XMLHttpRequest();

			oReq.onload = function(e) {
			  var arraybuffer = oReq.response; // not responseText
			  /* ... */
			  callback(oReq.response)
			}
			oReq.open("GET", url);
			oReq.send();
		}
		get('data.json', function(data) {
			console.log(data)
		})

修改完代码自动重启

可以使用第三方工具: nodemon 帮助我们解决频繁修改代码重启服务器的问题。

nodemon 是一个基于 node.js 开发的一个第三方命令行工具,我们使用的时候需要独立安装:

# 在任意目录执行该命令都可以
npm install --global nodemon

安装完毕之后,使用:

node app.js

# 使用 nodemon
nodemon app.js

只要是通过 nodemon app.js 启动的服务,则它会监视你的文件变化,当文件发生变化时,自动帮你重启服务器。

文件操作路径和模块操作路径

文件操作路径:

// 在文件操作的相对路径中
//     ./data/a.txt 相对于当前目录
//     data/a.txt   相对于当前目录
//     /data/a.txt  绝对路径,当前文件模块所处磁盘根目录
//     h:/xx/xx..   绝对路径

fs.readFile('/data/a.txt', function (err, data) {
     
	if (err) {
     
		return console.log(err);
	}
	console.log(data.toString());
})

模块操作路径:

// 这里的 / 也是磁盘根路径
require('/data/foo.js')

// 相对路径
require('./data/foo.js')

// 模块加载的路径中的相对路径 ./ 不能省略

你可能感兴趣的:(nodejs,nodejs学习笔记)