Node.js

Node.js 是什么

  • Node.js 不是一门语言,不是库、不是框架,Node.js 是一个 JavaScript 运行时环境,可以解析和执行 JavaScript 代码,以前只有浏览器可以可以解析执行 JavaScript 代码,现在的 JavaScript 可以完全脱离浏览器来运行,一切都归功于:Node.js
  • 浏览器中的 JavaScript有EcmaScript,BOM, DOM,Node.js 中的 JavaScript没有 BOM、DOM,
  • 在 Node 这个 JavaScript 执行环境中为 JavaScript 提供了一些服务器级别的操作 API
    • 例如文件读写
    • 网络服务的构建
    • 网络通信
    • http 服务器
    • 等处理。。。
  • 构建与 Chrome 的 V8 引擎之上,Google Chrome 的 V8 引擎是目前公认的解析执行 JavaScript 代码最快的,Node.js 的作者把 Google Chrome 中的 V8 引擎移植了出来,开发了一个独立的 JavaScript 运行时环境。
  • 特点
    • event-driven 事件驱动
    • non-blocking I/O model 非阻塞IO模型(异步)
    • lightweight and efficient. 轻量和高效

Node.js 能做什么

  • Web 服务器后台
  • 命令行工具
    • npm(node)
    • git(c 语言)
    • hexo(node)
  • 对于前端开发工程师来讲,接触 node 最多的是它的命令行工具
    • webpack
    • gulp
    • npm

本文知识简介

  • B/S 编程模型
    • Browser - Server
    • back-end
    • 任何服务端技术这种 BS 编程模型都是一样,和语言无关
  • 模块化编程
    • RequireJS
    • SeaJS
    • @import('文件路径'),在 Node 中可以像 @import() 一样来引用加载 JavaScript 脚本文件
  • Node常用API
  • 异步编程
    • 回调函数
    • Promise
    • async
    • generator
  • Express Web 开发框架

起步

解析执行 JavaScript
  1. 创建编写 JavaScript 脚本文件
  2. 打开终端,定位到脚本文件所属目录
  3. 输入 node 文件名 执行对应的文件

注意:文件名不要使用 node.js 来命名,也就是说除了 node 这个名字你随便起,而且最好也不要使用中文。

文件读写

浏览器中的 JavaScript 是没有文件操作的能力的,但是 Node 中的 JavaScript 具有文件操作的能力

fs 是 file-system 的简写,就是文件系统的意思,在 Node 中如果想要进行文件操作,就必须引入 fs 这个核心模块,在 fs 这个核心模块中,就提供了所有的文件操作相关的 API

javascript 复制代码
// 1. 使用 require 方法加载 fs 核心模块
var fs = require('fs')

// 2. 读取文件
//    第一个参数就是要读取的文件路径
//    第二个参数是一个回调函数  
//        读取成功
//          data 等于数据
//          error 等于null
//        读取失败
//          data 等于undefined没有数据
//          error 等于错误对象
fs.readFile('./data/a.txt', function (error, data) {
  // 文件中存储的其实都是二进制数据 0 1,
  // 可以通过 toString 方法把其转为我们能认识的字符
  // console.log(data)
  // console.log(error)
  // 在这里就可以通过判断 error 来确认是否有错误发生
  if (error) {
    console.log('读取文件失败了')
  } else {
    console.log(data.toString())
  }
})

文件写入:

javascript 复制代码
var fs = require('fs')
// 第一个参数:文件路径
// 第二个参数:文件内容
// 第三个参数:回调函数
//    error:
//      文件写入成功,error 是 null
//      文件写入失败,error 就是错误对象
fs.writeFile('./data/你好.md', '大家好,给大家介绍一下,我是Node.js', function (error) {
  // console.log(error)
  if (error) {
    console.log('写入失败')
  } else {
    console.log('写入成功了')
  }
})
http

在 Node 中专门提供了一个核心模块:http,这个模块的职责就是帮你创建编写服务器的

javascript 复制代码
// 1. 加载 http 核心模块
var http = require('http')

// 2. 使用 http.createServer() 方法创建一个 Web 服务器
//    返回一个 Server 实例
var server = http.createServer()

// 3. 服务器要干嘛?提供服务:数据的服务;发请求;接收请求;处理请求;发送响应
//    注册 request 请求事件
//    当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数:回调处理函数
server.on('request', function () {
  console.log('收到客户端的请求了')
})

// 4. 绑定端口号,启动服务器
server.listen(3000, function () {
  console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问')
})

Node 中的 JavaScript

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

EcmaScript

核心模块

Node 为 JavaScript 提供了很多服务器级别的 API ,这些 API 绝大多数都被包装到了一个具名的核心模块中了。例如文件操作的 fs 核心模块,http 服务构建的 http 模块,path 路径操作模块、os 操作系统信息模块。如果想要使用它,就必须先使用 require 方法加载才能使用:

javascript 复制代码
var fs = require('fs')
var http = require('http')

ip 地址和端口号

  • ip 地址用来定位计算机
  • 端口号用来定位具体的应用程序,一切需要联网通信的软件都会占用一个端口号,端口号的范围从 0 - 65536 之间。在计算机中有一些默认端口号,最好不要去使用,例如 http 服务的 80
  • 我们在开发过程中使用一些简单好记的端口号就可以了,例如 3000、5000 等没什么含义
  • 可以同时开启多个服务,但一定要确保不同服务占用的端口号不一致才可以,即在一台计算机中,同一个端口号同一时间只能被一个程序占用

在 Node 中使用模板引擎

安装:

shell 复制代码
$ npm install art-template

使用:

javascript 复制代码
var template = require('art-template')

var ret = template.render('hello {{ name }}', {
  name: 'Jack'
})

console.log(ret) // => hello Jack

CommonJS 模块规范

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

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

语法:

javascript 复制代码
var 自定义变量名称 = require('模块')

两个作用:

  • 执行被加载模块中的代码
  • 得到被加载模块中的 exports 导出接口对象
导出 exports
  • Node 中是模块作用域,默认文件中所有的成员只在当前文件模块有效
  • 对于希望可以被其它模块访问的成员,我们就需要把这些公开的成员都挂载到 exports 接口对象中就可以了

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

javascript 复制代码
exports.a = 123
exports.b = 'hello'
exports.c = function () {
  console.log('ccc')
}
exports.d = {
  foo: 'bar'
}

导出多个成员也可以这么来写:

javascript 复制代码
module.exports = {
  foo: 'bar',
  add: function () {
    
  }
}

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

javascript 复制代码
module.exports = 'hello'

以下情况会覆盖:

javascript 复制代码
module.exports = 'hello'

// 以这个为准,后者会覆盖前者
module.exports = function (x, y) {
  return x + y
}

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

javascript 复制代码
module.exports = {
  add: function () {
    return x + y
  },
  str: 'hello'
}
原理解析

exports 和 module.exports 的一个引用:

javascript 复制代码
// var module = {
//   exports = {}
// }
// var exports = module.exports

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

exports.foo = 'bar'

// 等价于
moudle.exports.foo = 'bar'

// return module.exports
exports 和 module.exports 的区别
  • exports 和 module.exports 的区别
    • 每个模块中都有一个 module 对象,module 对象中有一个 exports 对象, 我们可以把需要导出的成员都挂载到 module.exports 接口对象中 也就是:moudle.exports.xxx = xxx 的方式
    • 但是每次都 moudle.exports.xxx = xxx 很麻烦,所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:exports
      exports === module.exports 结果为 true
      所以对于: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 这个用来重新建立引用关系的
require 方法加载规则
  • 优先从缓存加载
  • 判断模块标识
    • 核心模块
    • 第三方模块
    • 自己写的模块
  • 路径形式的模块:
    ./ 当前目录,不可省略
    .../ 上一级目录,不可省略
    /xxx 几乎不用 (首位的 / 在这里表示的是当前文件模块所属磁盘根路径)
    d:/a/foo.js 几乎不用
    .js 后缀名可以省略

     require('./foo.js')
    
  • 核心模块的本质也是文件,核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了

    require('fs')
    require('http')
    
  • 第三方模块
    凡是第三方模块都必须通过 npm 来下载
    使用的时候就可以通过 require('包名') 的方式来进行加载才可以使用
    不可能有任何一个第三方包和核心模块的名字是一样的
    加载art-template示例:找到node_modules/art-template/package.json 文件中的 main 属性,main 属性中就记录了 art-template 的入口模块,然后加载使用这个第三方包
    如果 package.json 文件不存在或者 main 指定的入口模块是也没有,则 node 会自动找该目录下的 index.js,也就是说 index.js 会作为一个默认备选项
    如果以上所有任何一个条件都不成立,则会进入上一级目录中的node_modules 目录查找,如果上一级还没有,则继续往上上一级查找, 如果直到当前磁盘根目录还找不到,最后报错:can not find module xxx

    var template = require('art-template')
    
npm 命令行工具

npm 也有版本这个概念。

shell 复制代码
npm --version

升级 npm(自己升级自己):

shell 复制代码
npm install --global npm
常用命令
  • npm init
    • npm init -y 可以跳过向导,快速生成
  • npm install
    • 一次性把 dependencies 选项中的依赖项全部安装
    • npm i
  • 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:

shell 复制代码
# 在任意目录执行都可以
# --global 表示安装到全局,而非当前目录
# --global 不能省略,否则不管用
npm install --global cnpm

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

举个例子:

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

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

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

shell 复制代码
npm install jquery --registry=https://registry.npm.taobao.org

但是每一次手动这样加参数很麻烦,所我们可以把这个选项加入配置文件中:

shell 复制代码
# 配置到淘宝服务器
npm config set registry https://registry.npm.taobao.org

# 查看 npm 配置信息
npm config list

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

package.json

建议每一个项目都要有一个 package.json 文件(包描述文件,就像产品的说明书一样),这个文件可以通过 npm init 的方式来自动初始化出来。

package.json中的 dependencies 选项,可以用来帮我们保存第三方包的依赖信息。这样如果你的 node_modules 删除了也不用担心,只需要:npm install 就会自动把 package.json 中的 dependencies 中所有的依赖项都下载回来。

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

npm 5 以前是不会有 package-lock.json 这个文件的,npm 5 以后才加入了这个文件,当你安装包的时候,npm 都会生成或者更新 package-lock.json 这个文件。

  • npm 5 以后的版本安装包不需要加 --save 参数,它会自动保存依赖信息
  • 当你安装包的时候,会自动创建或者是更新 package-lock.json 这个文件
  • package-lock.json 这个文件会保存 node_modules 中所有包的信息(版本、下载地址),这样的话重新 npm install 的时候速度就可以提升
  • 从文件来看,有一个 lock 称之为锁,这个 lock 是用来锁定版本的。如果项目依赖了 1.1.1 版本,没有package-lock时重新 install 其实会下载最新版本,而不是 1.1.1,有package-lock可以锁住 1.1.1 这个版本,防止自动升级新版

Express

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

安装
shell 复制代码
npm install --save express
hello world
javascript 复制代码
const express = require('express')
const app = express()

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(3000, () => console.log('Example app listening on port 3000!'))
基本路由
  • 请求方法
  • 请求路径
  • 请求处理函数

get:

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

post:

javascript 复制代码
// 当你以 POST 方法请求 / 的时候,指定对应的处理函数
app.post('/', function (req, res) {
  res.send('Got a POST request')
})
静态服务
javascript 复制代码
// /public资源
app.use(express.static('public'))

// /files资源
app.use(express.static('files'))

// /public/xxx
app.use('/public', express.static('public'))

// /static/xxx
app.use('/static', express.static('public'))

app.use('/static', express.static(path.join(__dirname, 'public')))

配置 art-template 模板引擎

安装:

shell 复制代码
npm install --save art-template
npm install --save express-art-template

配置:

javascript 复制代码
// 第一个参数用来配置视图的后缀名,这里是 art ,则你存储在 views 目录中的模板文件必须是 xxx.art
// app.engine('art', require('express-art-template'))

// 这里我把 art 改为 html
app.engine('html', require('express-art-template'))

使用:

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

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

javascript 复制代码
// 注意:第一个参数 views 千万不要写错
app.set('views', 目录路径)

在 Express 中获取表单 GET 请求参数

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

javascript 复制代码
req.query

在 Express 获取表单 POST 请求体数据

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

安装:

shell 复制代码
npm install --save body-parser

配置:

javascript 复制代码
var express = require('express')
// 0. 引包
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())

使用:

javascript 复制代码
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))
})
路由设计
请求方法 请求路径 get 参数 post 参数 备注
GET /studens 渲染首页
GET /students/new 渲染添加学生页面
POST /studens/new name、age、gender、hobbies 处理添加学生请求
GET /students/edit id 渲染编辑页面
POST /studens/edit id、name、age、gender、hobbies 处理编辑请求
GET /students/delete id 处理删除请求
提取路由模块

router.js:

javascript 复制代码
/**
 * router.js 路由模块
 * 职责:
 *   处理路由
 *   根据不同的请求方法+请求路径设置具体的请求处理函数
 * 模块职责要单一,不要乱写
 * 我们划分模块的目的就是为了增强项目代码的可维护性
 * 提升开发效率
 */
var express = require('express')

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

// 2. 把路由都挂载到 router 路由容器中
router.get('/students', function (req, res) {
})

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

router.post('/students/new', function (req, res) {
})

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

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

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

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

app.js:

javascript 复制代码
var router = require('./router')

// 挂载路由
app.use(router)
设计操作数据的 API 文件模块
javascript 复制代码
/**
 * student.js
 * 数据操作文件模块
 * 职责:操作文件中的数据,只处理数据,不关心业务
 */


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

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

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

/**
 * 删除学生
 */
exports.delete = function () {
  
}
自己编写的步骤
  • 处理模板
  • 配置开放静态资源
  • 配置模板引擎
  • 简单路由:/students 渲染静态页出来
  • 路由设计
  • 提取路由模块
  • 由于接下来一些列的业务操作都需要处理文件数据,所以我们需要封装 student.js
  • 先写好 student.js 文件结构
    • 查询所有学生列表的 API find
    • findByid
    • save
    • updateById
    • deleteById
  • 实现具体功能
    • 通过路由收到请求
    • 接收请求中的数据(get、post)
      • req.query
      • req.body
    • 调用数据操作 API 处理数据
    • 根据操作结果给客户端发送响应
  • 业务功能顺序
    • 列表
    • 添加
    • 编辑
    • 删除
  • find
  • findIndex

MongoDB

参考 http://www.runoob.com/mongodb/mongodb-tutorial.html

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

表就是关系

或者说表与表之间存在关系。

  • 所有的关系型数据库都需要通过 sql 语言来操作
  • 所有的关系型数据库在操作之前都需要设计表结构
  • 而且数据表还支持约束
    • 唯一的
    • 主键
    • 默认值
    • 非空
  • 非关系型数据库非常的灵活
  • 有的非关系型数据库就是 key-value 对儿
  • 但是 MongoDB 是长的最想关系型数据库的非关系型数据库
    • 数据库 -》数据库
    • 数据表-》集合(数组)
    • 表记录-》(文档对象)
  • MongoDB 不需要设计表结构
  • 也就是说你可以任意的往里面存数据,没有结构性这么一说

安装

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EdiOHG2d-1589879868803)(media/1509940577391.png)]

启动和关闭数据库

启动:

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

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

shell 复制代码
mongod --dbpath=数据存储目录路径

停止:

在开启服务的控制台,直接 Ctrl+c 即可停止。
或者直接关闭开启服务的控制台也可以。

连接和退出数据库

连接:

shell 复制代码
# 该命令默认连接本机的 MongoDB 服务
mongo

退出:

shell 复制代码
# 在连接状态输入 exit 退出连接
exit

基本命令

  • show dbs
    • 查看显示所有数据库
  • db
    • 查看当前操作的数据库
  • use 数据库名称
    • 切换到指定的数据(如果没有会新建)
  • 插入数据

mongoose

官方的 mongodb 包来操作:https://github.com/mongodb/node-mongodb-native

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

异步编程

回调函数

不成立的情况:

javascript 复制代码
function add(x, y) {
  console.log(1)
  setTimeout(function () {
    console.log(2)
    var ret = x + y
    return ret
  }, 1000)
  console.log(3)
  // 到这里执行就结束了,不会等到前面的定时器,所以直接就返回了默认值 undefined
}

console.log(add(10, 20)) // => undefined

不成立的情况:

javascript 复制代码
function add(x, y) {
  var ret
  console.log(1)
  setTimeout(function () {
    console.log(2)
    ret = x + y
  }, 1000)
  console.log(3)
  return ret
}

console.log(add(10, 20)) // => undefined

回调函数:

如果我讲到这种程度了,说明一个问题,没有认知能力()

如何熟练达到像定义一个变量一样来封装一个带有回调函数的方法。

主要原因在于写得少,这是 JavaScript 编程的一大特色:异步编程。

甚至很多具有服务端开发经验的都不太容易熟悉这种方式。

javascript 复制代码
function add(x, y, callback) {
  // callback 就是回调函数
  // var x = 10
  // var y = 20
  // var callback = function (a) { }
  console.log(1)
  setTimeout(function () {
    var ret = x + y
    callback(ret)
  }, 1000)
}

add(10, 20, function (a) {
  // a 才是我们得到的结果
})

基于原生 XMLHTTPRequest 封装 get 方法:

javascript 复制代码
function get(url, callback) {
  var oReq = new XMLHttpRequest()
  // 当请求加载成功之后要调用指定的函数
  oReq.onload = function () {
    // 我现在需要得到这里的 oReq.responseText
    callback(oReq.responseText)
  }
  oReq.open("get", url, true)
  oReq.send()
}

get('data.json', function (data) {
  console.log(data)
})

Promise

参考文档:http://es6.ruanyifeng.com/#docs/promise

callback hell:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-622wlEhO-1589879868805)(media/1509967270775.png)]

无法保证顺序的代码:

javascript 复制代码
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)
})

通过回调嵌套的方式来保证顺序:

javascript 复制代码
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 的英文就是承诺、保证的意思(I promise you)

Promise 基本语法:

javascript 复制代码
var fs = require('fs')

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

// 创建 Promise 容器
// 1. 给别人一个承诺 I promise you.
//    Promise 容器一旦创建,就开始执行里面的代码
var p1 = new Promise(function (resolve, reject) {
  // console.log(2)
  fs.readFile('./data/aa.txt', 'utf8', function (err, data) {
    if (err) {
      // 失败了,承诺容器中的任务失败了
      // console.log(err)
      // 把容器的 Pending 状态变为 Rejected

      // 调用 reject 就相当于调用了 then 方法的第二个参数函数
      reject(err)
    } else {
      // console.log(3)
      // 承诺容器中的任务成功了
      // console.log(data)
      // 把容器的 Pending 状态改为成功 Resolved
      // 也就是说这里调用的 resolve 方法实际上就是 then 方法传递的那个 function
      resolve(data)
    }
  })
})

// console.log(4)

// p1 就是那个承若
// 当 p1 成功了 然后(then) 做指定的操作
// then 方法接收的 function 就是容器中的 resolve 函数
p1
  .then(function (data) {
    console.log(data)
  }, function (err) {
    console.log('读取文件失败了', err)
  })

封装 Promise 版本的 readFile:

javascript 复制代码
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)
  })

其它

代码风格

规范只是建议,可以遵守也可以不遵守,在一些比较专业的团队中,对于代码风格会有对应的风格校验工具,如果你的代码风格有问题,例如少了一个空格或者多了一个空格,你的代码都是不允许提交的。

后面会学习如何使用工具来强制校验代码风格的问题。

代码无分号问题

无论你是否使用的是无分号的代码风格规范,都建议当一行代码是以:

  • (
  • [
  • `

以上三者开头的的时候,最好都在其之前补上一个分号。

例如:

javascript 复制代码
;(function () {
  // code here
})()

修改完代码自动重启

我们这里可以使用一个第三方命名航工具:nodemon 来帮我们解决频繁修改代码重启服务器问题。

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

shell 复制代码
# 在任意目录执行该命令都可以
# 也就是说,所有需要 --global 来安装的包都可以在任意目录执行
npm install --global nodemon

安装完毕之后,使用:

shell 复制代码
node app.js

# 使用 nodemon
nodemon app.js

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

文件操作路径和模块路径

文件操作路径:

// 在文件操作的相对路径中
//    ./data/a.txt 相对于当前目录
//    data/a.txt   相对于当前目录
//    /data/a.txt  绝对路径,当前文件模块所处磁盘根目录
//    c:/xx/xx...  绝对路径
// fs.readFile('./data/a.txt', function (err, data) {
//   if (err) {
//     console.log(err)
//     return console.log('读取失败')
//   }
//   console.log(data.toString())
// })

模块操作路径:

// 这里如果忽略了 . 则也是磁盘根目录
require('/data/foo.js')

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

// 模块加载的路径中的相对路径不能省略 ./
相关推荐
胡桃夹夹子12 小时前
前端,npm install安装依赖卡在sill idealTree buildDeps(设置淘宝依赖)
前端·npm·node.js
LLLuckyGirl~13 小时前
node.js之---CommonJS 模块
node.js
m0_7482495413 小时前
node.js下载、安装、设置国内镜像源(永久)(Windows11)
node.js
m0_7482313117 小时前
Node.js使用教程
node.js·编辑器·vim
LLLuckyGirl~1 天前
node.js之---内置模块
node.js
飞的肖1 天前
鸿蒙DevEco Studio 5.0.1 Release下载npm镜像无法连接下载的解决方式:镜像地址变更为淘宝的npm 镜像,可解决
前端·npm·node.js
lss_033_lss1 天前
依赖冲突`npm install --no-audit --save @testing-library/jest-dom@^5.14.1...` failed
前端·npm·node.js·react
放下华子我只抽RuiKe51 天前
Vue.js 表单验证实战:一个简单的登录页面
前端·javascript·vue.js·学习·flutter·node.js·json
棋小仙1 天前
webpack打包node后端项目
后端·webpack·node.js
xiaohua0708day1 天前
webpack
前端·webpack·node.js