# 1.cmd

1. 命令行窗口 (小黑屏)、CMD 窗口、终端、shell - 开始菜单 --> 运行 --> CMD --> 回车
- 常用的指令:
dir 列出当前目录下的所有文件
cd 目录名 进入到指定的目录
md 目录名 创建一个文件夹
rd 目录名 删除一个文件夹

- 目录
	. 表示当前目录
	.. 表示上一级目录
	
- 环境变量(windows系统中变量)	
	path
		C:\work\jdk\jdk1.7.0_75/bin;
		%CATALINA_HOME%/bin;
		C:\work\soft\tools\AppServ\Apache24\bin;
		C:\work\soft\tools\AppServ\php5;
		C:\Users\lilichao\AppData\Local\Programs\Fiddler;
		C:\work\environment\Egret\Egret Wing 3\bin;
		C:\Users\lilichao\AppDataoaming\npm;
		C:\Program Files\MongoDB\Server\3.2\bin;
		C:\Users\lilichao\Desktop\hello
		
	- 当我们在命令行窗口打开一个文件,或调用一个程序时,
		系统会首先在当前目录下寻找文件程序,如果找到了则直接打开
		如果没有找到则会依次到环境变量path的路径中寻找,直到找到为止
		如果没找到则报错
		
	- 所以我们可以将一些经常需要访问的程序和文件的路径添加到path中,
		这样我们就可以在任意位置来访问这些文件和程序了

I/O (Input/Output)
- I/O 操作指的是对磁盘的读写操作

Node
- Node 是对 ES 标准一个实现,Node 也是一个 JS 引擎
- 通过 Node 可以使 js 代码在服务器端执行
- Node 仅仅对 ES 标准进行了实现,所以在 Node 中不包含 DOM 和 BOM
- Node 中可以使用所有的内建对象
String Number Boolean Math Date RegExp Function Object Array
而 BOM 和 DOM 都不能使用
但是可以使用 console 也可以使用定时器(setTimeout () setInterval ())

- Node可以在后台来编写服务器
	Node编写服务器都是单线程的服务器
	- 进程
		- 进程就是一个一个的工作计划(工厂中的车间)
	- 线程
		- 线程是计算机最小的运算单位(工厂中的工人)
			线程是干活的
			
- 传统的服务器都是多线程的
	- 每进来一个请求,就创建一个线程去处理请求
	
- Node的服务器单线程的
	- Node处理请求时是单线程,但是在后台拥有一个I/O线程池

# 2.Node

# 1. 给 Node 传参

我们都知道,node 中有一个全局对象叫做作 process 对象

node index.js Nirvana age=18

我们其实会发现我们所传递的参数,都会存在 node 中的 argv 数组中。

argv:[
   "node":node的系统路径,
   "index.js":运行javascri文件的路径
   ...[参数
    e.g:
    "Nirvana",
    "age=18"
]

当我们需要去访问传递的参数时我们可以

process.argv[2];
process.argv[3];
你也可以:
process.argv.foreach()

# 2.Node 程序的输出

# console.log

最常用的输出内容的方式

# console.clear

清空控制台

# console.trace

打印执行栈

# 3. 常见的全局对象

首先我们先了解一些特殊的全局对象

为什么叫做全局对象呢?

imgimg

● 这些全局对象实际上是模块中的变量,只是每个模块都有,看起来像全局变量
● 在命令行交互中是不可以使用的
● 包括:_dirname,_filename,exports,module,require()
  • process 对象:process 提供了 Node 进程中相关的信息

  • console 对象:提供简单的控制台调试

  • 定时器函数:在 Node 中使用定时器的方式有好多种

  • global 对象

global 是一个全局对象,事实上前端我们提到的 process,console,setTimeout 等都要放在其中

img

他其实就跟我们的 window 对象很像,但也有区别

var name ="Nirvana"
console.log(name)//Nirvana
console.log(global.name)//undefined
因为在浏览器中,是没有模块的概念,但是在node中会有模块化的思想,每一个文件都是一个模块,他不会轻易将变量放在global上面的

# 4.JavaScript 的模块化

# 1. 什么是模块化

  • 事实上模块化开发最终的目的是将程序划分成一个个小的结构

  • 这个结构中编写属于自己的逻辑代码,有自己的作用域,不会影响到其他的结构

  • 这个结构也可以将自己希望暴露的变量,函数,对象等导出给其他结构使用

  • 也可以导入

# 2.CommonJS

Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。

# 1. 特点

  • 所有代码都运行在模块作用域,不会污染全局作用域。

  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。

  • 模块加载的顺序,按照其在代码中出现的顺序。

  • CommonJS 模块就是对象

  • 运行时加载

# 2. 基本语法

// 暴露方式
module.exports = value;
exports.xxx = value;
// 导入方式
const value = require('./test.js');// 自定义模块就是路径,第三方模块就是名称

img

# 3. require 细节

  • require 的加载过程是同步的
  • 当模块被多次引入时,模块会进行缓存,最后只执行一次

img

img

require 的加载过程

img

# 5.Path 模块

img

基础 api 可以查看 http://nodejs.cn/api/path.html

主要注意的是 path.join 和 path.resolve 的区别!!!!

# 6.fs 模块

img

img

# demo 案例

const fs = require('fs');
const filepath ="./app.js"
//1. 同步操作
// const info = fs.statSync(filepath);
//console.log ("后续需要执行的代码");
// console.log(info);
//2. 异步操作
// fs.stat(filepath,(err,info) => {
//     if(err){
//         console.log(err);
//         return;
//     }
//     console.log(info);
// })
//console.log ("后续需要执行的代码");
//3.promise
fs.promises.stat(filepath,(err,info) => {
        if(err){
            console.log(err);
            return;
        }
        console.log(info);
    })
    console.log("后续需要执行的代码");

# 文件描述符

const fs = require("fs")
fs.open("./abc.txt",(err,info)=>{
  if(err){
    console.log(err);
    return;
  }
  fs.fstat(fd,(err,info)=>{
      console.log(info) 
  })
})

# 文件的读写

const fs = require('fs');
const content = "console.log(1)"
fs.writeFileSync("./demo.js",content,(err) => {
    if(err){
        console.log(err);
    }
    
})

# 7.events 模块

# 基础方法

const EventEmitter = require('events');
const emitter = new EventEmitter();
//2. 监听一个事件
emitter.on("click",(args) => {
    console.log("监听到click事件",args);
})
//3. 发出一个事件
emitter.emit("click",(args) => {})

# 8. 实现 CLI

  1. index.js
#! /user/bin/env node
这其实是一个shebang 他会去全局寻找node然后来执行
  1. 在 package.json 里去配置一个 bin
{
  "name": "cli-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin":{
    "nirvana":"index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  1. 使用 npm link 去将 bin 里的指令与环境变量做一个链接
  2. 引入 commander 包,便于对参数进行添加查询(--version --help)

img

  1. 命令拓展

create.js

img

action.js

# 9.Buffer 模块

# 什么是 buffer?

Buffer 是内存区域。 JavaScript 开发者可能对这个概念并不熟悉,比每天与内存交互的 C、C++ 或 Go 开发者(或使用系统编程语言的任何程序员)要少得多。

它表示在 V8 JavaScript 引擎外部分配的固定大小的内存块(无法调整大小)。

可以将 buffer 视为整数数组,每个整数代表一个数据字节。

它由 Node.js Buffer 类实现。

# 为什么需要 buffer?

Buffer 被引入用以帮助开发者处理二进制数据,在此生态系统中传统上只处理字符串而不是二进制数据。

Buffer 与流紧密相连。 当流处理器接收数据的速度快于其消化的速度时,则会将数据放入 buffer 中。

一个简单的场景是:当观看 YouTube 视频时,红线超过了观看点:即下载数据的速度比查看数据的速度快,且浏览器会对数据进行缓冲。

# 如何创建 buffer

使用 Buffer.from()Buffer.alloc()Buffer.allocUnsafe() 方法可以创建 buffer。

const buf = Buffer.from('Hey!')
  • Buffer.from(array)

  • [Buffer.from(arrayBuffer, byteOffset[, length]])

  • Buffer.from(buffer)

  • [Buffer.from(string, encoding])

也可以只初始化 buffer(传入大小)。 以下会创建一个 1KB 的 buffer:

const buf = Buffer.alloc(1024)
// 或
const buf = Buffer.allocUnsafe(1024)

虽然 alloc 和 allocUnsafe 均分配指定大小的 Buffer(以字节为单位),但是 alloc 创建的 Buffer 会被使用零进行初始化,而 allocUnsafe 创建的 Buffer 不会被初始化。 这意味着,尽管 allocUnsafe 比 alloc 要快得多,但是分配的内存片段可能包含可能敏感的旧数据。

当 Buffer 内存被读取时,如果内存中存在较旧的数据,则可以被访问或泄漏。 这就是真正使 allocUnsafe 不安全的原因,在使用它时必须格外小心。

# 使用 buffer

# 访问 buffer 的内容

Buffer(字节数组)可以像数组一样被访问:

const buf = Buffer.from('Hey!')
console.log(buf[0]) //72
console.log(buf[1]) //101
console.log(buf[2]) //121

这些数字是 Unicode 码,用于标识 buffer 位置中的字符(H => 72、e => 101、y => 121)。

可以使用 toString () 方法打印 buffer 的全部内容:

console.log(buf.toString())

注意,如果使用数字(设置其大小)初始化 buffer,则可以访问到包含随机数据的已预初始化的内存(而不是空的 buffer)!

# 获取 buffer 的长度

使用 length 属性:

const buf = Buffer.from('Hey!')
console.log(buf.length)

# 迭代 buffer 的内容

const buf = Buffer.from('Hey!')
for (const item of buf) {
  console.log(item) //72 101 121 33
}

# 更改 buffer 的内容

可以使用 write () 方法将整个数据字符串写入 buffer:

const buf = Buffer.alloc(4)
buf.write('Hey!')

就像可以使用数组语法访问 buffer 一样,你也可以使用相同的方式设置 buffer 的内容:

const buf = Buffer.from('Hey!')
buf[1] = 111 //o
console.log(buf.toString()) //Hoy!

# 复制 buffer

使用 copy () 方法可以复制 buffer:

const buf = Buffer.from('Hey!')
let bufcopy = Buffer.alloc(4) // 分配 4 个字节。
buf.copy(bufcopy)

默认情况下,会复制整个 buffer。 另外的 3 个参数可以定义开始位置、结束位置、以及新的 buffer 长度:

const buf = Buffer.from('Hey!')
let bufcopy = Buffer.alloc(2) // 分配 2 个字节。
buf.copy(bufcopy, 0, 0, 2)
bufcopy.toString() //'He'

# 切片 buffer

如果要创建 buffer 的局部视图,则可以创建切片。 切片不是副本:原始 buffer 仍然是真正的来源。 如果那改变了,则切片也会改变。

使用 slice () 方法创建它。 第一个参数是起始位置,可以指定第二个参数作为结束位置:

const buf = Buffer.from('Hey!')
buf.slice(0).toString() //Hey!
const slice = buf.slice(0, 2)
console.log(slice.toString()) //He
buf[1] = 111 //o
console.log(slice.toString()) //Ho

# 10. 事件循环

这里的话就不过多解释啦,大家可以自行去看执行栈和事件循环(包括宏任务和微任务)

# 阻塞 IO 和非阻塞 IO

做个简单的比喻:进程 = 火车,线程 = 车厢

  • 线程在进程下行进(单纯的车厢无法运行)

  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)

  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)

  • 同一进程下不同线程间数据很易共享(A 车厢换到 B 车厢很容易)

  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)

  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)

  • 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)

  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"

  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

img

# 11.Http 模块

# Web 服务器

img

# web 服务器初体验

const http =require('http');
// 创建一个 web 服务器
const server = http.createServer((req,res) => {
    res.end("hello world")
})
// 启动服务器,并且制定端口号和主机
server.listen(8000,'localhost',()=>{
    console.log('服务器启动成功~');
});

img

# 基于 request 的基本使用

const http =require('http');
// 创建一个 web 服务器
const server = http.createServer((req,res) => {
  
    //requset 对象中封装了客户段给我们服务器传过来的所有信息
    console.log(req.url);// 请求路径
    console.log(req.method);// 请求方式
    console.log(req.headers);// 请求头
    res.end("hello world")
})
// 启动服务器,并且制定端口号和主机
server.listen(8000,'localhost',()=>{
    console.log('服务器启动成功~');
    console.log(server.address());
});

img

img

# url 模块的使用

const http =require('http');
const url = require('url');
const querystring = require('querystring')a
// 创建一个 web 服务器
const server = http.createServer((req,res) => {
    const {pathname,query} = url.parse(req.url)
 
    // res.end("hello world")
  if(pathname === "/login"){
      console.log(query);
      const {username,password} = querystring.parse(query)// 这是一个对象
      res.end("欢迎回来~")
  }
     // 基本使用方式
    // if(req.url === '/login'){
    //     res.end ("欢迎回来~")
    // }else if(req.url === '/users'){
    //     res.end ("用户列表~")
    // }else{
    //    res.end ("错误请求,请检查~")
    // }
})
// 启动服务器,并且制定端口号和主机
server.listen(8001,'localhost',()=>{
    console.log('服务器启动成功~');
    console.log(server.address());
});

# method 的使用

const http =require('http');
const url = require('url');
const querystring = require('querystring')
// 创建一个 web 服务器
const server = http.createServer((req,res) => {
    const {pathname} = url.parse(req.url);
    if(pathname === "/login"){
        console.log(res.method);
        if(req.method === 'POST'){
            // 拿到 body 中的数据
            req.setEncoding('utf8');// 设置 requset 的编码格式是 utf-8
            req.on('data', (data) =>{
                // console.log(data.toString());
                console.log(data);
                const {username, password} =JSON.parse(data)
                console.log(username,password);
            })
        }
        res.end("111")
    }
})
// 启动服务器,并且制定端口号和主机
server.listen(8001,'localhost',()=>{
    console.log('服务器启动成功~');
    console.log(server.address());
});

# header 属性

# 1.content-type 是这次请求携带的数据类型

  • application/json:表示这是一个 json 类型

  • text/plain:表示是文本类型

  • application/xml:表示是 xml 类型

  • multipart/form-data:表示是上传文件

# 2.content-length 是文件的大小与长度

# 3.keep-alive:

  • http 是基于 TCP 协议的,但是通常在进行一次请求和响应结束后会立即中断
  • 在 http1.1 中,所有连接的默认是 keep-alive 的

# 4.accept-encoding: 告知服务器,客户端支持的文件压缩格式

# 响应结果

const http =require('http');
const url = require('url');
const querystring = require('querystring')
// 创建一个 web 服务器
const server = http.createServer((req,res) => {
    // 设置响应的 header
    // 方式一:
    // res.setHeader('Content-Type', 'application/json');
    // 方式二
    res.writeHead(200,{
        'Content-Type': 'text/html'
    });
    // 响应结果
    res.write("响应结果一");
    res.end("<h1>hello world</h1>")
})
// 启动服务器,并且制定端口号和主机
server.listen(8001,'localhost',()=>{
    console.log('服务器启动成功~');
});

# 网络请求

const http = require('http');
//GET 请求
http.get("http://localhost:8000",(res)=>{
    res.on("data", (data)=>{
        console.log(data);
    })
    res.on("end",()=>{
        console.log("获取所有结果");
    })
})
//POST 请求
const req =http.request({
    method: "POST",
    host: "localhost",
    port:8000
},(res)=>{
    res.on("data", (data)=>{
        console.log(data);
    })
    res.on("end",()=>{
        console.log("获取所有结果");
    })
})
req.end("post请求finish")

# Express 框架学习

Express 整个框架的核心就是中间件,理解了中间件一切都非常简单

# 1.Express 安装

  1. 自己搭建 (有时间的小伙伴可以试试)
  2. 通过 cli 实现
// 安装脚手架
npm i -g express-generator
// 创建项目
express express-demo
// 安装依赖
npm i
// 启动项目
node bin/www

# 2.express 初体验

const express = require('express');
//express 其实是一个函数 “createApplication”
// 返回 app
const app =express();
app.get('/', (req, res,next) => {
    res.end("hello,express")
})
app.post('/', (req, res, next) => {
    res.end("hello, post express")
})
app.post('/login', (req, res, next) => {
    res.end("hello, login")
})
// 开启监听
app.listen(8000,() => {
    console.log("服务器启动了")
})

# 3. 认识中间件

img

img

# 4. 中间件的应用

一般来说由我们自己来编写

  • express 主要提供了两种方式:app/router.use 和 app/router.methods
阅读次数