Skip to content

node1

约 4908 字大约 16 分钟

2025-05-02

什么是Nods.js

是一个基于Chrome V8引擎的JavaScript 运行环境

1. fs 文件系统模块

操作文件的模块

const fs = require('fs')
  • 读文件

    fs.readFile()

    fs.readFile('./1.text', 'utf-8', (err, dataStr) => {
      // console.log(err)// null 成功
      if (err) {
       return console.log('读取失败' + err.message)
        
      }
      console.log('读取成功' + dataStr)
      
      
    })
  • 写文件

    fs.writeFile()

    
    fs.writeFile('F:./.text', '123', 'utf-8', (err) => {
      if (err) {
        return console.log('写入失败')
        
      }
      
    })
  • 相对路径的的问题

解决方案:在使用 fs 模块操作文件时,直接提供完整的路径,不要提供 ./ 或 ../ 开头的相对路径,从而防止路径动态拼接的问题

  • __dirname 当前文件所处的目录

const fs = require('fs')
console.log(__dirname)

fs.readFile(__dirname + '/1.text', 'utf-8', (err, dataStr) => {
  console.log(dataStr)
  
})

2. path 路径模块

用来处理路径模块

const path = require('path')
  • path.join()

    方法,用来将多个路径片段拼接成一个完整的路径字符串

const fs = require('fs')
const path = require('path')

fs.readFile(path.join(__dirname, '../文件系统fs/1.text'), 'utf-8', (err, dataStr) => {
  if (err) {
    console.log('读取文件失败', err.message)
  }
  console.log(dataStr)
  
})
  • path.basename()

    方法,用来从路径字符串中,将文件名解析出来

  • path.extname**()** 的代码示例

3. 拆分 css js html

const fs = require('fs')

const path = require('path')

const regStyle = /<style>[\s\S]*<\/style>/
const regJs = /<script>[\s\S]*<\/script>/

fs.readFile(path.join(__dirname, './index.html'), 'utf-8', (err, dataStr) => {
  if (err) return console.log('读取失败' + err.message)
  
  // console.log(dataStr)
  resolveCss(dataStr)
  resolveJs(dataStr)
  resolveHtml(dataStr)
  
  
})


// 拆除css
const resolveCss = (htmlStr) => {

  const cssStr = regStyle.exec(htmlStr)

  let strCss = cssStr[0].replace('<style>', '').replace('</style>', '')
  
  fs.writeFile(path.join(__dirname, './index.css'), strCss, 'utf-8', err => {
    if (err) return console.log('失败' + err.message)
    console.log('成功')
    
    
  })
}

// 拆除js
const resolveJs = (htmlStr) => {

  const cssStr1 = regJs.exec(htmlStr)

  let strCss = cssStr1[0].replace('<script>', '').replace('</script>', '')
  
  fs.writeFile(path.join(__dirname, './index.js'), strCss, 'utf-8', err => {
    if (err) return console.log('失败' + err.message)
    console.log('成功')
    
    
  })
}

// 拆分html
const resolveHtml = (htmlStr) => {


  let strCss = htmlStr.replace(regStyle, '<link rel="stylesheet" href="./index.css">').replace(regJs, '<script src="./index.js"></script>')
  
  fs.writeFile(path.join(__dirname, './index2.html'), strCss, 'utf-8', err => {
    if (err) return console.log('失败' + err.message)
    console.log('成功')
    
    
  })
}

3. http模块

创建web服务器

  • 导入

    const http = require('http')
  • 创建服务器实例对象

    const server =  http.createServer()
  • 实例对象绑定request事件

    server.on('request', (req, res) => {
        log('你好')
    })
  • 启动服务器

    server.listen(80, () => {
        log('服务器启动')
    })

1. req 对象

请求对象 包含与客户端相关属性

  • req.url

    客户端请求地址

    端口号后面地址

  • req.method

    客户端请求类型



const http = require('http')

const server = http.createServer()

server.on('request', (req) => {
  console.log( '请求地址' + req.url)
  console.log( '请求方式' + req.method)
  
})

server.listen(80, () => {
  console.log('服务器启动:http://127.0.0.1')
  
})

请求地址/
请求方式GET

2. res

获取服务器相关属性

  • res.end(数据)

    向客户端发数据

const http = require('http')

const server = http.createServer()

server.on('request', (req, res) => {
  console.log( '请求地址' + req.url)
  console.log( '请求方式' + req.method)
  
  // 向客户端响应内容
  res.setHeader('Content-Type','text/html;charset=utf-8') // 中文乱码问题

  res.end(req.url + req.method + '你好')
})

server.listen(80, () => {
  console.log('服务器启动:http://127.0.0.1')
  
})

3. 根据不同的 url 响应不同的 html 内容

①获取请求的 url 地址

②设置默认的响应内容为 404 Not found

③判断用户请求的是否为 / 或 /index.html 首页

④判断用户请求的是否为 /about.html 关于页面

⑤设置 Content-Type 响应头,防止中文乱码

⑥使用 res.end() 把内容响应给客户端

4. 模块化

内置模块:(内置模块是由 Node.js 官方提供的,例如 fs、path、http 等)

自定义模块:(用户创建的每个 .js 文件,都是自定义模块)

第三方模块:(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载

1. 自定义模块

node 模块化

将大型模块 拆分为小的 模块 用时再组装

nodejs模块化规范 : commonJS模块化规范

js模块化规范: ESmodule 规范

1. commonJS模块化规范

  • 导入 暴露

    // a.js
    let num = 23
    let str = '你好'
    
    const fn = () => {
      return '我是函数'
    }
    
    module.exports = {  // 暴露
      num,
      str,
      fn
    }
    // b.js
    const { num, str, fn } = require('./a') // 导入
    
    console.log(num)
    console.log(str)
    console.log(fn())

2. ES6 新模块化语法 ESmodule 规范

  • 单个暴露

    // a,js
    let a = 0
    let b = 0
    export a
    export a
  • 单个导入

    import {a, b} from './a.js'
    import * as xxx from './a.js' //单个暴露别名取法  不可多个暴露 
    //多个暴露需要  xxx.default
  • 批量暴露

    let a = 0
    let b = 0
    export default {a, b}
  • 批量导入

    import num = from './a.js'

注意: 页面 html 引入 js 文件时 需要告诉浏览器

<script src='./b.js' type="module"></script>

2. 第三方模块 npm包

1. 下载

有哪些包:npm | Home

下载地址

  • 命令

    npm i 包名
  • 事件处理包 moment

2. 解决下包慢

切换下载地址

npm config set registry https://registry.npmmirror.com

管理地址切换

安装

npm i nrm -g

nrm ls  // 可用镜像地址
nrm use  taobao  //使用

3. 开发自己的包

①新建 itheima-tools 文件夹,作为包的根目录

②在 itheima-tools 文件夹中,新建如下三个文件:

package.json (包管理配置文件)

index.js (包的入口文件)

README.md (包的说明文档)

  • package.json
{
  "name": "zy-tools",
  "version": "1.0.0",
  "main": "index.js",
  "description": "提供时间格式化,HTMLEscape功能",
  "keywords": ["zy", "dateFormat","escape"],
  "license":"ISC"
}
  • index.js

    
    function dateFormat(dataStr) {
      const dt = new Date(dataStr)
    
    
      const y = dt.getFullYear()
      const m = padZero(dt.getMonth() + 1)
      const d = padZero(dt.getDate()) 
    
      const hh = padZero(dt.getHours()) 
      const mm = padZero(dt.getMinutes()) 
      const ss = padZero(dt.getSeconds()) 
    
      return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
    }
    
    // 加0
    function padZero(str) {
    
     return str < 10 ? '0' + str : str
    
    }
    module.exports = {
      dateFormat
    }
  • 使用

    const zy = require('../myPackage/index') 
    
    console.log(zy.dateFormat(new Date()))

3. 模块的加载机制

  • 内置模块

模块在第一次加载后会被缓存。 这也意味着多次调用 require() 不会导致模块的代码被执行多次。

注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率

  • 自定义模块

使用 require() 加载自定义模块时,必须指定以 ./ 或 ../ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 ../ 这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。

同时,在使用 require() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:

①按照确切的文件名进行加载

②补全 .js 扩展名进行加载

③补全 .json 扩展名进行加载

④补全 .node 扩展名进行加载

⑤加载失败,终端报错

  • 第三方模块****的加载机制

如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。

例如,假设在 'C:\Users\itheima\project\foo.js' 文件里调用了 require('tools'),则 Node.js 会按以下顺序查找:

① C:\Users\itheima\project\node_modules\tools

② C:\Users\itheima\node_modules\tools

③ C:\Users\node_modules\tools

④ C:\node_modules\tools

5. Express 模块

官方给出的概念:Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。

通俗的理解:Express 的作用和 Node.js 内置的 http 模块类似,是专门用来创建 Web 服务器的。

Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。

Express 的中文官网: http://www.expressjs.com.cn/

1. 安装

// 快速 eser

npm i express

2. 创建服务器

const express = require('express')


// 创建web服务器
const app = express()

// 启动服务器

app.listen(80, () => {
  console.log('http://127.0.0.1')
  
})

3. 监听GET POST请求

app.get('请求URL'function(req, res) => {
    // 处理函数
})
const express = require('express')


// 创建web服务器
const app = express()

// 监听get请求
app.get('/user', (req, res) => {

  // 返回JSON
  res.send({name:'张三',age: 18})
})

// 监听post请求
app.post('/users', (req, res) => {
  res.send('请求成功')
})

// 启动服务器

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

4. 获取URL传的参数?name=20

?name=20=age = 20

app.get('/', (req, res) => {
  
  res.send(req.query)
})

//{"name":"张安","age":"20"}

5. 获取动态的URL 动态参数 /user/:id


app.get('/user/:id', (req, res) => {
  res.send(req.params)
})
// {"id":":123"}

6. 访问静态资源

通过代码访问 public 下的图片 css js 文件

app.use(express.static('public'))
const express = require('express')

const app = express()


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

app.listen(80, () => {
  console.log('http://127.0.0.1')
  
})
  • 托管多个静态资源
app.use(express.static(('public'))  // 如有相同文件先查找写在前面的  如果没有 则查找后面的
app.use(express.static(('files'))
  • 访问时 想挂载路径前缀

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

7. 热部署 nodemon

  • 安装
npm i -g nodemon
  • 使用
nodemon index.js

8. Express 路由

映射关系

客户端的请求和服务器的处理函数的映射关系

  • 使用
app.请求类型(请求路径, 处理函数)

1. 路由模块化

不直接挂载到app上

  • 创建路由模块.js
  • 利用express.Router() 创建路由对象
  • 在路由对象上挂载具体路由
  • 导出路由对象
  • app.use()使用路由
const express = require('express')

const router = express.Router()

router.get('/user/list', (req, res) => {
  res.send('数据发送')
})



// 导出路由
module.exports = router
const router = require('./router.js')
const express = require('express')

const app = express()

app.use(router)
app.use('/api',router) // 可以加访问前缀

app.listen(80, () => {
  console.log('http://127.0.0.1')
  
})

9. 中间组件

业务流程的中间环节

注意:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。

  • next() 函数式定义

    next 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。

    const mw = (req, res, next) => {
        // 数据处理
        
        // 传给下个中间件或路由
        next()
    }

    1.全局生效的中间件

    客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。

    const mw = (req, res, next) => {
        // 数据处理
        
        // 传给下个中间件或路由
        next()
    }
    app.use(mw)
    
    
    
    // 简写
    app.use((req, res, next) => {
        // 数据处理
        
        // 传给下个中间件或路由
        next()
    })
  • 作用

    多个中间件之间,共享同一份 req res。基于这样的特性,我们可以在上游的中间件中,统一为 req 或 res 对象添加自定义的属性或方法,供下游的中间件或路由进行使用。

  • 定义多个全局中间件

2. 局部中间件

3. 注意事项

①一定要在路由之前注册中间件

②客户端发送过来的请求,可以连续调用多个中间件进行处理

③执行完中间件的业务代码之后,不要忘记调用 next() 函数

④为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码

⑤连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象

4. 中间件分类

  • 应用界别

    通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件

  • 路由级别

    绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上

  • 错误级别

    错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题

    格式:错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。

    **注意:**错误级别的中间件,必须注册在所有路由之后

  • 内置级别

    app.use(express.static('public'))
    express.json  // 解析JSON格式表单数据  4.16.0后可用
    express.urlencoded // 解析URL-encoded 格式的请求数据
  • 三方

    1. body-parser 解析请求数据

      npm i body-parser
      
      // 导入
      const parser = require('body-parser')
      // 使用
      app.use(parser.urlencoded({extended: false}))

5. 自定义中间件

数据太大 一部分一部分接受 监听req的data事件

数据接收完之后 会触发 req的end 事件

利用 内置qureystring 模块专门用来处理查询字符串。通过这个模块提供的 parse() 函数,可以轻松把查询字符串,解析成对象的格式。

将解析出来的数据对象挂载为 req.body

10. 写接口

  • 创建基本服务器

    const express = require('express')
    
    
    const app = express()
    
    
    
    app.listen(80, () => {
      console.log('http://127.0.0.1')
      
    })
  • 创建路由模块

    const express = require('express')
    
    const apiRouter = express.Router()
    apiRouter.get('/user', (req, res) => {
      const query = req.query
      res.send({
        status: 0,
        msg: 'GET请求成功',
        data: query
      })
    })
    
    module.exports = apiRouter
    
    
    // 返回数据
    {
      "status": 0,
      "msg": "GET请求成功",
      "data": {
        "name": "123",
        "age": "12"
      }
    }
  • POST接口

    const express = require('express')
    const apiRouter = require('./api/apiRouter')
    
    const app = express()
    app.use(express.json()) // 解析客户端json
    app.use('/api',apiRouter)
    
    
    
    app.listen(80, () => {
      console.log('http://127.0.0.1')
      
    })
    const express = require('express')
    
    const apiRouter = express.Router()
    
    // get接口
    apiRouter.get('/user', (req, res) => {
      const query = req.query
      res.send({
        status: 0,
        msg: 'GET请求成功',
        data: query
      })
    })
    
    // post接口
    apiRouter.post('/post', (req, res) => {
      const body = req.body
    
      res.send({
        status: 0,
        msg: 'POST请求成功',
        data: body
      })
    })
    
    module.exports = apiRouter

11. 跨域问题

1. CORS(主流的解决方案,推荐使用)

CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源

  • 安装

    npm i cors
  • 使用导入

    const cors = reqire('cors')
  • 在路由之前导入

    app.use(cors())

2. JSONP(有缺陷的解决方案:只支持 GET 请求)

3. cors 的响应头

  • **- Access-Control-Allow-**Origin
  • **- Access-Control-Allow-**Headers
  • **Access-Control-Allow-**Methods

12. 身份认证

对于服务端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:

① 服务端渲染推荐使用 Session 认证机制

② 前后端分离推荐使用 JWT 认证机制

session

Cookie 是存储在用户浏览器中的一段不超过 4 KB 的字符串。它由一个名称(Name)、一个值(Value)和其它几个用于控制 Cookie 有效期、安全性、使用范围的可选属性组成。

不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动当前域名下所有未过期的 Cookie 一同发送到服务器。

Cookie****的几大特性:

①自动发送

②域名独立

③过期时限

④4KB 限制

客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中。

随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给服务器,服务器即可验明客户端的身份。

2. Express 中使用 Session 认证

  • 安装

    npm i express-session
  • 配置 express-session 中间件

  • session 中****存数据

  • session 中****取数据

  • 清空 session

3. JWT 认证机制

JWT 通常由三部分组成,分别是 Header(头部)、Payload(有效荷载)、Signature(签名)。

JWT 的三个组成部分,从前到后分别是 Header、Payload、Signature。

其中:

l Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。

l Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。

4. Express 中使用 JWT

  • 安装

    npm i jsonwebtoken@8.5.1

ljsonwebtoken 用于生成 JWT 字符串

lexpress-**jwt 用于将** JWT 字符串解析还原成 JSON 对象

  • 导入

  • 定义 secret 密钥

    为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密解密的 secret 密钥:

    ①当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串

    ②当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密

  • 在登录成功****后生成 JWT 字符串
  • JWT 字符串****还原为 JSON 对象
  • 使用 req.user 获取用户信息

  • 捕获解析 JWT 失败后产生的错误

13. 密码加密

不可被逆向破解

  • 安装

    npm i bcryptjs@2.4.3
  • 使用

    const bcrypt = require('bcryptjs')
    
    // 在注册用户的处理函数中,确认用户名可用之后,调用 bcrypt.hashSync(明文密码, 随机盐的
    长度) 方法,对用户的密码进行加密处理:
    
    // 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
    userinfo.password = bcrypt.hashSync(userinfo.password, 10)

14. 表单验证

  • 安装 @hapi/joi 包,为表单中携带的每个数据项,定义验证规则:
npm install joi
  • 安装 @escook/express-joi 中间件,来实现自动对表单数据进行验证的功能:
npm i @escook/express-joi
  • 新建 /schema/user.js 用户信息验证规则模块,并初始化代码如下:
const joi = require('joi')
/**
* string() 值必须是字符串
* alphanum() 值只能是包含 a-zA-Z0-9 的字符串
* min(length) 最小长度
* max(length) 最大长度
* required() 值是必填项,不能为 undefined
* pattern(正则表达式) 值必须符合正则表达式的规则
* ref('新密码')  验证新密码一致
*/
// 用户名的验证规则
const username = joi.string().alphanum().min(1).max(10).required()
// 密码的验证规则
const password = joi
.string()
.pattern(/^[\S]{6,12}$/)
.required()
// 注册和登录表单的验证规则对象
exports.reg_login_schema = {
// 表示需要对 req.body 中的数据进行验证
body: {
username,
password,
},
}
  • 修改 /router/user.js 中的代码如下:
const express = require('express')
const router = express.Router()
// 导入用户路由处理函数模块
const userHandler = require('../router_handler/user')
// 1. 导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
// 2. 导入需要的验证规则对象
const { reg_login_schema } = require('../schema/user')
// 注册新用户
// 3. 在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证
// 3.1 数据验证通过后,会把这次请求流转给后面的路由处理函数
// 3.2 数据验证失败后,终止后续代码的执行,并抛出一个全局的 Error 错误,进入全局错误级别中间件中进行
处理
router.post('/reguser', expressJoi(reg_login_schema), userHandler.regUser)
// 登录
router.post('/login', userHandler.login)
module.exports = router
  • 在 app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户 端:
const joi = require('joi')
// 错误中间件
app.use(function (err, req, res, next) {
// 数据验证失败
if (err instanceof joi.ValidationError) return res.cc(err)
// 未知错误
res.cc(err)
})

15 上传图片

  • 安装处理forData模块

    npm i multer@1.4.2
  • 使用

    const express = require("express");
    const multer = require("multer");
    const path = require("path");
    const app = express();
    
    // 配置存储引擎
    const storage = multer.diskStorage({
      destination: (req, file, cb) => {
        cb(null, path.join(__dirname, '../uploads'); // 保存到服务器本地的目录
      },
      filename: (req, file, cb) => {
        // 生成唯一文件名:时间戳+随机数+后缀
        const uniqueName = Date.now() + "-" + Math.round(Math.random() * 1e9) + path.extname(file.originalname);
        cb(null, uniqueName);
      }
    });
    
    // 文件类型过滤
    const fileFilter = (req, file, cb) => {
      const allowedTypes = ["image/jpeg", "image/png", "image/gif"];
      if (allowedTypes.includes(file.mimetype)) {
        cb(null, true);
      } else {
        cb(new Error("仅支持 JPEG/PNG/GIF 格式"), false);
      }
    };
    
    const upload = multer({ 
      storage: storage,
      limits: { fileSize: 5 * 1024 * 1024 }, // 限制5MB
      fileFilter: fileFilter
    });
    
    // 上传接口
    app.post("/upload", upload.single("image"), (req, res) => {
      if (!req.file) {
        return res.status(400).json({ error: "未选择文件" });
      }
      // 返回文件访问路径
      res.json({ 
        url: `http://localhost:3000/uploads/${req.file.filename}` 
      });
    });
    
    // 启动服务器
    app.listen(3000, () => console.log("服务运行在 3000 端口"));
  • app.js

// 托管静态资源文件
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));