node

发布时间 2023-07-17 16:45:04作者: yeweiliang95

安装

Linux

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash

# 查看nvm版本
nvm -v

# 配置淘宝镜像 提升下载速度
export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node/

# 查看node版本列表
nvm ls-remote

# 安装node 16.20.1版本
nvm install 16.20.1

#选择node版本
node use xxx

# 查看node版本
node -v

#现在node版本
nvm uninstall xxx

# 查看安装的所以版本
nvm ls

Node设置淘宝镜像

npm config set registry=https://registry.npm.taobao.org/

命令行接收参数

process对象 公开的argv属性

argv :

第一个参数: node命令完整地址

第二个参数:正在被执行的文件地址

其他参数从第三个开始

  • 循环迭代

    process.argv.forEach((val, index) => {
      console.log(`${index}: ${val}`)
    })
    
  • 数组

    const args = process.argv.slice(2) //屏蔽掉前两个参数
              
    //没有索引
    const args = process.argv.slice(2)
    args[0]
              
    //索引  node app.js name=joe  引用minimist库
    const args = require('minimist')(process.argv.slice(2))
    args['name'] //joe
    

async和await

async异步执行函数,该函数的执行不会阻塞后面代码的执行

await等待,用于等待一个promise对象,只能在异步async中使用,否则会报错

返回一个promise对象处理完成的结果

await表达式会暂停当前 async function的执行,等待Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function,若 Promise 处理异常(rejected)await 表达式会把 Promise 的异常原因抛出。如果 await 操作符后的表达式的值不是一个 Promise,那么该值将被转换为一个已正常处理的 Promise

const axios = require('axios');
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}


// Promise方式
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

// async await方式
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}
//doIt();

commonjs模块规范

1.模块引用

require('math');

引入一个模块api到当前上下文中

2.模块定义

exports 导出当前模块中的方法,变量

module对象,代表模块自身,exports是module的属性,

module.exports 会覆盖exports导出的值,对象

3.模块标识

传递给require方法的参数,符合小驼峰命名的字符串,或者以.、..开头的相对路径,或者绝对路径,可以没有文件后缀.js

image-20220421112912825

Node的模块实现

在node引入模块,需要经历的3个步骤

1.路径分析

2.文件定位

3.编译执行

在Node中,模块文件分为两类,Node提供的模块,称为核心模块,用户编写的成为文件模块

  • 核心模块在Node源代码的编译过程中,编译未二进制文件。在文件启动时,部分核心模块被加载进内存中,所以这部分模块引入时,文件定位和编译执行可以省略掉,并且在路径分析中优先判断,所以它的加载速度时最快的
  • 文件模块则时在运行时动态执行加载,需要完整路径分析,文件定位,编译执行过程,速度比核心模块慢

1.优先从缓存加载

Node会对引入过的模块进行缓存,以减少二次引入时的开销,缓存的时编译和执行过后的对象

不论时核心模块还是文件模块,require方法对相同模块的二次加载一律采用缓存优先的方式,这是第一优先级的,不同的是核心模块的缓存检查优于文件模块的缓存检查

2.路径分析和文件定位

不同的标识符在,模块查找,和定位上有不同程度的差异

1.模块标识符分析

require方法接受一个标识符作为参数,在Node实现中,正式基于这样一个标识符进行模块查找的。模块标识符在node中主要分为以下几类:

  • 核心模块,如http,fs,path等
  • .或.. 开始的相对路径文件和/开头的绝对路径文件模块
  • 非路径形式的文件模块,如自定义的connect模块
  • 核心模块

    • 核心模块加载速度仅次于缓存加载,它在Node的源代码编译过程中已经被编译未二进制文件,其加载速度是最快的。
    • 用户自定义标识符不能跟核心模块标识符相同,加载失败,如果需要加载,必须选择一个不同的标识符或者使用不同路径的方式
  • 路径形式的文件模块

    • 相对路径和绝对路径开始的标识符,会被当作文件模块来处理,在分析模块时,require方法会将路径转换成真实路径,以真实路径为索引,将编译执行后的结果存放到缓存中,以使二次加载时更快。
    • 由于文件模块给Node指定了文件位置,所以查找过程中节省了大量时间,其加载时间满于核心模块
  • 自定义模块

    • 非核心模块,也不是以路径形式的标识符。它是一种特殊的文件模块,可能是一个文件或者包的形式。这类模块查找是最耗时的,也是所以方式中最慢的一种

    • 模块路径

      • 是Node在定位文件模块的具体文件时制定的查找策略。具体表现为一个路径组成的数组
      • 当前文件目录下的node_modules目录
      • 父目录下的node_modules目录
      • 父目录的父目录下的node_modules目录

      image-20220421145128790

    • 在加载过程中,node会逐个尝试模块中的路径,直到找到目标文件为止。文件路径越深,加载耗时越多,这个就是自定义模块加载速度慢的原因

2.文件定位

文件扩展名分析,目录和包的处理

  • 文件扩展名分析

require在分析标识符的过程中,会出现标识符不包含文件扩展名的情况,CommonJS模块规范也允许在标识符中不包含文件的扩展名,这种情况下Node会按js,json,node的次序补足扩展名,依次尝试。

事件循环,调用栈

使用call apply bind 重新定义 this

var name = '小王', age = 17;
var obj = {
    name: '小张',
    objAge: this.age, // this 指向windows
    myFun: function () {
        console.log(this.name + "年龄" + this.age) // this 指向obj
    }
}

var db = {
    name: "EZ",
    age: "999"
}

obj.myFun.call(db)
obj.myFun.apply(db)
obj.myFun.bind(db)() // 返回一个函数



// 传参
var name = '小王', age = 17;
var obj = {
    name: '小张',
    objAge: this.age, // this 指向windows
    myFun: function (f, t) {
        this.delete()
        console.log(this.name + "年龄" + this.age, "f = " + f, "t = " + t) // this 指向obj
    }
}

var db = {
    name: "EZ",
    age: "999",
    delete:function(){
        this.name = 'EZ1'
    }
}

obj.myFun.call(db, '北京', '武汉')      // EZ年龄999 f = 北京 t = 武汉
obj.myFun.apply(db, ['上海', '武汉'])   // EZ年龄999 f = 上海 t = 武汉
obj.myFun.bind(db, '北京1', '武汉1')() // 返回一个函数  EZ年龄999 f = 北京1 t = 武汉1
obj.myFun.bind(db, ['上海', '武汉'])() // 返回一个函数  EZ年龄999 f = 上海,武汉 t = undefined

// call bind 参数用逗号作为分隔符 传参
// apply    参数使用数组 
// bind 除了返回函数以为,参数和call一样

KOA

接受参数

get

ctx.request.query 获取request对象传入的值(get参数)

ctx.request.params 获取动态路径地址(url参数)

post

ctx.request.body 获取POST提交的参数

JOI

对前段传来的数据做 valication(合法性校验)

用法

Joi.validate(value, schema, [options]);

例:

router.post('/create', function (req, res, next) {

  const schema = Joi.object().keys({
    name: Joi.string().min(2).max(20).required(),
    age: Joi.number().min(0).max(100).required(),
    sex: Joi.string().valid(['男', '女']),
  })

  const result = Joi.validate({ name: '小明', age: 12, sex: "男" }, schema);

  res.send(result);

});

返回:

{
    "error": null,
    "value": {
        "name": "小明",
        "age": 12,
        "sex": "男"
    }
}

result.error === null 为true后,result.value拿值

验证规则

基本验证规则:类型 / 长度范围 / 取值范围 / 是否必填 / 与其它字段的关系 / 默认值

类型

//任意类型 any()

//指定类型 array() boolean() binary() date() func() number() object() string()

类型下面还有子约束

//Requires the number to be an integer (no floating point).
Joi.number().integer(), 必须是整数

//Requires the string value to only contain a-z, A-Z, and 0-9.
Joi.string().alphanum()

Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),

Joi.string().email()

长度范围

Joi.number().min(2) Joi.array().max(5)

取值范围

(1) valid - 白名单

可以用来实现枚举类型

a: Joi.any().valid('a'), b: Joi.any().valid('b', 'B'), c: Joi.any().valid(['c', 'C'])

(2) invalid - 黑名单

a: Joi.any().invalid('a'), b: Joi.any().invalid('b', 'B'), c: Joi.any().invalid(['c', 'C'])

(3) allow - 白名单的补充

a: Joi.any().allow('a'), b: Joi.any().allow('b', 'B'), c: Joi.any().allow(['c', 'C'])

是否必填

只对 undefined有效,null 会认为不合法

Joi.any().required()

  const result = Joi.validate(undefined, Joi.string().required());

与其它字段的关系

(1) with / without / or

如现在有 a、b 两个字段:

Copy const schema = Joi.object().keys({
    a: Joi.any(),
    b: Joi.any()
}).with('a', 'b');

a.with('a', 'b') //a 和 b 必须都要填写

b.without('a', 'b'); //a 和 b 只能填写其中一个

c.or('a', 'b') //b 和 b 至少填写一个

(2) when

需求:验证条件是男人必须 50-100 岁,女人必须 0-50岁

Copy  const schema = Joi.object().keys({
    name: Joi.string().min(2).max(20).required(),
    age: Joi.number().min(0).max(100).required().when('sex', {
      is: '男',
      then: Joi.number().min(50).max(100),
      otherwise: Joi.number().min(0).max(50),
    }),
    sex: Joi.string().valid(['男', '女']),
  })

  const result = Joi.validate({ name: '小明', age: 60, sex: "女" }, schema);

return:

Copy{
    "error": {
        "isJoi": true,
        "name": "ValidationError",
        "details": [
            {
                "message": "\"age\" must be less than or equal to 50",
                "path": [
                    "age"
                ],
                "type": "number.max",
                "context": {
                    "limit": 50,
                    "value": 60,
                    "key": "age",
                    "label": "age"
                }
            }
        ],
        "_object": {
            "name": "小明",
            "age": 60,
            "sex": "女"
        }
    },
    "value": {
        "name": "小明",
        "age": 60,
        "sex": "女"
    }
}

默认值

default()

  const result = Joi.validate(undefined, Joi.string().default("空"));

return:

{
    "error": null,
    "value": "空"
}

某个字段添加多个约束

验证条件为即可是string值也可是number值:

  const result = Joi.validate(23, [Joi.string(), Joi.number()]);

对多余传进来的变量不要理会

options参数加上

 const schema = Joi.object().keys({
    name: Joi.string().min(2).max(20),
    age: Joi.number().min(0).max(100).required(),
    sex: Joi.string().valid(['男', '女']),
  })

const result = Joi.validate({ name: '小明', age: 12, sex: "男", hometown: "上海" }, schema, { allowUnknown: true });

return:

{
    "error": null,
    "value": {
        "name": "小明",
        "age": 12,
        "sex": "男",
        "hometown": "上海"
    }
}

value 里会保留多传的 hometown 字段

基本使用案例

常用方法

let paramSchema = Joi.object().keys({ 
    // 3 - 30 个 数字、字符 
    username: Joi.string().alphanum().min(3).max(30).required(), 

    // 3 - 30 位 字母数字组合密码 
    password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/), 

    // string || number 都可以通过 
    access_token: [Joi.string(), Joi.number()], 

    // 生日限制 
    birthyear: Joi.number().integer().min(1900).max(2018), 

    // email 限制 
    email: Joi.string().email(), 

    // URI限制 
    website: Joi.string().uri({ scheme: [ 'git', /git+https?/ ] }), 

    // ==== 允许为空/ 否认不允许为空 ==== 
    search: Joi.string().allow(''), 

    // 验证枚举值,如果不传,默认为all 
    type: Joi.string().valid('disabled', 'normal', 'all').default('all'), 

    // 开始时间 会自动格式化 
    startTime: Joi.date().min('1-1-1974').max('now'), 

    // 结束时间 必须大于开始时间,小于2100-1-1 
    endTime: Joi.when(Joi.ref('startTime'), { is: Joi.date().required(), then: Joi.date().max('1-1-2100') }), 
    
    // 页码 限制最小值 
    page: Joi.number().integer().min(1).default(1), pageSize: Joi.number().integer().default(8), 
    // deleteWhenLtTen: Joi.number().integer().max(10).strip(), 
   
    // 数组中包含某个字段 && 数字 
    arrayString: Joi.array().items( 
        // 数组中必须包含 name1 
        Joi.string().label('name1').required(), 
        // 数组中必须包含 数字 
        Joi.number().required(), 
        // 除掉【以上类型的以外字段】---数组中可以包含其他类型,如bool
        Joi.any().strip() 
    ), 

    // 数组对象, 如需其参考以上字段 
    arrayObject: Joi.array().items( 
        Joi.object().keys({ 
            age: Joi.number().integer().max(200), 
            sex: Joi.boolean() 
        }) 
    ) 

    with('isA', 'AVal') //意思是,isA 和 AVal 这两字段如果填写了isA,也必须要填写AVal
    with('isB', 'BVal') //道理同上
    without('isA', 'isB'); //意思是 isA 和 isB 只能填写其中一个    
    or('isA', 'isB') //意思是 isA 和 isB 这两字段至少填写其一
    // 测试数据
    const testData = { Password: "12345678"}
    // 验证
    let value = Joi.validate(testData, paramSchema, { allowUnknown: true, abortEarly: true });
    console.log(value);
    if (value.error) { throw error; }