# node 基础

# 1 node 实现api服务

  // console.log(11)
  // 需求: 希望有一个服务,可以依据请求的接口内容返回相应的数据
  import express from 'express'
  import { DataStore } from './data'
  console.log(DataStore.list)

  const app = express()
  app.get('/', (req, res) => {
    // res.end('1122')
    res.json(DataStore.list)
  })

  app.listen(8081, () => {
    console.log('服务已经开启')
  })

# 2、全局对象

全局对象是Javascrip中的特殊对象,全局对象可以看做是全局变量的宿主

  • 与浏览器平台的window不完全相同
  • Nodejs 全局对象上挂载许多属性
# 1、Nodejs常见全局变量
+ __filename: 返回正在执行脚本文件的绝对路径
+ __dirname: 返回正在执行脚本所在目录
+ timer类函数: 执行顺序与事件循环间的关系
+ process: 提供与当前进程互动的接口
+ require: 实现模块的加载
+ module、exports: 处理模块的导出
# 2、全局变量值 process
  • 无须 require 可直接使用
  • 获取进程信息
  • 执行进程操作
  // 1 资源: cpu 内存
  const fs = require('fs')
  Buffer.alloc(1000)
  // console.log(process.memoryUsage())
  // rss 常驻内存
  // heapTotal:总的内存
  // heapUsed: 实际使用内存
  // external: 扩展内存
  // arrayBuffers: 一片独立的内存,不占据V8内存
  console.log(process.cpuUsage()) 
  // 2 运行环境:运行目录、node环境、cpu架构、用户环境、系统平台
  console.log(process.cwd())// 当前的工作目录
  console.log(process.version)// 当前node版本
  console.log(process.versions)// 多一点的信息版本
  console.log(process.arch)// 本机操作系统
  console.log(process.env.NODE_ENV)// 环境信息
  console.log(process.env.path)// 本机环境变量
  console.log(process.env.USERPOFILE)// 本机设置关机元目录
  console.log(process.env.HOME)// 本机设置关机元目录(mac)
  console.log(process.platform)// 平台

  // 3 运行状态: 启动参数、PID、运行时间、
  console.log(process.argv) // 启动参数
  console.log(process.argv0)// 第一个参数 execArgv -- 参数
  console.log(process.pid)// 当前js程序运行过程中占据的唯一id  ppid
  console.log(process.uptime())// 运行时间   

  // 4 事件
  process.on('exit', (code) => {
    console.log('exit' + code)
    setTimeout(() => {
      console.log(123) // 不支持异步代码
    }, 100);
  })
  process.on('beforeExit', (code) => {
    console.log('beforeExit' + code)
    // 支持异步代码
  })

  console.log('代码执行完了')
  // process.exit()

  // 05 标准 输出 输入 错误
  console.log = function (data) {
    process.stdout.write('---' + data + '\n') // 流
  }

  // console.log(11)

  // fs.createWriteStream('text.txt')
  //   .pipe(process.stdout) // pipe 管道

  // process.stdin.pipe(process.stdout) // 标准输出

  process.stdin.setEncoding('utf-8') // 设置了字符编码 防止乱码
  // 监听是否可读,如果可读  把东西取出来,拿出来读出来
  process.stdin.on('readable', () => {
    let chunk = process.stdin.read() // 代码块去拿输入的一些东西,拿完以后做判断
    if (chunk != null) {
      process.stdout.write('data ' + chunk) // 输出相应内容
    }
  })

# 3 path模块

用于处理文件/目录的路径

# 1、 path模块常用API
  • basename() 获取路径中的基础名称
      /**
       * path路径的最后一个部分 第二个参数 有返回不带后缀的名字 没有 返回全部
      * 第二个参数表示客栈明,如果没有设置则返回完整的文件名称带后缀
      * 第二个参数作为后缀时, 
      */ 
      path.basename(__filename)  // 06-path.js
      path.basename(__filename, 'js')  // 06-path
      path.basename(__filename, 'css')  // 06-path.js
      path.basename('/a/b/c')  // c
      path.basename('/a/b/c/')  // c
    
  • dirname() 获取路径中的目录名称
      /**
       * 01 返回路径中最后一个部分的上一层目录所在路径
      *
      */
      console.log(path.dirname(__filename)) // 目录的路径
      path.dirname('/a/b/c') // /a/b
      path.dirname('/a/b/c/') // /a/b
    
  • extname() 获取路径中扩展名称
      // 01 返回 path 路径中响应文件的后缀名
      // 02 如果path路径中有多个点,它匹配最后一个点,到结尾的内容
      path.extname(__filename)  // .js
      path.extname('/a/b')  // 
      path.extname('/a/b/index.html.js.css')  // .css
      path.extname('/a/b/index.html.js.')  // .
    
  • parse() 解析路径
    
      /**
       * 01 接受一个路径,返回一个对象,包含不同的信息
      * 02 root dir base ext name 
      */
      const obj = path.parse('/a/b/c/index.html')
      console.log(obj) //{root: '/', dir: '/a/b/c', base: 'index.html', ext: '.html', name: 'index'}
    
  • isAbsolute() 判断路径中是否为绝对路径
      console.log(path.isAbsolute('foo')) // false
      console.log(path.isAbsolute('/foo')) // true
      console.log(path.isAbsolute('///foo')) // true
      console.log(path.isAbsolute('')) // false
      console.log(path.isAbsolute('.')) // false
      console.log(path.isAbsolute('../bar')) // false
    
  • join() 拼接多个路径片段
      console.log(path.json('a/b', 'c', 'index.html')) // a\b\c\index.hmtl
      console.log(path.json('/a/b', 'c', '../', 'index.html')) // \a\b\index.hmtl
      console.log(path.json('/a/b', 'c', './', 'index.html')) // \a\b\c\index.hmtl
      console.log(path.json('/a/b', 'c', '', 'index.html')) // \a\b\c\index.hmtl
      console.log(path.json('')) // .
    
  • resolve() 返回绝对路径
      /**
       * resolve([from], to) // 把to变为绝对路径
      * 
      */
      path.resolve() // 绝对路径 /Desktop/node/code
      path.resolve('/a', '/b') //  /b
    
  • format() 序列化路径
      const obj = path.parse('./a/b/c')
      console.log(path.format(obj))
    
    
  • mormalize() 规范化路径
      path.normalize('') // .
      path.normalize('a/b/c/d') // a/b/c/d 
      path.normalize('a///b/c../d') // a/b/c../d 
      path.normalize('a//\\/b/c\\/d') // a/b/c/d 
      path.normalize('a//\\b/c\\/d') // a/c/d 
    

# 4、Buffer 是 Nodejs 的内置类

是除process外的另一个重要的全局变量,Buffer缓冲区 Buffer 让 Javascript可以操作二进制 Buffer是什么?在哪?做什么? Nodejs平台下Javascript可实现IO,IO行为操作的就是二进制数据 Stream 流操作并非 Nodejs 独创 流操作配合管道实现数据分段传输 数据的端到端传输会有生产者和消费者

nodejs 中 buffer 是一片内存空间

  • node下的一个全局变量
  • 实现Nodejs平台下的二进制数据操作
  • 不占据V8堆内存大小的内存空间
  • 内存的使用由Node来控制,由V8的GC回收
  • 一般配合 Stream 流只用,充当数据缓存区
# 1、创建Bugger实例
  • alloc: 创建指定字节大小的buffer
  • allocUnsafe: 创建指定管大小的Buffer(不安全)
  • from: 接受数据,创建buffer
# 2、 Buffer实例方法
  • fill: 使用数据填充 buffer
      let buf = Buffer.alloc(6)
      buf.fill('123') // 123123
    
    
  • write: 向buffer 中写入数据
  • toString: 从buffer中提取数据
  • slice: 截取buffer
  • indexOf: 在buffer 中查找数据
  • copy: 拷贝 buffer 中的数据
# 3、Buffer 静态方法
  • concat: 将多个buffer拼接成一个新的buffer
  • isBuffer: 判断当前数据是否为 buffer
# 4、自定义 Buffer 值 split
```js
  ArrayBuffer.prototype.split = function (sep) {
    let len = Buffer.from(sep).length
    let ret = []
    let start = 0
    let offset = 0

    while(offset = this.indexOf(seq, start) !== -1) {
      ret.push(this.slice(start, offset))
      start = offset + len 
    }
    ret.push(this.slice(start))
    return ret
  }
```

# 5、 FS模块

  • fs是Nodejs中内置核心模块
  • 代码层面上fs氛围基本操作类和常用API
  • 权限位、标识符、文件描述
# 1、Fs模块结构
  • FS 基本操作类
  • FS 常用 API
# 2、权限位、标识符、文件描述符
  • 权限位: 用户对于文件所具备的操作权限 r 4 读 w 2 写 x 1 执行

    • flag表示对文件操作方式

      • r:表示可读
      • w:表示可写
      • s:表示同步
      • +:表示执行相反操作
      • x:表示排它操作
      • a:表示追加操作
    • fd 就是操作系统分配给被打开文件的标识

# 3 文件读写与拷贝操作
  • 文件操作API
  • readFile: 从指定文件中读取数据

  • writeFile: 向指定文件中写入数据

  • appendFile:追加的方式想指定的文件中写入数据

  • copyFile: 将某个文件中的数据拷贝至另一文件

  • watchFile: 对指定文件进行监控

      const fs = require('fs')
      const path = require('path')
    
      // readFile
      fs.readFile(path.resolve('data.txt'), 'utf-8', (err, data) => {
        console.log(err)
        if (!null) {
          console.log(data)
        }
      })
    
      // writeFile 覆盖的写操作 路径不存在,会创建一个操作
    
      fs.writeFile('data.txt', 'dfasf', {
        mode: 438,
        flag: 'r+',// 可读可写
        encoding: 'utf-8'
      }, (err) => {
        if (!err) {
          fs.readFile('data.txt', 'utf-8', (err, data) => {
            console.log(data)
          })
        }
      })
    
      // apendFile
    
      fs.appendFile('data.txt', 'hello node.js', (err) => {
        console.log('写入成功')
      })
    
      // // copyFile
      fs.copyFile('data.txt', 'test.txt', () => {
        console.log('拷贝成功')
      })
    
      // watchFile
      fs.watchFile('data.txt', {
        interval: 20
      }, (curr, prev) => {
        // curr 修改之后的文件  prev 修改之前的文件
        if (curr.mtime != prev.time) {
          console.log('文件被修改了')
          fs.unwatchFile('data.txt')
        }
      })
    
# 5、文件操作实现 md 转 html
  const fs = require('fs')
  const path = require('path')
  const marked = require('marked')
  const browserSync = require('browser-sync')

  /**
   * 01 读取 md 和 css 内容
   * 02 将上述读取出来的内容替换占位符,生成一个最终需要展的 Html 字符串 
   * 03 将上述的 Html 字符写入到指定的 Html 文件中
   * 04 监听 md 文档内容的变经,然后更新 html 内容 
   * 05 使用 browser-sync 来实时显示 Html 内容
   */

  let mdPath = path.join(__dirname, process.argv[2])
  let cssPath = path.resolve('github.css')
  let htmlPath = mdPath.replace(path.extname(mdPath), '.html')

  fs.watchFile(mdPath, (curr, prev) => {
    if (curr.mtime !== prev.mtime) {
      fs.readFile(mdPath, 'utf-8', (err, data) => {
        // 将 md--》html
        let htmlStr = marked(data)
        fs.readFile(cssPath, 'utf-8', (err, data) => {
          let retHtml = temp.replace('{{content}}', htmlStr).replace('{{style}}', data)
          // 将上述的内容写入到指定的 html 文件中,用于在浏览器里进行展示
          fs.writeFile(htmlPath, retHtml, (err) => {
            console.log('html 生成成功了')
          })
        })
      })
    }
  })

  browserSync.init({
    browser: '',
    server: __dirname,
    watch: true,
    index: path.basename(htmlPath)
  })

  const temp = `
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title></title>
          <style>
              .markdown-body {
                  box-sizing: border-box;
                  min-width: 200px;
                  max-width: 1000px;
                  margin: 0 auto;
                  padding: 45px;
              }
              @media (max-width: 750px) {
                  .markdown-body {
                      padding: 15px;
                  }
              }
              {{style}}
          </style>
      </head>
      <body>
          <div class="markdown-body">
              {{content}}
          </div>
      </body>
      </html>
  `
# 6、文件打开与关闭
```js 
  fs.open(path.resolve('data.txt'), 'r', (err, fd) => {
    console.log(fd)
  })
  // close
  fs.open('data.txt', 'r', (err, fd) => {
    console.log(fd)
    fs.close(fd, err => {
      console.log('关闭成功' )
    })
  })
  fs.close()
```
# 7、大文件读写操作
```js
  const fs = require('fs')

  // read : 所谓的读操作就是将数据从磁盘文件中写入到 buffer 中
  let buf = Buffer.alloc(10)

  /**
   * fd 定位当前被打开的文件 
   * buf 用于表示当前缓冲区
   * offset 表示当前从 buf 的哪个位置开始执行写入
   * length 表示当前次写入的长度
   * position 表示当前从文件的哪个位置开始读取
   */
  /* fs.open('data.txt', 'r', (err, rfd) => {
    console.log(rfd)
    fs.read(rfd, buf, 1, 4, 3, (err, readBytes, data) => {
      console.log(readBytes)
      console.log(data)
      console.log(data.toString())
    })
  }) */

  // write 将缓冲区里的内容写入到磁盘文件中
  buf = Buffer.from('1234567890')
  fs.open('b.txt', 'w', (err, wfd) => {
    fs.write(wfd, buf, 2, 4, 0, (err, written, buffer) => {
      console.log(written, '----')
      fs.close(wfd)
    })
  })
```
# 8、文件拷贝自定义实现
```js
  const fs = require('fs')

  /**
   * 01 打开a文件,利用read 将数据保存到 buffer 中暂存起来
   * 02 打开 b 文件,利用 write 将 buffer 中数据写入到 b 文件中
   * 
   */ 

  let buf = Buffer.alloc(10)

  // 01 打开指定文件
  fs.open('a.txt', 'r', (err, rfd) => {
    // 02 从打开的文件中读取数据
    fs.read(rfd, buf, 0, 10, 0, (err, readBytes) => {
      // 03 打开 b 文件,用于执行数据写入操作
      fs.open('b.txt', 'w', (err, wfd) => {
        // 04 将 buffer 中的数据写入到 b.txt 中
        fs.write(wfd, buf, 0, 10, 0, (err, written) => {
          console.log('写入成功')
        })
      })
    })
  })

  fs.open('a.txt', 'r', (err, rfd) => {
    // 03 打开 b 文件,用于执行数据写入操作
    fs.open('b.txt', 'w', (err, wfd) => {
      // 02 从打开的文件中读取数据
      fs.read(rfd, buf, 0, 10, 0, (err, readBytes) => {
        // 04 将 buffer 中的数据写入到 b.txt 中
        fs.write(wfd, buf, 0, 10, 0, (err, written) => {
          console.log('写入成功')
        })
      })
    })
  })

  // 02 数据的完全拷贝
  fs.open('a.txt', 'r', (err, rfd) => {
    fs.open('b.txt', 'a+', (err, wfd) => {
      fs.read(rfd, buf, 0, 10, 0, (err, readBytes) => {
        fs.write(wfd, buf, 0, 10, 0, (err, written) => {
          fs.read(rfd, buf, 0, 5, 10, (err, readBytes) => {
            fs.write(wfd, buf, 0, 5, 10, (err, written) => {
            console.log('写入成功')
          }) 
        })
        }) 
      })
    })
  })

  const BUFFER_SIZE = buf.length
  let readOffset = 0

    
  fs.open('a.txt', 'r', (err, rfd) => {
    fs.open('b.txt', 'w', (err, wfd) => {
      function next () {
        fs.read(rfd, buf, 0, BUFFER_SIZE, readOffset, (err, readBytes) => {
          if (!readBytes) {
            // 如果条件成立,说明内容已经读取完毕
            fs.close(rfd, ()=> {})
            fs.close(wfd, ()=> {})
            console.log('拷贝完成')
            return
          }
          readOffset += readBytes
          fs.write(wfd, buf, 0, readBytes, (err, written) => {
            next()
          })
        })
      }
      next()
    })
  })
```
# 9、FS之目录操作 API
+ access:判断文件活目录是否具有操作权限
  ```js
    fs.access('a.txt', (err) => {
    if (err) {
      console.log(err)
    } else {
      console.log('有操作权限')
    }
  })
  ```
+ stat:获取目录及文件信息
  ```js
    fs.stat('a.txt', (err, statObj) => {
      console.log(statObj.size)
      console.log(statObj.isFile())
      console.log(statObj.isDirectory())
    })
  ```
+ mkdir: 创建目录
  ```js
    fs.mkdir('a/b/c', {recursive: true}, (err) => { // 父级目录是存在的  recursive 递归创建
    if (!err) {
      console.log('创建成功')
    } else {
      console.log(err)
    }
  })
  ```
+ rmdir:删除目录
  ```js
    // 4 rmdir // 默认删除空目录  basename recursive: 删除费空目录
    fs.rmdir('a', {recursive: true}, (err) => {
      if (!err) {
        console.log('删除成功')
      } else {
        console.log(err)
      }
    })
  ```
+ readdir: 读取目录中的内容
  ```js
    fs.readdir('a', (err, files) => {
      console.log(files)  // [ 'a.txt', 'b' ]
    })

  ```
+ unlink; 删除指定文件
  ```js
    fs.unlink('a/a.txt', (err) => {
      if (!err) {
        console.log('删除成功')
      }
    })
  ```
# 10、创建目录之间同步实现
```js
  const fs = require('fs')
  const path = require('path')

  /**
  * 01 将来调用时需接受类似于 a/b/c, 这样的路径,它们时间是采用 / 去链接
  * 02 利用 / 分隔符将路径进行拆分,将每一项放入一个数组中进行管理 ['a', 'b', 'c']
  * 03 对上述的数组进行遍历,我们需要拿到每一项,然后与前一项进行拼接 / 
  * 04 判断一下当前对拼接之后的路径是否具有可操作性的权限,如果有则证明存在,否则的话就需要执行创建
  */
  function makeDirSync (dirPath) {
    let items = dirPath.split(path.sep)
    for (let i = 1; i <= items.length; i++) {
      let dir = items.slice(0, i).join(path.sep)
      try {
        fs.accessSync(dir)
      } catch (err) {
        fs.mkdirSync(dir)
      }
    }

  }

  makeDirSync('a/b/c')
```