Node

Node介绍

  1. node 是一个开发平台, 就像 Java 开发平台、.Net 开发平台、PHP 平台、Apple 开发平台一样。
    • 何为开发平台? 有对应的编程语言、有语言运行时、有能实现特定功能的 API (例如: fs http path)
  2. 该平台使用的编程语言:javascript 语言.
  3. node.js 平台是基于谷歌 V8引擎构建的
  4. 基于 node.js 可以开发控制台程序(命令行程序)、桌面应用程序(借助 electron 、node-webkit等框架实现)、Web 应用程序(网站)

#Node的特点

  1. 事件驱动(事件触发,会执行回调函数里面的代码)

    • js也是事件驱动,鼠标放置,点击,触发不同的事件执行对应的代码
    • onclick = function(){}
    • node.js 是传一个回调函数

    2.非阻塞(I/O)

    • I/O就是输入输出的操作
    • 读写I/O+网络I/O
    • nodejs 的I/O 不会阻塞程序
1
2
3
4
5
6
7
8
9
//引入模块
let fs = require('fs')

console.log(111);

fs.readFile(function(data){
console.log(data,222);
})
console.log(333);

  • 不会阻塞我们主程序的运行
  1. 单线程;
  • 首先我们 js 就是单线程的语言, node基于 js 也是单线程;
  • 减少内存开销 (线程/2M)
  1. 拥有世界最大的开源生态系统– npm
  • npm - 开源库托管网站! 官网
  • 我们 node是一个开发平台有 实现特定功能的 api, 但是内置的 api 是有限的,,想要实现 node.js 自己实现不了的功能怎么办 ?? 就去找 npm,,
  • 只要是我们想到的功能,都已经有人帮我们已经写好了,就在 npm 这个代码仓库里,,,
  • npm 这个丰富的开源系统,,而且也很开放, 也都会写模块,,然后把代码上传到 npm 上,供大家使用
  • 就像 jquery 一样,很多人都给它写插件啊,,给她拓展功能啊,
  • 我们 node.js 也是,,,, npm 里面帮我们node拓展了各种各样的功能包

#Node 创建 文件名命名常用规范

  • 不要用中文 (特别里面是使用 npm 安装包的时候: npm init -y)
  • 不要包含空格
  • demo里可以 项目总切记不要
  • 不要出现node关键字
  • 建议以 ‘ - ‘ 分割单词 (例如: child-demo.js )

Node 编写代码常用规范

  • 命名: 变量名和函数名命名,按照驼峰命名 —— var userName = ‘123’
  • 优先使用 const(常量) 和 let (变量)
  • 引入: 引入模块时,变量名最好和模块名一样: var fs = require('fs');
  • 尽量使用单引号 ,, json数据里使用双引号
  • 动态字符串使用 反引号 :
    1
    `你好啊 ${name}` ''
  • 空格: 操作符前后需要加空格, 比如 + - * / = var foo = 'bar' + 'baz'
  • 分号: 表达式结尾添加分号,,,虽然编译器自动会给我们,可能会带来一些错误!
1
2
3
4
5
6
7
8
9
10
11
12
var x = 1;
var y = 2;
x=y
(function(){
console.log(x);
})()

...........................
//执行时会误以为是:
x=y(function(){}());

// 到时候会报错: y is not a function

案例 1. 编写 Hello world 在 node 环境下执行

  1. 创建 hello-world.js文件
  2. 编写: console.log('hello world')
  3. 终端打开: node hello-world.js 运行即可 (在 node环境下运行)
1
2
3
4
注意1: node + 文件名 / 路径 
注意2: 文件名和路径不要手写,容易写错,,tab
注意3: 确保当前路径没有错
注意4: 终端命令介绍: cd/pwd/ls/ctr+c

###案例 2: 文件读写案例

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
/**
* 写入文件 write file
*/
//1. 引入 fs 模块
var fs = require('fs');

//2. 写入文件
// fs.writeFile(file, data[,options], callback)
// 异步写入数据到文件
// 参数1: 文件要写入到哪里
// 参数2: 要写入的数据
// 参数3: 编码 默认: utf8 可选
// 参数4: 回调 回调参数: err
var msg = '..: 第一美女助教11';
console.log(111)
fs.writeFile('./data.txt',msg,function (err) {

// 如果有错,,抛错
if (err) {
throw err;
}

console.log('写入成功')
})
console.log(222)
// 注意:
//1. 如果文件存在会覆盖
//2. 该操作是异步操作
// (遇到异步的操作,先执行后面的代码,等异步回调回来再执行回调的内容)
//3. 默认写入的文件编码: 为 utf8 可选 一般不写
//4. 在回调里面,判断是否有错,,有错就抛出异常 throw err

读取文件

  1. 读取文件 : fs.readFile(file, [, options], callback);

    常用: fs.readFile ( where , encoding , callback )

  • 参数1: 从哪里读取数据? 必填
  • 参数2: 以什么格式读取出来 ?
  • 参数3: 读取完毕后的回调函数, 必填
  • 读取文件注意:
    • 该操作也是 异步操作;
    • 回调函数有2个参数: errdata
    • 如果读取文件时没有指定编码,那么返回的将是原生的二进制数据;如果指定了编码,那么会根据指定的编码返回对应的字符串数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 问题1:
// <Buffer e6 9c 89 e6 b2 a1 e6 9c 89 e8 be a3 e4
// b9 88 e4 b8 80 e9 a6 96 e6 ad 8c 2c e8 ae a9 e4 bd a
// 0 e6 83 b3 e8 b5 b7e6 88 91 3f 3f>
// Buffer 是一个缓冲 是一个字节数组 就是一种二进制数据 负责传输数据和文件
//1. Buffer 是什么 ? 是一个缓冲,,是一个二进制数据流格式
// 是发送或者接收文件传输过程中的格式
//2. 它类似于一个数组 每个元素是 2位十六进制的字节 字节数组
//3. 一个汉字三个字节
// 是我们想要的格式吗??
// 我们想要一个字符串
// 只要通过 toString() 就可以了 其实内部已经给我们转化为了 utf8格式

//问题2: ENOENT E: error NO :no ENT entity: 实体 没有这个文件或者文件夹

//问题3: 显示第二个参数 :'utf8' 也是可以的

同步读取文件

  1. 方法: fs.readFileSync(path[, options])
  2. 返回值 接收数据
  3. // fs.readFileSync(path[, options])
    // 参数1: 从哪里读取文件
    // 参数2: 编码 (可选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log(哈哈)

var data = fs.readFileSync('./data1.txt','utf8');

console.log(data)

console.log(打印不出)

// 总结:
//1. 该操作是同步的
//2. 异步捕捉异常: throw err
// 同步 : 能不能不捕捉异常
// 同步,一旦出错,,后面的所有代码都不执行,,体验很不好,,必须要有捕捉
//3. 怎么捕捉呢?
// try ...catch()

try…catch (捕获异常 , 抛出错误)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 一旦出错,后面的代码不执行了..
console.log(111)

try {
var data = fs.readFileSync('./abcd.txt','utf8');

console.log(data)

}catch(err){

// throw err
console.log('读取时:'+err)
}

console.log(222)

异步读文件

###throw errr (捕获异常 , 抛出错误)

问题: 同步没有 err , 能不能不捕获异常了???

1
2
no
后面的会崩溃,不会执行
  1. 同步: try catch
1
异步不能使用 try..catch 因为不管正确和错误都会走回调

路径

  1. __dirname
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
//1. 加载 fs 模块
var fs = require('fs');
var path = require('path') // path 模块 负责处理路径

//2. 读取文件


// ('./data.txt')

console.log(__dirname);
///Users/MaWenxing/Desktop/14期讲课/day01/4-源代码/day01_14/04-__dirname
///Users/MaWenxing/Desktop/14期讲课/day01/4-源代码/day01_14/04-__dirname

// 后面的路径,,我能获取到,如果我们也能自动获取前面的就好了?
// 巧了,,,,唉,,巧了,,,,正好有一个熟悉,,__dirname

// 当前 js 的文件夹路径 js 的相对文件路径
// /Users/MaWenxing/Desktop/14期讲课/day01/4-源代码/day01_14/04-__dirname /data.txt
// var file = '/Users/MaWenxing/Desktop/14期讲课/day01/4-源代码/day01_14/04-__dirname/data.txt'

// /data.txt ./data.txt data.txt
// var file = __dirname + '/data.txt'
// var file = path.join(__dirname,'./data.txt')
fs.readFile(path.join(__dirname,'./data.txt'),'utf8',function (err,data) {

if (err) {
throw err
}

console.log(data)
})

// 报错的原因: path 路径是相对于 `执行 node 命令所在的目录`

// __dirname : 获取当前 js 所在的`文件夹`目录
// 因为怕误写 ./data.txt /data.txt data.txt --> path.join
//

// 总结:
// 以后遇到这种文件路径的: 全部使用 path.join(__dirname,'./....')

通过 node.js 编写 http 服务程序 - 极简版本

  • 步骤

    1. 加载 http 模块;

    2. 创建 http 服务;

    3. 监听 request 事件 —— 为 http 服务对象添加 request 事件处理程序;

    4. 启动服务,开始监听 —— 开启 http 服务监听,准备接收客户端请求;

      *注意

      1. 浏览器可能是乱码, 需要设置 浏览器显示时所使用的编码

        1
        response.setHeader('Content-Type','text/plain ; charset=utf-8');  // 注意 后面的分号 和引号的位置
        1. 演示一下设置Content-Type=text/htmlContent-Type=text/plain的区别。
        2. request 对象 包含了用户请求报文中的所有内容,通过 request 对象可以获取所有用户提交过来的数据;
        3. response 对象用来向用户响应一些数据,当服务器要向浏览器响应数据的时候必须使用 response 对象
        4. 有了 request 对象和 response 对象,就既可以获取用户提交的数据,也可以向用户响应数据了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//1. 加载 http 模块
var http = require('http');

//2. 创建 http 服务
var server = http.createServer();

//3. 监听 request 事件
// 参数1: 请求体 request -> req
// 参数2: 响应体 response -> res
server.on('request',function (req,res) {

console.log('有人请求了') // 会打印两次,一次是请求 ico 图标 (忽略)
res.write('hello world 哈哈哈'); //2
res.end(); //1.
})

//4. 启动服务,开始监听
// 参数1: 指定一个端口 如果冲突了,再换一个
// 参数2: 回调
server.listen(9000,function () {
console.log('服务已经启动,请访问: http://127.0.0.1:9000')
})

简化: 创建、开启、监听 一步走

1
2
3
4
5
6
7
8
//2. 创建 开启 监听
http.createServer(function (req,res) {

res.end('hello world')
}).listen(8080,function () {

console.log('服务器已经开启了')
})

text/plain 和 text/html 的区别

1
2
3
4
5
response.setHeader('Content-Type','text/plain ; charset=utf-8');  // 注意 后面的分号 和引号的位置
text/plain : 告诉浏览器发送的数据是 `纯文本`类型,,让浏览器以`纯文本`的格式进行解析
text/html : 告诉浏览器发送的数据是 `html` 类型,,...........html.......

res.end('hello <h1>world</h1> 哈哈') //不能直接写 `<h1>`因为浏览器会自动识别

编写 http 服务程序 — 根据 不同的路径 url 请求 响应不同的 : 纯文本

注意:

  • 根据 req.url的不同来判断 (例如: /abc /index )

  • 别忘了 404

    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
    /**
    * 编写 http 服务程序 — 根据 不同的 url 请求 响应不同的 : 纯文本
    * 1. 编写 http 服务程序
    * 2. 获取不同的 url
    * 3. 响应不同的纯文本
    */
    //1. 加载 http 模块
    var http = require('http');

    //2. 创建.开启.监听
    http.createServer(function(req,res){

    // 设置文件类型 和 编码格式
    res.setHeader('Content-Type','text/plain;charset=utf-8');

    console.log(req.url);

    // / index --> hello index
    // /login --> hello login
    // /register --> hello register
    // /list --> hello list

    if (req.url=='/' || req.url == '/index') {

    res.end('hello index')

    } else if(req.url=='/login') {

    res.end('hello login')
    }else if(req.url=='/register') {

    res.end('hello register')
    }else if(req.url=='/list') {

    res.end('hello list')
    }else{

    res.end('hello 404 page not found')
    }


    }).listen(8080,function () {
    console.log('服务开启了');
    })

Common System Errors - 常见错误号

  • EACCESS (Permission denied)
    • An attempt was made to access a file in a way forbidden by its file access permissions.
    • 访问被拒绝
  • EADDRINUSE (Address already in use)
    • An attempt to bind a server (net, http, or https) to a local address failed due to another server on the local system already occupying that address.
    • 地址正在被使用(比如:端口号占用)
  • EEXIST (File exists)
    • An existing file was the target of an operation that required that the target not exist.
    • 文件已经存在
  • EISDIR (Is a directory)
    • An operation expected a file, but the given pathname was a directory.
    • 给定的路径是目录
  • ENOENT (No such file or directory)
    • Commonly raised by fs operations to indicate that a component of the specified pathname does not exist – no entity (file or directory) could be found by the given path.
    • 文件 或 目录不存在
  • ENOTDIR (Not a directory)
    • A component of the given pathname existed, but was not a directory as expected. Commonly raised by fs.readdir.
    • 给定的路径不是目录