node--基础篇(一)

319 阅读5分钟

I/O处理(fs)

1. 同步调用readFileSync

const fs = require('fs');

// 同步调用
const data = fs.readFileSync('./conf.js'); //代码会阻塞在这里 
console.log(data);

2. 异步调用readFile

const fs = require('fs');

// 异步调用
fs.readFile('./conf.js', (err, data) => {
    if (err) throw err;
    console.log(data);
})

3. promisify

const fs = require('fs');
const {promisify} = require('util')

const readFile = promisify(fs.readFile)
readFile('./conf.js').then(data=>console.log(data))

4. promisify API

const fsp = require("fs").promises;

fsp
  .readFile("./confs.js")
  .then(data => console.log(data))
  .catch(err => console.log(err));

5. async/await

(async () => {
    const fs = require('fs')
    const { promisify } = require('util')
    const readFile = promisify(fs.readFile)
    const data = await readFile('./index.html')
    console.log('data',data)
})()

Buffer缓冲区(Buffer类似数组,所以很多数组方法它都有)

读取数据类型为Buffer,Buffer - 用于在 TCP 流、文件系统操作、以及其他上下文中与八位字节流进行交互。 八位字节组 成的数组,可以有效的在JS中存储二进制数据

1. Buffer.alloc创建字节

// 创建一个长度为10字节以0填充的Buffer
const buf1 = Buffer.alloc(10); 
console.log(buf1);

2. Buffer.from

// 创建一个Buffer包含ascii.
// ascii 查询 http://ascii.911cha.com/
const buf2 = Buffer.from('a') 
console.log(buf2,buf2.toString())
// 创建Buffer包含UTF-8字节
const buf3 = Buffer.from('Buffer创建方法');
console.log(buf3);

3. write写入数据

const buf1 = Buffer.alloc(10);  // 创建Buffer
buf1.write('hello'); // 写入Buffer数据
console.log(buf1);

4. toString()读取Buffer数据

onst buf3 = Buffer.from('Buffer创建方法');
console.log(buf3.toString()); //// 读取Buffer数据 

5. concat合并Buffer

const buf4 = Buffer.concat([buf1, buf3]);
console.log(buf4.toString());

http服务

1.创建一个http服务器

const http = require('http');
const http = require('http');
const server = http.createServer((request, response) => {
    console.log('there is a request');
    response.end('a response from server');
});
server.listen(3000);

接口

1. 显示一个服务端页面

const {url, method} = request; //请求的地址和方法
 if (url === '/' && method === 'GET') {
   fs.readFile('index.html', (err,data) => {
     if(err) {
       response.writeHead(500, { 'Content-Type':'text/plain;charset=utf-8' })
       response.end('500,服务器错误');
       return
     }
     response.statusCode = 200; // 设置返回状态码
     response.setHeader('Content-Type', 'text/html'); // 设置返回数据格式
     response.end(data);
   })
 } else {
  response.statusCode = 404;
  response.setHeader('Content-Type', 'text/plain;charset=utf-8'); 			   response.end('404, 页面没有找到');
}

2. 接着编写一个接口

else if (url === '/users' && method === 'GET') {
    response.writeHead(200, { 'Content-Type': 'application/json' });
    response.end(JSON.stringify([{name:'tom',age:20}]));
}

Stream流

stream - 是用于与node中流数据交互的接口

1. fs.createReadStream->fs.createWriteStream->pipe

//二进制友好,图片操作
const fs = require('fs')
const rs2 = fs.createReadStream('./01.jpg')
const ws2 = fs.createWriteStream('./02.jpg')
rs2.pipe(ws2);

2. 响应图片请求

const {url, method, headers} = request;

else if (method === 'GET' && headers.accept.indexOf('image/*') !== -1) {
    fs.createReadStream('.'+url).pipe(response);
}

备注:

  1. Accept代表发送端(客户端)希望接受的数据类型,比如:Accept:text/xml; 代表客户端希望 接受的数据类型是xml类型。
  2. Content-Type代表发送端(客户端|服务器)发送的实体数据的数据类型。 比如:Content- Type:text/html; 代表发送端发送的数据格式是html。
  3. 二者合起来, Accept:text/xml; Content-Type:text/html ,即代表希望接受的数据类型是xml格 式,本次请求发送的数据的数据格式是html。

CLI工具

1. 创建工程

mkdir vue-auto-router-cli
cd vue-auto-router-cli
npm init -y
npm i commander download-git-repo ora handlebars figlet clear chalk open -s

// download-git-repo 实现clone
// ora 下载进度条,loading状态提示
// commander 定制命令
// figlet把小的文字放大
// clear 清屏
// chalk 设置彩色cmd
// open 自动打开浏览器

2. 自定义bin文件---bin/candy.js

// 指定脚本解释器为node 
#!/usr/bin/env node 
console.log('cli.....')

3. 配置package.json

// 当执行candy的时候,指向./bin/candy.js
"bin": {
  "candy": "./bin/candy.js"
},

4. npm link,将npm模块链接到对应的运行项目中去(一定要link,否则找不到自定义命令)

cd vue-auto-router-cli
执行
npm link

5. 在cmd执行candy,就会运行candy.js的代码

6. 定制命令行界面---candy.js

#!/usr/bin/env node 
// 指定脚本解释器为node

引入commander包
const program = require('commander')

// 这里就是配置我们输入candy -v显示版本号
program.version(require('../package').version)

// program.command用于定制命令
program
  .command('init <name>') // 初始化一个xxx的文件
  .description('init project') // 文件描述
  .action(name => {            // 功能,初始化打开欢迎界面
     require('../lib/init')
  })

// 固定写法,解析输入的参数
program.parse(process.argv)

7. 打开欢迎界面---lib/init.js

const {promisify} = require('util')
const figlet = promisify(require('figlet'))
const clear = require('clear')
const chalk = require('chalk')
const log = content => console.log(chalk.green(content))
module.exports = async name => {
// 打印欢迎画面
clear()
const data = await figlet('Candy Welcome') log(data)
}

8. 克隆脚手架---lib/download.js

const {promisify} = require('util')
module.exports.clone = async (repo,desc) => {
    // repo 克隆地址
    // desc 下载到哪个目录下
    const download = promisify(require('download-git-repo'))
    const ora = require('ora')
    const process = ora(`下载.....${repo}`)
    // 启动进度条
    process.start()
    await download(repo, desc)
    // 启动成功
    process.succeed()
}

---修改/lib/init.js

const {clone} = require('./download')
module.exports.init = async name => {
log('🚀创建项目:' + name)
// 从github克隆项目到指定文件夹
await clone('github:su37josephxia/vue-template', name)
}

9. 安装依赖---直接在init里面封装一个子进程

// promisiy化spawn
// 对接输出流
const spawn = async (...args) => {
    // 启动一个子进程
    const { spawn } = require('child_process')
    // 封装一个promise
    return new Promise(resolve => {
        // 执行需要执行的命令args
        const proc = spawn(...args)
        // proc子进程产生正常的数据流跟主进程对接pipe
        proc.stdout.pipe(process.stdout)
        // proc子进程产生异常的数据流跟主进程对接pipe
        proc.stderr.pipe(process.stderr)
        proc.on('close', () => {
            resolve()
        })
    })
}

module.exports.init = async name => {
log('安装依赖')
// 安装依赖
// cnpm 命令
// 第二个参数可以放很多,所以是个数组
// cwd 指定运行的路径
await spawn('cnpm', ['install'], { cwd: `./${name}` })
// 打印一个完成的标识
log(`
👌安装完成:
To get Start:
===========================
    cd ${name}
    npm run serve
===========================
`)
}

10.配置自启动项目

const open = require("open")
module.exports.init = async name => {
// ...
// 打开浏览器
open(`http://localhost:8080`);
await spawn('cnpm', ['run', 'serve'], { cwd: `./${name}` })
}

11.执行命令,创建一个test工程并初始化,最后自动打开浏览器窗口

candy init test

12. 完整的初始化代码----init.js

const {promisify} = require('util')
const figlet = promisify(require('figlet'))
const clear = require('clear')
const chalk = require('chalk')
const {clone} = require('./download')
const spawn = async (...args) => {
  // 启动一个子进程
  const {spawn} = require('child_process')
  // 封装一个promise
  return new Promise(resolve => {
    // 执行需要执行的命令args
    const proc = spawn(...args)
    // proc子进程产生正常的数据流跟主进程对接pipe
    proc.stdout.pipe(process.stdout)
    // proc子进程产生异常的数据流跟主进程对接pipe
    proc.stderr.pipe(process.stderr)
    proc.on('close', () => {
      resolve()
    })
  })
}
const log = content => console.log(chalk.green(content))
module.exports = async name => {
  // 打印欢迎画面
  clear()
  const data = await figlet('Candy Welcome')
  log(data)
  // 创建项目
  log(`🚀创建项目:` + name)
  // 克隆代码
  await clone('github:su37josephxia/vue-template', name)
  log('安装依赖')
  // cnpm 命令
  // 第二个参数可以放很多,所以是个数组
  // cwd 指定运行的路径
  await spawn('cnpm', ['install'], {cwd: `./${name}`})
  log(`
👌安装完成:
To get Start:
===========================
    cd ${name}
    npm run serve
===========================
`)

  const open = require('open')
  open('http://localhost:8080')
  await spawn('npm', ['run', 'serve'], {cwd: `./${name}`})
}

实现自动生成路由表,自动生成菜单

  • loader 文件扫描
  • 代码模板渲染 hbs Mustache风格模板

1.编写自动生成模板 ----lib/refresh.js

const fs = require('fs')
// 引入模板库引擎
const handlebars = require('handlebars')
// 引入粉笔工具
const chalk = require('chalk')
// 输出一个新的函数
module.exports = async () => {
  // 获取页面列表
  const list =
    // fs同步读目录
    fs.readdirSync('./src/views')
      .filter(v => v !== 'Home.vue')
      .map(v => ({
        name: v.replace('.vue', '').toLowerCase(),
        file: v
      }))
  
  // 生成路由
  compile({list}, './src/router.js', './template/router.js.hbs')
  // 生成菜单
  compile({list}, './src/App.vue', './template/App.vue.hbs')


  /**
   *
   * @param {*} meta 数据定义
   * @param {*} filePath 目标文件
   * @param {*} templatePath 模板文件
   */
  function compile(meta, filePath, templatePath) {
    // 判断模板文件是否存在
    if (fs.existsSync(templatePath)) {
      // 同步读取文件
      const content = fs.readFileSync(templatePath).toString()
      // 生成编译后的结果
      const reslut = handlebars.compile(content)(meta)
      // 同步写入文件
      fs.writeFileSync(filePath, reslut)
    }
    console.log(chalk.red(`🚀${filePath} 创建成功`))
  }

}

2. 定义一个新的命令----bin/candy.js

program
    .command('refresh')
    .description('refresh routers...')
    .action(require('../lib/refresh'))

监听变化

1.类似于热更新的功能---lib/serve.js

const spawn = (...args) => {
    const { spawn } = require('child_process');
    const proc = spawn(...args)
    proc.stdout.pipe(process.stdout)
    proc.stderr.pipe(process.stderr)
    return proc
}

module.exports = async () => {
    const watch = require('watch')
    let process
    let isRefresh = false
    watch.watchTree('./src', async (f) => {
        if (!isRefresh) {
            isRefresh = true
            process && process.kill()
            await require('./refresh')()
            setTimeout(() => { isRefresh = false }, 5000)
            process = spawn('npm', ['run', 'serve'])
        }
    })
}

2. 定义一个新的命令----bin/candy.js

program
    .command('serve')
    .description('serve')
    .action(require('../lib/serve'))

发布npm

1.根目录生成----publish.sh

#!/usr/bin/env bash
npm config get registry # 检查仓库镜像库
npm config set registry=http://registry.npmjs.org
echo '请进行登录相关操作:'
npm login # 登陆
echo "-------publishing-------"
npm publish # 发布
npm config set registry=https://registry.npm.taobao.org # 设置为淘宝镜像 echo "发布完成"
exit

2. 执行

npm publish