Node-基础知识

2020/7/19 Node
// 前端和Server端开发的区别
1. 服务稳定性
      server端可能会遭受各种恶意攻击和误操作
      单个客户端可以意外挂掉,但是服务端不能
      pm2进程守护
2. 考虑CPU和内存 // 优化、扩展
      客户端独占一个浏览器,内存和CPU都不是问题
      server端要承载很多请求,CPU和内存都是稀缺资源
      stream写日志,使用redis存session
3. 日志记录
      前端也会参与写日志,但只是日志的发起方,不关心后续
      server端要记录日志、存储日志、分析日志,前端不关心
4. 安全
      server端要随时准备接收各种恶意攻击,前端则少很多
      : 越权操作,数据库攻击等
      语法xss攻击和sql注入
5. 集群和服务拆分
      产品发展速度快,流量可能会迅速增加
      如何通过扩展机器和服务拆分来承载大流量
----------------------------------------------------------------------------------------------
// 什么是nodejs
1. nodejs不是一门语言,不是库,不是框架,是一个js运行时环境,可以解析和执行js代码
2. nodejs是javascript的运行时,构建在chrome的v8引擎,nodejs用了事件驱动,非阻塞I/O模型
3. 阻塞: I/O时进程休眠等待I/O完成后进行下一步
4. 非阻塞: I/O时函数立即返回,进程不等待I/O完成
5. I/O操作很慢,是操作系统底层,读写硬盘数据
6. 事件驱动: I/O等异步操作结束后的通知,观察者模式
7. cpu密集: 计算、逻辑判断、压缩、解压、加密、解密
8. I/O密集: 存储设备、网络设施的读取、文件操作、数据库
9. 进程: 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,简单理解是一个运行的程序
10. 多进程: 启动多个进程,多个进程可以一块执行多个任务,原理是快速切换
11. 线程: 进程内一个相对独立的、可调度的执行单元,与同属一个进程的线程共享进程的资源
12. 多线程: 启动一个进程,在一个进程内启动多个线程,这样多个线程也可以一块执行多个任务
13. nodejs的单线程: 单线程只是针对主进程,I/O操作系统底层是多线程调度
14. 单线程不是单进程
15. 浏览器是多进程的
16. Node 是一个基于 V8 引擎的 Javascript 运行环境,它使得 Javascript 可以运行在服务端,直接与操作系统进行交互,与文件控制、网络交互、进程控制等
    Chrome 浏览器同样是集成了 V8 引擎的 Javascript 运行环境,与 Node 不同的是他们向 Javascript 注入的内容不同,Chrome 向 Javascript 注入了 window 对象,Node 注入的是 global,这使得两者应用场景完全不同,Chrome 的 Javascript 所有指令都需要通过 Chrome 浏览器作为中介实现
17. 浏览器中的 WebW orker 开启多进程只是把 js 放到这个多开的进程中的线程中去执行,然后再和原来的进程进行通信交互
18. 在 Node 中开启多进程
    1. child_process.fork 开启子进程
        // compute.js
        function getSum() {
            let sum = 0
            for (let i = 0; i < 1000; i++) {
                sum +=i
            }
            return sum
        }
        process.on('message', data => {
            console.log('子进程 id', process.pid)
            console.log('子进程接收到的信息', data)
            const sum = getSum()
            process.send(sum) // 发送消息给主进程
        })
        // http.js
        const http = require('http')
        const fork = require('child_process').fork
        const server = http.createServer((req, res) => {
            if (req.url === 'get-sum') {
                console.log('主进程 id', process.pid)
                // 开启子进程
                const computeProcess = fork('./compute.js')
                computeProcess.send('开始计算') // 和子进程交互,告诉子进程开始工作
                computeProcess.on('message', data => {
                    console.log('主进程接收到的信息', data)
                    res.end('sum is' + data)
                })
                computeProcess.on('close', () => {
                    console.log('子进程因报错而退出')
                    computeProcess.kill()
                    res.end('error')
                })
            }
        })
    2. cluster.fork 开启集群 // 类似 pm2
        const http = require('http')
        const cpuCoreLength = require('os').cpus().length
        const cluster = require('cluster')
        // 当前是否是主进程
        if (cluster.isMaster) {
            for (let i = 0; i < cpuCoreLength; i++) {
                cluster.fork() // 开启子进程
            }
            cluster.on('exit', worker => {
                console.log('子进程退出')
                cluster.fork() // 进程守护
            })
        } else {
            // 多个子进程会共享一个 TCP 连接,提供一份网络服务
            const server = http.createServer((req, res) => {
                res.writeHead(200)
                res.end('done')
            })
            server.listen(3000)
        }
----------------------------------------------------------------------------------------------
// global变量,使用时不需要require
1. commonjs
2. process // 进程
   const { argr, argr0, execArgr, execPath, env } = process // 进程
   argr是一个数组,2个是固定的,1是启动时使用的命令路径,2是当前执行文件的路径
   argr0是argr第一个值的引用
   execArgr可以拿到写在文件名之前的特殊参数 // node 参数 文件名
   execPath调用脚本的路径,相当于argr的第一个值
   env是各种配置参数
   process.cmd() // 返回process执行的路径
3. timer // setImmediate()、setTimeout()、nextTick()
    setImmediate(() => { code }) // 把当前函数放到下一个队列的队首
    process.nextTick(() => { code }) // 把当前函数放到当前队列的最后一个
    process.nextTick < setTimeout < setImmediate // process.nextTick最快,setImmediate最慢,一般用setImmediate
4. Buffer // 二进制
----------------------------------------------------------------------------------------------
// CommonJs
1. 每个文件是一个模块,有自己的作用域
2. 在模块内部module变量代表模块本身
3. module.export属性代表模块对外接口
// require规则
1. /表示绝对路径
2. ./表示相对路径
3. 支持js、json、node扩展名,不写则依次尝试
4. 不写路径直接写名字引入则认为是自带的模块或者各级node_modules内的第三方模块
// require特性
1. module被加载引用时会执行,加载后缓存,只执行一次
2. 一旦出现某个模块被循环加载,就只输出已经执行的部分,还未执行的部分不会输出
3. exports = module.exports,不能改变exports的指向
4. exports = { a: 1, b: 2 } // 错误
5. module.exports = { a: 1, b: 2 } // 正确
6. module.exports.fn1 = s1
   const mod = require('路径')
   mod.fn1
7. module.exports = {
      s1,
      s2
   }
   const { s1, s2 } = require('路径')
----------------------------------------------------------------------------------------------
// debug
1. node --inspect-brk 文件名
   在chrome中安装插件
   在url中输入'chrome://inspect'
2. 在vscode中打断点,可以打条件断点
----------------------------------------------------------------------------------------------
// 基础API——Path路径,操作系统不同,方法不同
1. const path = require('path')
2. path.normalize('路径') // 自动修复路径的错误
3. path.join('路径1', '路径2', ...) // 拼接路径,包含path.normalize()
4. path.resolve('相对路径') // 把相对路径解析成绝对路径
5. path.basename('路径') // 返回文件名
6. path.dirname('路径') // 返回所在的绝对路径
7. path.extname('路径') // 返回扩展名
8. path.parse('路径') // 把一个路径拆分成一个对象包括5、6、7、root、name
9. path.format('对象') // 把一个对象解析成一个路径,参数是一个对象
10. path.sep // 返回路径的分隔符,'/'
11. path.delimiter // 返回path的分隔符,':'
12. path.win32 // 操作系统,可以通过这2个属性调用与自己操作系统不同的方法和属性
13. path.posix // 操作系统,可以通过这2个属性调用与自己操作系统不同的方法和属性
14. __dirname、__filename总是返回文件的绝对路径 // require时自带的
15. process.cmd() // 总是返回执行node命令所在的文件夹路径
16. 在require方法中使用'./'相对的是当前文件所在的文件夹,而在其他地方和process.cmd()一样,相对的是node启动的文件夹
----------------------------------------------------------------------------------------------
// 基础API——Buffer,处理二进制数据流,是global对象,不需要require
1. Buffer用于处理二进制数据流,默认为十六进制表示,(0-255)的数字
2. 实例类似整数数组,大小固定不可改变
3. 使用的堆内存是c++代码,在v8堆外分配物理内存
// Buffer实例化
1. Buffer.alloc(10) // 创建长度为10的Buffer,默认用0填充
2. Buffer.alloc(10, 1) // 创建长度为10的Buffer,用1填充(十六进制)
3. Buffer.allocUnsafe() // 不安全的Buffer,可能包含旧数据
4. Buffer.from([1, 2, 3]) // 创建长度为3的Buffer
5. Buffer.from('text', 'base64') // 第二个参数不传,默认是utf-8
// 静态方法
1. Buffer.bytelength('测试') // 返回字节的长度,一个中文对应三个字节,返回6,英文是一个对应一个
2. Buffer.isBuffer({}) // 判断是否是Buffer对象, 返回布尔值
3. Buffer.concat([b1, b2]) // 拼接Buffer
// 实例方法
1. buf.length // 返回对应的字节数,和内容无关
2. buf.toStirng() // 可传入base64,默认是utf-8
3. buf.fill(10, 2, 6) // 从下标2开始到6填充10
4. buf.equals(buf) // 判断buf内容是否一致,返回布尔值
5. buf.indexOf() // 与js数组一样
6. buf.copy(b, 0, i) // copy到b,从0开始,i结束
// 处理中文乱码
1. const stringDecoder = require('string-decoder').stringDecoder
   const decoder = new stringDecoder('utf-8')
   decoder.write('字符')
----------------------------------------------------------------------------------------------
// 基础API——event,事件对象,继承了EventEmitter类(events)
1. const EventEmitter = require('events') // 引入
   class CustomEvent extend EventEmitter { code } // 继承
   const ce = new CustomEvent() // 实例化
   ce.on('test', () => { code }) // 绑定,也可用once绑定,只触发一次
   ce.emit('test', '参数') // 触发,后面可以用逗号隔开传参,个数不限
   ce.removeListener('test1', 'test2', () => { code }) // 移除,后面可以用逗号隔开移除多个事件
   ce.removeAllListeners() // 移除全部
----------------------------------------------------------------------------------------------
// 基础API——fs,文件有关,有回调函数,第一个参数保留给异常,当err是null或undefined时则是成功,可操作二进制数据
1. 所有的fs的API都有同步方法,在方法名后加Sync // 同步: fs.readFileSync(),异步: fs.readFile()
2. const fs = require('fs')
3. fs.readFile('文件路径', 'utf-8', (err, data) => { code }) // 读文件,默认是Buffer对象,可用toString()转成字符串,也可以在文件路径后面传第二个参数'utf-8'
4. fs.writeFile('文件路径', '写入内容', { encoding: 'utf-8' }, err => { code }) // 写文件, 可追加可覆盖需在第三参数传flags, 'a'表追加, 'w'表覆盖
5. fs.stat('文件路径', (err, stat) => { stat对象 }) // 文件信息
    stat.isFile() // 判断是否是文件
    stat.isDirectory() // 判断是否是文件夹
6. fs.rename('文件路径', '新名字', err => { code }) // 重命名
7. fs.unlink('文件路径', err => { code }) // 删除
8. fs.readdir('文件夹路径', (err, files) => { code }) // 读文件夹
9. fs.mkdir('新建的文件名', err => { code }) // 创建文件夹
10. fs.rmdir('文件夹路径', err => { code }) // 删除文件夹
11. fs.watch('监听的路径', { recursive: true }, (event, filename) => { code }) // 监听, recursive是递归监听,event是发生了什么变化,filename发生变化的文件名
12. const rs = fs.createReadStream('路径') // 读流,有方向的数据,读一点给一点,而不是全部读完再一起给
    rs.pipe(process.stdout) // 输出到控制台,pipe是输出,process.stdout是控制台
13. const ws = fs.createWriteStream('路径') // 写流,有方向的数据,写一点给一点,而不是全部写完再一起给
    ws.write('流') // 写
    rs.pipe(ws) // 把读到的数据用流的方式传递到写的地方, 可用于复制文件
    rs.on('data', chunk => {}) // 每次写的时候都会触发data事件
    rs.on('end', () => {}) // 当写完会触发end事件
    在http处理中也可以直接使用 rs.pipe(res) 即执行了 res.end('数据流')
14. process.stdin.pipe(process.stdout) // 标准流的输入输出, 可在node环境测试
----------------------------------------------------------------------------------------------
// promisify把异步函数转成promise解决回调地狱
1. const promisify = require('util').promisify
   const read = promisify(require('fs').readFile)
   read('路径').then(data => { code }).catch(err => { code })
2. 也可以通过asyncawait方式,在同步中try/catch相当于异步中的.catch
----------------------------------------------------------------------------------------------
// node连接mysql,属于硬盘数据库
1. npm install mysql // 安装
2. const mysql = require('mysql') // 引入
3. const con = mysql.createConnection({ // 创建连接对象
      host: 'localhost',
      user: 'root',
      password: '123456',
      port: '3306',
      database: '库名'
   })
4. con.connect() // 开始连接
5. con.query(sql语句, (err, data) => { code }) // 执行sql语句
6. con.end() // 关闭连接
7. 如果是插入或更新语句,会返回一个obj对象,其中可以判断affectedRows影响了几行、insertId插入的id,从而知道是否执行成功
----------------------------------------------------------------------------------------------
// node连接redis,属于内存数据库
1. 在命令行执行 redis-server 启动redis
2. npm install redis
3. const redis = require('redis')
4. const c = redis.create(lient(3006, '127.0.0.1'))
5. c.on('error', err => { code })
6. c.set('name', 'cj', redis.print) // 插入name=cj成功后在控制台打印ok
7. c.get('name', (err, data) => { code }) // 获取name
8. c.quit() // 退出
9. 把session存储在redis中就可以避免session过大会把node服务进程内存挤爆, 而且在做集群时,启用多个node服务即多个进程,无法共享数据
----------------------------------------------------------------------------------------------
// node 连接 mongodb
const MongoClient = require('mongodb').MongoClient // npm install mongodb
const url = 'mongodb://localhost:27017' // 地址
const dbName = 'myblog' // 库名

MongoClient.connect(
    url,
    {
        useUnifiedTopology: true
    },
    (err, client) => {
        if (err) {
            console.error('mongodb connect error', err)
            return
        }

        // 没有报错,说明连接成功
        console.log('mongodb connect success')

        // 切换到数据库(控制台 `use myblog`)
        const db = client.db(dbName)

        // 使用集合
        const usersCollection = db.collection('users') // users 即集合名称

        // 新增
        usersCollection.insertOne({
            username: 'chenj',
            password: 'abc',
            realname: '一条牛'
        }, (err, result) => {
            if (err) {
                console.error('users insert error', err)
                return
            }
            console.log(result)

            // 关闭连接
            client.close()
        })

        // 修改
        usersCollection.updateOne(
            { username: 'zhangsan' }, // 查询条件
            { $set: { realname: '张三A' } }, // 修改的内容,注意有 $set
            (err, result) => {
                if (err) {
                    console.error('users update error', err)
                    return
                }
                console.log(result)

                // 关闭连接
                client.close()
            }
        )

        // 删除
        usersCollection.deleteOne(
            { a: 101 },
            (err, result) => {
                if (err) {
                    console.error('users delete error', err)
                    return
                }
                console.log(result)

                // 关闭连接
                client.close()
            }
        )

        // 查询
        usersCollection.find({
            username: 'zhangsan',
            password: '123'
        }).toArray((err, result) => {
            if (err) {
                console.error('users find error', err)
                return
            }
            console.log(result)

            // 关闭连接
            client.close()
        })
    }
)
----------------------------------------------------------------------------------------------
// node 连接 mongoose
1. Schema 定义数据格式的规范
2. Model 规范 Collection
3. 规范数据操作的 API
4. mongoose 连接 mongodb
// db.js
const mongoose = require('mongoose')
const url = 'mongodb://localhost:27017'
const dbName = 'myblog'

mongoose.set('useFindAndModify', false)

mongoose.connect(`${url}/${dbName}`, {
    useNewUrlParser: true,
    useUnifiedTopology: true
})

const db = mongoose.connection

// 发生错误
db.on('error', err => {
    console.error(err)
})

// 连接成功
db.once('open', () => {
    console.log('mongoose connect success…')
})

module.exports = mongoose

5. model 和 Schema
// blog.js 即 connection
// 对应 blog 集合
const mongoose = require('../db')

// 规范
const BlogSchema = mongoose.Schema({
    title: {
        type: String,
        required: true // 必需
    },
    content: String,
    author: {
        type: String,
        required: true
    }
}, { timestamps: true }) // 会自动加时间戳

const Blog = mongoose.model('blog', BlogSchema) // blog 对应 blogs 集合

module.exports = Blog

6. mongoose 操作 mongodb
const Blog = require('../models/Blog')

!(async () => {

    // 新建博客
    const blog1 = await Blog.create({
        title: '标题3',
        content: '内容3',
        author: 'shuangyue'
    })
    console.log(blog1)

    // 获取列表
    const list = await Blog.find({
        // author: 'zhangsan'
        title: /A/ // 正则表达式,模糊查询
    }).sort({ _id: -1 })
    console.log(list)

    // 根据 id 获取单个博客
    const blog3 = await Blog.findById('5f4cc1824e9b73583b69b404')
    console.log(blog3)

    // 修改博客
    const res = await Blog.findOneAndUpdate(
        { _id: '5f4cc1824e9b73583b69b404' }, // 条件
        { content: '内容3内容3内容3' },
        {
            new: true // 返回修改之后的最新的内容,默认为 false
        }
    )
    console.log(res)

    // 删除
    const res = await Blog.findOneAndDelete({
        _id: '5f4cc1824e9b73583b69b404',
        author: 'shuangyue' // 验证一下作者,增加安全性,防止误删
    })
    console.log(res)
})()
----------------------------------------------------------------------------------------------
// 登录流程
1. 当用户登录成功时, 把用户信息如用户名存储到cookie中, 后端在判断是否登录, 可以直接通过cookie中的值判断
2. 如果直接把用户名存储在cookie中, 会不安全, 所以用到session
3. session原理是把每个请求的客户端都生成一个对应的id如userId, 然后通过Set-Cookie把userId存储到客户端的cookie中
4. 生成的id是可以加密的, 然后再把用户信息等内容存储到id对应的变量中去, 实现绑定, 原理和cookie中直接存储用户信息差不多
5. 把session存储到redis中, 只是把上面4中的那些原本在变量中的数据存储到redis数据库中而已
6. 在不使用connect-redis这个库时, 存储到redis中需要自己set进去, 而用了就只要配置文件就可以
7. 在所有接口调用前, 可以加一个中间件验证是否有登录或权限等
----------------------------------------------------------------------------------------------
// nginx和日志
1. 通过nginx反向代理可以让不同端口的数据从同一个端口中返回
2. 日志记录访问服务器的各种东西 // accesslog访问日志、eventlog自义定日志、errorlog错误日志
3. 通过linux的crontab命令即定时任务执行sh脚本拆分日志文件,日志前可加上时间节点用于拆分
   可设置时间每天或每周等把日志文件 'access.log' 拷贝并重命名为 '2020-09-17.access.log'
   然后清空'access.log'文件,继续积累日志
   crontab -e // 编辑crontab文件任务
   crontab -l // 查看有什么定时任务
4. 日志是按行存储的,一行就是一条日志,使用readline逐行读取文件,分析日志内容 // readline基于stream
    const readline = require('readline')
    const fs = require('fs')
    const path = require('path')
    const fileName = path.join(__dirname, access.log)
    const readStream = fs.createReadStream(fileName) // 创建读流
    // 创建 readline 对象
    const rl = readline.createInterface({
      input: readStream // 读取这个文件
    })
    rl.on('line', () => {}) // 逐行读取触发
    rl.on('close', () => {}) // 读取完成触发
----------------------------------------------------------------------------------------------
// 安全
1. sql注入: 通过在表单输入sql代码给服务器执行 // 在username中输入 chenj -- 就能实现免密登录
   解决方法: 使用mysql的escape函数处理输入内容 // 在拼接 sql 时,变量使用 escape 包裹执行
   原理: 把输入的内容转义,在sql中'-- '是注释后面代码的意思,应用在所有能拼接sql语句的地方
2. xss攻击: 在页面展示内容掺杂js代码
   解决方法: 把左右的尖括号转义或使用npm install xss,是一个函数
3. 密码加密: node自带加密库,crypto
    const crypto = require('crypto')
    // 密钥
    const SECRET_KEY = 'chenj0922'
    // md5加密
    function md5(content) {
      let md5 = crypto.createHash('md5')
      return md5.update(content).digest('hex') // 16进制
    }
    // 加密函数
    function genPassword(password) {
      const str = `password=${password}&key=${SECRET_KEY}`
      return md5(str)
    }
    module.expotrs ={
      genPassword
    }
----------------------------------------------------------------------------------------------
// 自动重启node,相当于webpack的devServer
1. npm install supervisor
   supervisor 文件名
2. 当文件发生变化时,不用重新运行命令,相当于监听
----------------------------------------------------------------------------------------------
// 版本号x.y.z,如0.0.1
1. x: 不保证兼容,全新, 1.0.0,x为偶数时是稳定版,x为积数时是不稳定版
2. y: 有新增的功能同时兼容以前的功能,0.1.0
3. z: 有bug修改了, 0.0.2
4. 1.2.*表示z位最新不固定,~1.2.0一样
5. 2.x表示y和z位最新不固定,^2.0.0一样
6. alpha:内部测试版本,除非是内部测试人员,否则不推荐使用,有很多 bug
7. beta:公测版本,消除了严重错误,还是会有缺陷,这个阶段还会持续加入新的功能
8. rc:Release Candidate,发行候选版本,这个版本不会加入新的功能,主要是排错,修改 bug
9. release:正式版本
----------------------------------------------------------------------------------------------
// ESlint
1. env: 脚本的运行环境
2. globals: 额外的全局变量
3. rules: 启用的规则
4. off === 0 // 关闭
5. warn === 1 // 开启,使用警告级别的错误,不会让程序退出
6. error === 2 // 开启,使用错误级别的错误,程序会退出
7. 在官网中的规则前面有√符号的表示推荐使用,前面有小扳手的表示可以通过命令行进行修复 // eslint --fix .
8. /* eslint-disable */ code /* eslint-enable */ // 中间的代码不受全部规则验证
9. /* eslint-disable no-alert, no-console */ code /* eslint-enable no-alert, no-console */ // 中间的代码不受no-alert, no-console验证
10. code // eslint-disable-line 这行不受全部规则验证
11. // eslint-disable-next-line 下一行不受全部规则验证
12. eslint --init // 创建一个配置规则
13. 可以通过npm install pre-commit这个包让代码如果有错误时,不让上传到git中去
----------------------------------------------------------------------------------------------
// npm参数
1. --save-dev === -D // 安装到devDependencies中(开发环境)
2. --save === -S === 不写 // 安装到dependencies中(生产环境)
3. -g // 全局安装
4. 全局安装的npm不会出现在package.json中,可以直接使用命令方式调用,如果不是全局安装则要用npx启动,或者在package.json的script中配置命令才能使用npm run的方式
5. 没有init时不加-S-D时会安装到项目目录下,但不会写进package.json中
----------------------------------------------------------------------------------------------
// npm init生成的文件目录
1. README.md // 项目介绍
2. .gitignore // 不上传到git仓库的文件
    匹配模式前加'/'表示项目根目录
    匹配模式最后加'/'表示是目录,而不是文件
    匹配模式前加'!'表示取反
    '*'表示任意个字符
    '?'匹配任意一个字符
    '**'匹配多级目录
3. .npmignore // 不上传到npm仓库的文件,如果没有这个文件,会自动匹配.gitignore里面的配置
4. .editorConfig // 约定代码风格
5. .eslintrc.js // eslint代码校验
----------------------------------------------------------------------------------------------
// 其他
1. node项目入口一般在'/bin/www.js'文件中
2. 在开发环境中可以使用 nodemon 包来自动重启node项目,否则每次修改都需要重启
3. 可以使用 corss-env 包来在package.json的script中获取env
    "dev": "corss-env NODE_ENV=dev nodemon ./bin/www.js"
4. 在项目架构时需要合理的拆分,要把路由(router)分成一个文件,把成功失败做一层封装(model)
   在router中返回的数据也需要一个文件(controller)
   也就是说项目的架构可以分为:
    'bin/www.js' -> 'app.js' -> 'router' -> 'controller' -> 'model'
5. 路由和API的关系
    API是前端和后端或不同端之间对接的术语,包括url路由、req输入、res输出
    路由是API的一部分
6. 后端可以通过设置cookie
    res.setHeader('Set-Cookie', 'username=cj; path=/; httpOnly; expires=过期时间必须是UTC格式')
7. require 加载资源类型
    1. .js ==> module.exports/exports
    2. .json ==> JSON.parse
    3. any ==> 当做 .js 文件处理
8. 可以通过 nvm ls-remote 查看远程的 node 版本
----------------------------------------------------------------------------------------------
// 常用的 npm 命令
1. 清除缓存
  1. npm cache clean --force
  2. yarn cache clean
2. 快速删除 node_modules 文件夹
  1. npm install rimraf -g
  2. rimraf node_modules
3. 查看所有全局安装的 npm 模块
  1. npm list -g --depth=0

// nvm 配置
1. 更换下载源,打开安装目录下的 settings.txt 文件进入编辑,新增以下配置
    1. `node_mirror: https://npmmirror.com/mirrors/node/`
    2. `npm_mirror: https://npmmirror.com/mirrors/npm/`
    3. 注意不能使用 `https://npm.taobao.org` 相关的域名,此域名已经切换至 `https://npmmirror.com`
2. 查看远程 node 版本命令:nvm ls available
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
Last Updated: 2024/6/11 14:20:38
    飘向北方
    那吾克热-NW,尤长靖