nodejs基础

一 nodejs是什么

  • nodejs是一个基于 Chrome V8 引擎上的 JavaScript 运行时
  • Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型

简单来说:Node.js就是可以让JS脱离浏览器能够运行,而且能操作文件数据库等高级应用开发功能。
nodejs的高性能,是针对于web场景,高并发,I/O密集

  • I/O密集:文件操作,网络操作,数据库
  • CPU密集:压缩,解压,加密,解密等算法

二 非阻塞I/O

  • 阻塞: I/O时进程休眠等待,I/O完成时进行下一步
  • 非阻塞: I/O时函数立即返回,进程不等待I/O完成 (异步的感觉)

三 事件驱动

  • I/O等异步操作结束后的通知(遇到I/O,不等I/O执行完直接执行下面程序,等其执行完发射一个事件,对事件找其相应的处理程序)
  • 观察者模式

四 环境

4.1 模块规范 commonjs

(function(exports, require, module, _filename, _dirname){ console.log(‘this is a test’) })

  • 每个文件就是一个模块,有自己的作用域
  • 在模块内部module变量代表模块本身
  • module.exports属性代表模块对外接口
4.2 require特性
  • require运行时加载,加载一个模块的时候,其所有语句会被执行。(import编译时加载)
  • module被加载的时候执行,加载后缓存,也就是不会再执行
  • 一旦出现某个模块被循环加载,就只输出已经执行的部分,还未执行的部分不会输出 如b依赖a a依赖b
// main.js
const modA = require('./module_a')
const modB = require('./module_b')
console.log(modA.test)
console.log(modB.test)


// module_a.js
module.exports.test = 'A'
const modB = require('./module_b')
console.log('modA: ',modB.test)

module.exports.test = 'AA'


// module_b.js
module.exports.test = 'B'
const modA = require('./module_a')
console.log('modB:',modA.test)

module.exports.test = 'BB'


node main.js将会输出
modB: A
modA: BB
AA
BB
  1. 加载执行a模块, a模块的test是A,走到加载执行b模块, 这里a模块还没有执行完
  2. 进去b模块, b模块的test是B, 又走到a模块, 但是a模块已经执行过, 不再加载执行, 此时modA.test=“A”, 打印modB : A, 之后改变b模块的test为BB, 此时B模块走完
  3. 继续走a模块, console.log('modA: ',modB.test), 打印modA : BB,再到a模块的test是AA
  4. 再继续回到main.js继续执行第二行代码,module_b已执行不再执行了

正式项目里有可能会有多个文件循环依赖,是造成不可预知的问题,所以应该避免循环依赖

4.3 module.exports & exports
  • 其实exports是module.exports的指向,即const exports = module.exports
  • 如果exports不指向module.exports,也就毫无意义
    eg: exports = {test: 3}
    这样就改变了exports的指向,模块里不会导出test
  • 可以向exports添加属性 exports.test = 3, 这样ok的

也就是说exports是module.exports的一个引用,或者说是快捷方式.

实际开发中遇到的问题,一个文件里import和module.exports或者exports混用:

4.4 process
1. 参数相关
  • argv 获得运行命令所有参数(数组)
  • argv0 命令的第一个参数
  • execArgv 文件路径之前的node命令相关的参数
  • execPath node的路径
    最常用的是argv,不过现在获取命令行参数都用 yargs
2. 环境相关
  • env 当前电脑的环境信息
  • __dirname: 获得当前执行文件所在目录的完整目录名
    __filename: 获得当前执行文件的带有完整绝对路径的文件名
    process.cwd():获得当前执行node命令时候的文件夹目录名
    ./: 文件所在目录
3. timer
  • setImmediate
  • nextTick 快于 setTimout和setInterval 快于 setImmediate
  • nextTick将其事件放在当前队列的最后一个
  • setImmediate将其事件放在下一个队列的队首
  • setTimeout和setInterval介于两者之间
  • 但是process.nextTick里面嵌套process.nextTick是很不好的事情,复杂的逻辑会导致下一个队列不能及时执行
  • 所以一般尽量用setImmediate代替nextTick, setImmediate也是后来node出现的,用于优化
setImmediate(()=>{
  console.log('setImmediate')
})
process.nextTick(() => {
  console.log('nextTick')
})
setTimeout(()=>{
  console.log('setTimeout')
},0)
setTimeout(()=>{
  console.log('10setTimeout')
},10)

// nextTick
// setTimeout
// setImmediate
// 10setTimeout

五 基础API

  • path
  • fs
  • events
  • Buffer
  • 解决异步回调地狱 promisify

使用promisify和同步,异步的区别

const fs = require('fs')
const path = require('path')
const { promisify } = require('util')
const rPath = path.join(__dirname)

function f1(){ // 分别调用 f2 f3 f4,看执行情况
  f2()
  console.log(111)
}

function f2(){
  const files = fs.readdirSync(rPath)
  console.log('--f2--', files)
  console.log(222)
}

function f3(){
  fs.readdir(rPath, (err, files) => {
    console.log('--f3--', files)
  })
  console.log(333)
}

async function f4(){
  const files = await promisify(fs.readdir)(rPath)
  console.log('--f4--', files)
  console.log(444)
}

f1()
const server1 = http.createServer((req, res) => {
  const filePath = path.join(root, req.url)
  
  fs.stat(filePath, (err, stats) => {
    if(err){
      res.statusCode = 404
      res.end(`${filePath} is not a file or directory`)
      return
    }

    if(stats.isFile()){
      res.statusCode = 200
      res.setHeader('Content-Type', 'text/plain')
      fs.createReadStream(filePath).pipe(res)
    }else if(stats.isDirectory()){
      fs.readdir(filePath, (err, files) => {
        res.statusCode = 200
        res.setHeader('Content-Type', 'text/plain')
        res.end(files.join(','))
      })
    }
  })
})

const server2 = http.createServer(async (req, res) => {
  const filePath = path.join(root, req.url)

  try{
    const stats = await promisify(fs.stat)(filePath)

    if(stats.isFile()){
      res.statusCode = 200
      res.setHeader('Content-Type', 'text/plain')
      fs.createReadStream(filePath).pipe(res)
    }else if(stats.isDirectory()){
      const files = await promisify(fs.readdir)(filePath)
      res.statusCode = 200
      res.setHeader('Content-Type', 'text/plain')
      res.end(files.join(','))
    }
  }catch(err){
    res.statusCode = 404
    res.setHeader('Content-Type', 'text/plain')
    res.end(`${filePath} is not a file or directory`)
  }
})
import { promisify } from 'util'

const getCopyPatterns = async plat => {
  // 获取要复制的node包
  const root = 'root/src/lib';
  const mainSoFiles = await promisify(fs.readdir)(root).catch(err => {
    throw chalk.red(err)
  });
	
  if (!~mainSoFiles.indexOf(plat)) {
    return [];
  }
	
  const mainSoFileDir = path.join(root, plat);
  
  const platformFiles = await promisify(fs.readdir)(mainSoFileDir).catch(err => {
    throw chalk.red(err)
  });
  
  const copyPatterns = [];
  platformFiles.forEach(async filename => {
  
    const filedir = path.join(mainSoFileDir, filename);
    const fsStats = await promisify(fs.stat)(filedir).catch(err => {
      throw chalk.red(err)
    });
    
    if (fsStats.isFile() && /\.DS_Store$/.test(filename)) return;
    
    copyPatterns.push({
      from: filedir
    });
  });
	
  return copyPatterns;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值