简介

Node.js是一个基于 Chrome V8引擎的 JavaScript 运行环境
浏览器是 JavaScript 的前端运行环境
Node.js是 JavaScript 的后端运行环境
Node.js 中无法调用 DOM和BOM等浏览器内置API

fs 文件系统模块

fs是Node.js官方提供的,用来操作文件的模块
fs.readFile() 方法用来读取指定文件中的内容
fs.writeFile() 方法用来向指定文件中写入内容
在JavaScript代码中使用需要先导入

const fs = require('fs')
// 格式如下
fs.readFile(path[, option], callback)
// 示例
fs.readFile('./files/test.txt', 'utf-8', function(err, dataStr) {
    console.log(err) // err 失败的
    console.log(dataStr)  // 成功的回调
})
//  __dirname 表示当前文件所处的目录, 可以用来拼接路径   __dirname + 'files/1.txt'
// 通常情况下,使用 __dirname + 地址 拼接的形式会存在一些问题,不够规范,可以使用
// path.join('/a', '/b') 这种方式拼接路径
fs.writeFile('./test.txt', '修改的文件内容', 'utf-8', function(err) {
    console.log(err)   // err为 null 表示修改成功
})

注:fs.writeFile() 方法只能用来创建文件,不能用来创建路径
      fs.writeFile() 方法重复调用写入同一个文件,新内容总会覆盖旧内容

path 路径模块

const path = require('path')
path.join()  // 可以用来拼接路径
path.join(__dirname, './files/test.txt')

// 获取文件名称
path.basename()
// 调用 path.basename() ,需要传递一个路径参数,即需要获取文件名的文件路径,会返回一个文件名称,如果文件名带有拓展后缀,可在调用时传递第二个参数,即后缀名去除
var name = path.basename('test.txt', .txt)

// 获取文件扩展名
path.extname()
path.extname() 方法,只需要向方法中传入一个文件的路径地址,就会返回当前文件的拓展名字符串

http 模块

// 创建 web 服务器的基本步骤
// 1. 导入 http 模块
const http = require('http')
// 2. 创建 web 服务器实例
const server = http.createServer()
// 3. 为服务器实例绑定 request 事件,监听客户端的请求
server.on('request', (req, res) => {
    console.log("使用服务器实例的 .on() 方法可以为服务器绑定一个 request  事件,只要有客户端请求当前服务器,就会触发 request 事件,从而调用这个事件处理函数')
    res.setHeader('Content-Type', 'text/html; charset=utf-8')    // 有时出现中文乱码问题,就需要设置响应头
     res.end()   // 此方法可以向客户端发送内容
})
// 启动服务器
server.listen(80, () => {
    console.log('  调用 server.listen(端口号, 回调函数) 方法,即可启动 web 服务器  ')
})

模块化

模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程,对于整个系统来说,模块是可组合,分解和更换的单位

当使用 require() 方法加载其他模块时,会执行被加载模块中的代码

module.exports 对象

在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用。
外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象
最终向外共享的结果,永远都是 module.export 所指向的对象

exports 和 module.exports
require() 模块引入一个模块,得到的永远是 module.exports 指向的对象

CommonJS 规范

  1. 每个模块内部,module变量都代表当前模块
  2. module 变量是一个对象,它的 exports 属性(module.exports)是对外的接口
  3. 加载某个模块,其实是加载该模块的 module.exports 属性,require() 方法用于加载模块

常用的 npm 包工具
格式化时间的 资源包 moment

// 项目目录下安装 资源包
npm i moment
// 引入使用
const moment = require('moment)
// 调用 moment 提供的方法 给出所需时间格式  一个 M 代表月份小于10前方不补0,MM表示补零
const dt = moment().format('YYYY-MM-DD HH:mm:ss')

package.json

dependencies 和 devDependencies 区别
如果某些包只在项目开发阶段会用到,在项目上线后不会用到,建议把这些包记录到 devDependencies 节点中。与之对应的,如果某些包在开发和项目上线阶段都需要用到,则建议把这些包记录到 dependencies 节点中

// 安装到 dependencies 节点时,直接 npm i 包名即可
npm i moment

// 安装到 devDependencies 节点时,则需要在后面加上 -D
npm i moment -D       
// 上面为简写形式,完整为
npm i moment --save-dev

镜像源问题

//查看当前的下包源镜像
npm config get registry
// 将下包的镜像源切换为淘宝镜像源
npm config set registry=https://registry.npm.taobao.org/
//检查镜像源是否下载成功
npm config get registry

// 也可以 通过 nrm 提供的终端命令快速查看切换下包源镜像
// 通过 npm 将 nrm 安装为全局可用工具
npm i nrm -g
// 查看所有可用的 镜像源
nrm ls
// 将下包镜像源切换为 taobao 镜像
nrm use taobao
use后面可以接上 其他服务器名称,有 npm, yarn, cnpm, taobao, nj等等可供切换

开发属于自己的包

npm官网 https://www.npmjs.com

  1. 新建一个文件夹,作为自己开发的 包 的根目录
  2. 在文件夹中,新建三个文件: package.json(包管理配置文件) index.js(包入口文件) README.md(包说明文档)
// package.json 中基本配置
{
  "name": "qinyu-tools",  // 包名
  "version": "1.0.0",     // 版本号
  "main": "index.js",     //指向入口文件
  "description": "提供格式化时间,HTMLEscape功能",  // 介绍
  "keywords": ["qinyu","dateFormat", "escape"], // 搜索关键字
  "license": "ISC"  // 遵守协议
}

将自己的npm包上传

注册登录 npm 账号

命令行中输入 npm login , 按照提示输入 userName password 和邮箱地址,输入完邮箱地址如报错 403

npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy, or
npm ERR! 403 on a server you do not have access to.
// 此时,是因为npm使用taobao镜像导致,需要查看镜像,然后如果为镜像错误只需要执行
npm config set registry https://registry.npmjs.org/
// 将镜像切回即可正确完成登录操作

// 切换后继续输入账号信息后报错
npm ERR! 400 Bad Request - PUT https://registry.npmjs.org/-/user/org.couchdb.user:ichthyornis - The provided password is too short. Please pick a password longer than 10 characters.
// 此时是因为密码长度 需要10位数及以上才可以,重新设置即可

把包发布到 npm 上

将终端切换到包的根目录之后,运行 npm publish 命令,即可将包发布到 npm 上(包名不可重复需要是唯一)
发布完成之后可以登录 node.js 官网查看

删除自己已发布的包

运行 npm unpublish 包名 --force 命令,即可从 npm 删除自己已发布的包
局限性:

  1. npm unpublish 命令只能删除 72 小时以内发布的包
  2. npm unpublish 删除的包, 在 24 小时内不允许重复发布
  3. 发布包的时候慎重,尽量不要向 npm 上发布没有意义的包

模块的加载机制

模块在第一次加载后会被缓存,意味着多次调用 require() 不会导致模块的代码被执行多次
不论是内置模块、自定义模块、还是第三方模块,他们都会优先从缓存中加载,从而提高模块的加载效率
nodejs提供的内置模块加载的优先级最高

express

Express是基于Node.js平台,快速、开放、极简的Web开发框架
Node.js提供的原生http模块也可以用来创建Web服务器,但是用起来复杂,开发效率低,Express 是基于内置http模块进一步封装出来的,能够极大的提高开发效率。
express可以用来做什么?
对前端而言,常见的两种服务器分别为: Web网站服务器,用来专门提供Web网页资源的服务器; 和API接口服务器,专门对外提供API接口的服务器。

创建最基本的 Web服务器步骤

// 导入 express
const express = require(‘express’)
// 创建web服务器
const app = express() 
// 监听客户端的 get 和 post 请求,并向客户端响应具体的内容
app.get('/user', (req, res) => {
// 调用 express 提供的res.send()方法,向客户端响应一个 JSON 对象
    res.send({name: 'zs', age: 20, gender: '男'})
})

app.post('/user', (req, res) => {
    // 调用 express 提供的 res.send()方法,向客户端响应一个 文本字符串
    res.send('请求成功!')
})

// 获取客户端调用接口时 url上拼接传递的参数
app.get('/', (req, res) => {
    // 通过 req.query 可以获取到客户端发送过来的 查询参数, 默认情况下, req.query 是一个空对象
    console.log(req.query)
})

// 客户端调用此接口时,获取 :id 这个动态参数, 后面可以拼接更多参数
<!-- app.get('/user/:id/:name', (req, res) => { -->
app.get('/user/:id', (req, res) => {
    // req.params 是动态匹配的 url 参数,默认是一个空对象的形式
    console.log(req.params)
    res.send(req.params)
})

// 通过 app.use 将 express.static() 方法静态资源的访问路径指定为 './static'
app.use(express.static('./static'))
// 需要将多个静态资源通过服务器暴露出去时,可以多次重复调用 express.static()方法
app.use(express.static('./file'))
// 可以在需要暴露出去的静态资源前,挂载路径前缀的形式区分多个静态资源,此时要想访问到 utils里的静态资源,就需要访问    http://127.0.0.1:180/src  才可以正常访问
app.use('/src', express.static('utils'))


// 调用 app.listen(端口号,回调函数),启动服务器1.
app.listen(180, () => {
	console.log('express server running at http://127.0.0.1')
})

Express 托管静态资源 express.static()

通过 express.static() 方法,可以创建一个静态资源服务器,用于将静态资源 对外开放访问
express 在指定的静态目录中查找文件,并对外提供资源的访问路径,因此,存放静态资源的目录不会出现在访问时的 url 中,
例如设置静态资源 app.use(express.static('public')) ,指定的静态资源存放路径为 public ,但是在访问时,路径直接写到 public 下级的文件即可, public 是不会出现在访问路径中的

nodemon

node.js启动项目时通常使用 node 文件名 的形式,这样做代码每次修改后都需要重新输入启动命令
将 node 文件名 命令替换为 nodemon 文件名的形式启动项目,可以监听文件的修改,当文件修改时自动重启项目

yarn add nodemon -g
nodemon test.js

Express 路由 也就是映射关系

//简单路由实例 一个简单的路由包括三个部分,请求的方式,请求的地址,已经处理的回调函数
const express = require('express')
const app = express()
app.get('/', function(req, res) {
    res.send('发送的数据')
})
app.listen(111, () => {
    console.log('server is running')
})
// 路由的匹配过程: 每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则Express会将这次请求,转交给对应的 function 函数进行处理

// 实际开发中不建议使用 app 直接挂载路由,应当遵循模块化的原则
// 导出
const express = require('express')
const router = express.Router()
router.get('/user', (req, res) => {
    res.send('request success')
})
module.exports = router

// 导入
const router = require('./express.js')
// 在使用 use 注册路由模块时,router实例前加一个参数 '/api' 表示统一设置在所有的路由路径前添加一个前缀
app.use('api', router)

Express 中间件 (相当于拦截器)

当一个请求到达 Express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行 预处理
Express 的中间件,本质上就是一个 function处理函数
注意: 区别于路由处理函数,中间件函数的形参列表中,必须包含 next 参数(且必须是最后一个参数),而路由处理函数中只包含req 和 res
next 函数是实现多个中间件连续调用的关键,他表示把流转关系转交给下一个 中间件 或 路由
定义了全局中间件后,当服务器中的 请求被调用时,Express中间件就会对这次的请求进行预处理和响应,做出某些操作,类似于响应拦截器

const express = require('express')
const app = express()
// 语法示例
// 此时,常量 m 所指向的,就是一个中间件函数
const m = function(req, res, next) {
   // do something
   // 在当前中间件的业务处理完成之后,必须调用 next() 函数,表示把流转关系转交给下一个中间件或路由
   next()
}
app.listen(112, () => {
    console.log('The server is running')
})

//  如果需要定义全局生效的中间件,只需要使用 express 的实例调用一下 use() 方法
app.use(m)
// 客户端发起的任何请求,达到服务器之后,都会触发中间件,叫做全局生效的中间件

// 全局中间件的简写形式
app.use((req,res,next) => {
    // do something
    next()
})
// 多个中间件之间,共享同一份 req 和 res, 可以在上游的中间件中,统一为 req 和 res 对象添加自定义的属性和方法,供下游的中间件或者路由进行使用。
// 可以使用 app.use() 连续定义多个全局中间件,客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用

// 局部生效的中间件,全局中间件需要 app 调用 use 方法传入中间件函数,而局部中间件则不需要,只需要在需要调用的 路由函数中,将中间件函数作为参数传入即可
const mm = (req, res, next) => {
    console.log('此局部生效的中间件将被调用')
    next()
}

app.get('/', mm, (req, res) => {
    res.send('局部生效需传入指定中间件,不传就不会调用中间件')
})
// 如果需要在一个 路由函数中 调用多个局部中间件,可以直接以逗号分隔,或者将多个中间件写成数组的形式

tip:  一定要在 路由 之前注册中间件
      客户端发送过来的请求,可以连续调用多个中间件进行处理
      执行完中间件代码后,一定要调用一下 next() 函数,next() 函数之后最好就不要做操作了
      连续调用多个中间件时,多个中间件之间可以共享 req 和 res 对象