Node.js

Node.js 入门
Node.js 概述
  1. Node.js是什么
    是一个执行js代码的工具,工具是指可以安装在计算机操作系统上的软件
  2. 为什么浏览器和Node.js 都可以运行js代码
    因为浏览器和Node.js都内置了JavaScrip V8 引擎,它可以将js代码编译为计算机能够识别的机器码
  3. 浏览器中运行的 js 和 Node.js 中运行的 js 有区别吗
    浏览器中运行的 js 只能控制浏览器窗口和DOM
    Node.js 中运行的 js 只能控制操作系统级别的内容
  4. Node.js 可以做什么
    通常用来构建服务端应用和创建前端工程化工具
    js 运行在浏览器中,我们就叫它客户端 js
    js 运行在Node.js 中,我们就叫它服务端 js
系统环境变量

系统环境变量是指定义在操作系统上的变量,变量存储了系统运行时所需要的参数
比如在使用 webpack 构建前端应用时就使用到了系统环境变量,因为webpack 需要根据系统环境变量判断当前为开发环境还是生产环境(该变量为NODE_ENV ,值为 production 或者 development,webpack 在运行时通过 process.env.NODE_ENV 获取变量的值,从而知道当前代码的运行环境)

体验Node.js
function sayHello(name) {
  console.log("Hello" + " "+ name + "!")
}
sayHello("Node")
zhangmeng@zhangmengdeMacBook-Pro 1-basics % node index.js
Hello Node!
全局对象
  • Node.js 环境中全局对象为 global,在global 中存在一些和 window 对象中名字相同且作用相同的方法(global.console.log、global.setTimeout)
模块系统
模块概述

一个js文件就是一个模块,在模块文件中定义的变量和函数默认只能在模块文件中使用,如果需要在其他模块文件中使用,必须显示将其进行导出

模块成员导出

每一个模块文件中都存在一个模块对象,即 module ,该对象保存了当前模块相关信息
在模块对象中有一个属性 exports,该属性存储了一个对象,模块内部需要导出的成员都需要存储在这个对象中

const url = "http://www.example.com"

function log(message) {
  console.log(message)
}

console.log(__filename)
console.log(__dirname)

// 方法一、
// exports.endPoint = url
// exports.log = log

// 方法二、
// module.exports =  {
//   endPoint: url,
//   log: log
// }
模块成员导入

在其他文件中通过 require 方法引入模块,require 的参数就是要引入模块的路径,,引入模块的后缀可以省略,require 方法的返回值为 module.exports 对象

const logger = require("./logger")
console.log(logger)
console.log(logger.log)
console.log(logger.endPoint)
zhangmeng@zhangmengdeMacBook-Pro 2-module % node index.js
/Users/zhangmeng/Documents/课件/前端工程化开发/03-01-study-materials/03-01-code/03-01-01-前端工程化概述、Node.js基础/2-module/logger.js
/Users/zhangmeng/Documents/课件/前端工程化开发/03-01-study-materials/03-01-code/03-01-01-前端工程化概述、Node.js基础/2-module
{ endPoint: 'http://www.example.com', log: [Function: log] }
[Function: log]
http://www.example.com
Node.js 内置模块
  • path:模块内提供了一些和路径操作相关的方法
  • File System:文件操作系统,提供了和操作文件相关的方法
Path 模块
const path = require("path")

console.log(path.parse(__filename))

运行结果:

zhangmeng@zhangmengdeMacBook-Pro 2-module % node module_path.js
{
  root: '/',
  dir: '/Users/zhangmeng/Documents/课件/前端工程化开发/03-01-study-materials/03-01-code/03-01-01-前端工程化概述、Node.js基础/2-module',
  base: 'module_path.js',
  ext: '.js',
  name: 'module_path'
}
File System 模块
const fs = require("fs")

const result = fs.readdirSync("./")

fs.readdir("./", function (error, files) {
  console.log(files)
})

运行结果:

zhangmeng@zhangmengdeMacBook-Pro 2-module % node module_fs.js 
[ '.git', 'index.js', 'logger.js', 'module_fs.js', 'module_path.js' ]
NPM
Node.js 软件包

每一个基于 Node.js 开发的应用程序都是Node.js 软件包

什么是 NPM

Node Package Manager,Node.js 环境中的软件包管理器,随 Node.js 一起被安装
可以将 Node 软件包添加到我们的应用程序中,并对其进行管理,比如下载、删除、更新、查看版本等
NPM 和 Node 是两个独立的应用程序,只是被捆绑安装了

package.json

Node.js 规定在每一个软件包中都必须包含一个叫做package.json 的文件
它是应用程序的描述文件,包含应用程序的相关信息,比如:应用名称,应用版本,应用作者
通过 package.json 文件可以方便的管理应用和发布应用
创建 package.json 文件:npm init
快速创建 package.json 文件:npm init --yes

下载 Node.js 软件包

在应用程序的根目录下执行命令:

npm i 包名称
npm install 包名称 

软件包下载完成后回发生三件事情:

  1. 软件包会存储在 node_module 文件夹中,如果应用程序中不存在此文件夹,npm 会自动创建
  2. 软件包会记录在 package.json 中,包含名字及版本号
  3. npm 会在应用中创建 package-lock.json 文件,用于记录软件包及软件包依赖包的下载地址及版本
使用Node.js 软件包

在引入第三方软件包时,在require 方法中不需要添加路径信息,只需要传入包名称,require 方法会自动去 node_module 文件夹中查找

const _ = require("lodash")
const array = ["a", "b", "c", "d"]
// chunk 对数组中的元素进行分组
// 参数一表示要进行操作的数组
// 参数二表示每一组中包含的元素个数
console.log(_.chunk(array, 2)) // [ [ 'a', 'b' ], [ 'c', 'd' ] ]
软件包依赖问题
  1. 我的应用只依赖 mongoose 软件包,但是下载后 node_modules 文件夹下除了 mongoose 还有一些其他的软件包
    原因:其他的软件包为 mongoose 它的依赖包

  2. 为什么 mongoose 的依赖包不放在 mongoose 的文件夹中
    原因一:A依赖X,B依赖X,C依赖X,X就会下载3次
    原因二:A依赖B,B依赖C,就会存在文件夹嵌套的情况

  3. 所有的软件包都放在 node_modules 文件夹下不会导致版本冲突
    原因:A依赖X的1.0版本,B依赖X的2.0版本,先下载A,X的1.0版本会放置在根目录下 node_modules 文件夹中。再下载B,发现根目录下已经存在X,但是版本不一致,所以X的2.0版本会放置在 B 软件包下的 node_modules 文件夹中

  4. node_modules 文件夹中的软件包不需要提交到 git 仓库中,但是其他人拿到应用程序运行不起来
    解决方案:可以根据 package.json 中的记录下载对应的软件包。为了版本一致,npm 还会根据 package-lock.json 文件中的记录的地址进行下载
    ⚠️:将应用程序提交到版本库之前,将 node_modules 文件夹添加到 .gitignore 文件中

git init
git status
echo "node_modules/" > .gitigore
git status
git add .
git commit -m "npm first commit"
异步编程
CPU 与存储器
  • CPU
    中央处理器,代码被编译为机器码后,就是通过 CPU 执行的
  • 存储器
    内存:临时存储数据,断电后数据丢失
    磁盘:持久存储数据,断电后数据不丢失
I/O 模型
  • 同步 I/O 操作
    CPU等待 I/O 操作完成获取到操作结果后再去执行其他指令
  • 异步 I/O 操作
    CPU不等待 I/O 操作完成,CPU 发出 I/O 操作指令后,内存和磁盘开始工作,CPU继续执行其他指令, 当 I/O 操作完成后,会通知 CPU 调用回调函数
  • Node.js 采用的就是异步非阻塞 I/O 模型
const fs = require("fs")

fs.readFile("./x.txt", "utf-8", function (err, result) {
  console.log(result)
})
console.log("Hello")

执行结果:

zhangmeng@zhangmengdeMacBook-Pro 4-async % node 1-callback.js 
Hello
x
const fs = require("fs")
const data = fs.readFileSync("./y.txt", { encoding: "utf-8" })
console.log(data)

执行结果:

zhangmeng@zhangmengdeMacBook-Pro 4-async % node 1-callback.js
y
进程与线程

当我们运行应用程序时,操作系统都会创建该应用程序的实例对象,该实例对象就是应用程序的进程,操作系统会按进程为单位,为应用程序分配资源,比如内存,这样应用程序就可以在操作系统中运行起来

JS 单线程还是多线程

在 Node.js 代码运行环境中,它为js代码运行提供了一个主线程,通常我们说的单线程就是指这个主线程,但是 Node.js 内部依赖了一个叫做 libuv 的 c++ 库,在这个库中它维护了一个线程池,默认情况下,这个线程池中存在4个线程,js中的异步代码就是在这些线程中执行的,所以说js代码的运行不仅仅是一个线程,本质上js还是多线程的

基于回调函数的异步编程
什么是回调函数

回调函数是指将一个函数通过参数的方式传递给另一个函数,参数函数就是回调函数

function A() {
  console.log("A is running")
}
function B(callback) {
  console.log("B Start")
  callback() // A is running
  console.log("B End")
}
B(A)
回调函数传递参数
function A(arg) {
  console.log("A is running")
  console.log(arg)
}
function B(callback) {
console.log("B Start") callback("我是B函数传递给A函数的参数") // A is running console.log("B End")
}
B(A)
回调函数在异步编程中的应用
const fs = require("fs")
fs.readFile("./index.html", "utf-8", function (error, data) { if (error) console.log("发生了错误")
console.log(data)
})
回调地狱

回调函数多层嵌套导致代码难以维护

const fs = require("fs")
fs.readFile("./x.txt", "utf-8", function (error, x) {
  fs.readFile("./y.txt", "utf-8", function (error, y) {
    fs.readFile("./z.txt", "utf-8", function (error, z) {
      console.log(x)
      console.log(y)
      console.log(z)
}) })
})
基于promise的异步编程
promise 概述
  • promise 是 js 中异步编程解决方案,可以解决回调函数地狱问题
  • promise 可以理解为一个容器,用于包裹异步api,当容器中的一步 api 执行完成后,promise 允许我们在容器外面获取异步 api 的结果,从而避免回调函数嵌套
  • promise 有三个状态,分别是:等待(pending)、成功(fulfilled)、失败(rejected),默认为等待,等待可以变成成功,等待可以变成失败
  • 状态一旦更改不可以改变
promise 基础语法
const fs = require("fs")

const promise = new Promise(function (resolve, reject) {
  fs.readFile("./x.txt", "utf-8", function (error, result) {
    if (error) {
    // pending -> rejected
      reject(error)
    } else {
    // pending -> fulfilled
      resolve(result)
    }
  })
})

promise
  .then(function (result) {
    console.log(result)
  })
  .catch(function (error) {
    console.log(error)
  })
promise 链式调用
const fs = require("fs")

function readFile(path) {
  return new Promise(function (resovle, reject) {
    fs.readFile(path, "utf-8", function (error, result) {
      if (error) {
        reject(error)
      } else {
        resovle(result)
      }
    })
  })
}

readFile("./x.txt")
  .then(function (x) {
    console.log("x")
    return readFile("./y.txt")
  })
  .then(function (y) {
    console.log("y")
    return readFile("./z.txt")
  })
  .then(function (z) {
    console.log("z")
  })
  .catch(function (error) {
    console.log(error)
  })
  .finally(function () {
    console.log("finally")
  })

运行结果:

zhangmeng@zhangmengdeMacBook-Pro 4-async % node 3-then.js
xx
yy
zz
finally
promise.all 并发异步操作
function readFile(path) {
  return new Promise(function (resovle, reject) {
    fs.readFile(path, "utf-8", function (error, result) {
      if (error) {
        reject(error)
      } else {
        resovle(result)
      }
    })
  })
}

Promise.all([
  readFile("./x.txt"),
  readFile("./y.txt"),
  readFile("./z.txt")
]).then(function (result) {
  console.log(result)
})

运行结果:

zhangmeng@zhangmengdeMacBook-Pro 4-async % node 4-Promise.all.js 
[ 'xx', 'yy', 'zz' ]
基于异步函数的异步编程

promise 虽然解决了回调地狱问题,但是代码仍然不简洁,可以使用异步函数简化代码

  1. 异步函数概述
const fs = require("fs")
function readFile(path) {
  return new Promise(function (resolve, reject) {
    fs.readFile(path, "utf-8", function (error, data) {
      if (error) return reject(error)
      resolve(data)
}) })
}
async function getFileContent() {
  let x = await readFile("./x.txt")
  let y = await readFile("./y.txt")
  let z = await readFile("./z.txt")
  return [x, y, z]
}
getFileContent().then(console.log)
  1. util.promisify
const fs = require("fs")
const util = require("util")
// 将基于回调函数的异步 API 转换为返回 Promise 的API
const readFile = util.promisify(fs.readFile)
async function getFileContent() {
  let x = await readFile("./x.txt", "utf-8")
  let y = await readFile("./y.txt", "utf-8")
  let z = await readFile("./z.txt", "utf-8")
  return [x, y, z]
}
getFileContent().then(console.log)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值