nodejs day1

day1十部曲

1.课程安排
2.授课思路
3.浏览器工作原理
4.web开发的本质
5.nodejs的介绍
6.基于nodejs编写程序
7.nodejs学习
8.nvm
9.buffer
10.总结

一、课程安排

课程总天数:7天

第 01 天:

  • 浏览器工作原理
  • 浏览器访问网站全过程
  • node.js 介绍
  • 通过 node.js 编写服务端程序

第 02 天:

  • node.js 中的 request 对象 和 response 对象
  • npm 介绍
  • HTML 页面中路径的真实含义
  • package.json 文件
  • 路由介绍、设计路由
  • 代码封装
  • 开始实现 Hacker News 网站
  • 循环中的异步
  • 封装异步函数
  • 待定:模拟 Chrome 显示文件列表

第 03 天:

  • HackerNews 新闻列表展示
  • 添加新闻页
  • 新闻详情页
  • underscore
  • module 、 module.exports 和 exports
  • node.js 模块化

第 04 天:

  • HackerNews 代码模块化
  • http-server
  • express 介绍
  • 中间件
  • 通过 express 完成 HackerNews 思路

第 05 天:

  • express 实现 Hacker News
  • MongoDb 介绍

    第 06 天:

  • 完善 Hacker News
  • 封装 DB 模块

    第 07 天:

  • 实现 sms
  • ajax 实现 sms
  • node.js 抓取数据、cheerio 介绍
  • 模拟 body-parser 中间件

二、授课思路

准备知识

浏览器工作原理

  1. 浏览器组成
  2. 浏览器渲染引擎工作原理
  3. 通过浏览器访问网站全过程

Web 开发本质

  1. 请求: 客户端发起请求.
  2. 处理: 服务器 处理请求.
  3. 响应: 服务器将处理结果发送给客户端.

Web应用程序与桌面应用程序对比(计算器案例)

关于C/S(Client/Server) 和 B/S(Browser/Server)

Client / Server

node.js 介绍

  1. node.js 是什么?
  2. node.js 有哪些特点?
  3. node.js 官方网站
  4. node.js 学习资源
  5. 为什么要学习Node.js?
  6. Node.js安装和配置
  7. Node.js 开发 Web 应用程序 和 PHP、Java、ASP.Net等传统模式开发Web应用程序区别

node.js 编程

  1. node.js 编写控制台程序
  2. node.js 进行文件读写
    • 同步 & 异步读取文件
  3. node.js 创建 http 服务程序
    解释异步是如何实现的? eventloop
    npm 介绍
    node.js 实现新闻列表

三、浏览器工作原理

浏览器的组成

  • 人机交互部分(UI)
  • 网络请求部分(Socket)
  • JavaScript引擎部分(解析执行JavaScript)
  • 渲染引擎部分(渲染HTML、CSS等)
  • 数据存储部分(cookie、HTML5中的本地存储LocalStorage、SessionStorage)

sqlite

主流渲染引擎

介绍

  1. 渲染引擎 又叫 排版引擎 或 浏览器内核。

  2. 主流的 渲染引擎 有

    • Chrome浏览器: Blink引擎(WebKit的一个分支)。
    • Safari浏览器: WebKit引擎,windows版本2008年3月18日推出正式版,但苹果已于2012年7月25日停止开发Windows版的Safari。
    • FireFox浏览器: Gecko引擎。
    • Opera浏览器: Blink引擎(早期版使用Presto引擎)。
    • Internet Explorer浏览器: Trident引擎。
    • Microsoft Edge浏览器: EdgeHTML引擎(Trident的一个分支)。

工作原理

  1. 解析HTML构建Dom树(Document Object Model,文档对象模型),DOM 是W3C组织推荐的处理可扩展置标语言的标准编程接口。
  1. 构建渲染树渲染树并不等同于Dom树,因为像head标签 或 display: none这样的元素就没有必要放到渲染树中了,但是它们在Dom树中。

  2. 渲染树进行布局,定位坐标和大小、确定是否换行、确定position、overflow、z-index等等,这个过程叫"layout" 或 "reflow"

  3. 绘制渲染树,调用操作系统底层API进行绘图操作。

渲染引擎工作原理示意图

渲染引擎工作原理示意图

渲染引擎工作原理

WebKit工作原理(Chrome、Safari、Opera)

Blink渲染引擎工作原理

Gecko工作原理(FireFox)

Gecko渲染引擎工作原理

浏览器的 reflow 或 layout 过程

https://www.youtube.com/watch?v=ZTnIxIA5KGw

打开 Chrome 的 Rendering 功能

第一步:

第一步

第二步:

第二步

浏览器访问网站过程

  1. 在浏览器地址栏中输入网址。

淘宝网址

  1. 浏览器通过用户在地址栏中输入的URL构建HTTP请求报文。
1
2
3
4
5
6
7
8
9
GET / HTTP/1.1
Host: www.taobao.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: l=Ag0NWp9E8X4hgaGEtIBhOmKxnSOH6kG8; isg=AkZGLTL-Yr9tHDZbgd5bsn4Rlzwg5IphaK-1BzBvMmlEM-ZNmDfacSyDfdgF; thw=cn
  1. 浏览器发起DNS解析请求,将域名转换为IP地址。

淘宝网址

  1. 浏览器将请求报文发送给服务器。
  1. 服务器接收请求报文,并解析。
  1. 服务器处理用户请求,并将处理结果封装成HTTP响应报文。
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
HTTP/1.1 200 OK
Server: Tengine
Date: Thu, 13 Apr 2017 02:24:25 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Vary: Ali-Detector-Type, X-CIP-PT
Cache-Control: max-age=0, s-maxage=300
Via: cache8.l2cm10-1[172,200-0,C], cache13.l2cm10-1[122,0], cache3.cn206[0,200-0,H], cache6.cn206[0,0]
Age: 293
X-Cache: HIT TCP_MEM_HIT dirn:-2:-2
X-Swift-SaveTime: Thu, 13 Apr 2017 02:19:32 GMT
X-Swift-CacheTime: 300
Timing-Allow-Origin: *
EagleId: 9903e7e514920502659594264e
Strict-Transport-Security: max-age=31536000
Content-Encoding: gzip

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<title>淘宝网 - 淘!我喜欢</title>
<meta name="spm-id" content="a21bo" />
<meta name="description" content="淘宝网 - 亚洲较大的网上交易平台,提供各类服饰、美容、家居、数码、话费/点卡充值… 数亿优质商品,同时提供担保交易(先收货后付款)等安全交易保障服务,并由商家提供退货承诺、破损补寄等消费者保障服务,让你安心享受网上购物乐趣!" />
<meta name="aplus-xplug" content="NONE">
<meta name="keyword" content="淘宝,掏宝,网上购物,C2C,在线交易,交易市场,网上交易,交易市场,网上买,网上卖,购物网站,团购,网上贸易,安全购物,电子商务,放心买,供应,买卖信息,网店,一口价,拍卖,网上开店,网络购物,打折,免费开店,网购,频道,店铺" />
</head>
<body>
......
</body>
</html>
  1. 服务器将HTTP响应报文发送给浏览器。
  1. 浏览器接收服务器响应的HTTP报文,并解析。
  1. 浏览器解析 HTML 页面并展示,在解析HTML页面时遇到新的资源需要再次发起请求。
  1. 最终浏览器展示出了页面

HTTP请求报文和响应报文格式

http请求报文和响应报文

DNS 解析过程

DNS解析过程

windows 下 hosts 文件位置

C:\Windows\System32\drivers\etc\hosts

DOM 解析

参考代码:

1
2
3
4
5
6
<html>
<body>
<p>Hello World</p>
<div> <img src="example.png" alt="example"/></div>
</body>
</html>

Dom 解析工作原理

Webkit CSS 解析

CSS 解析工作原理

How Browsers work - 浏览器是如何工作的

How Browsers work
https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/

四、Web开发本质

牢记以下三点

  1. 请求,客户端发起请求。
  2. 处理,服务器处理请求。
  3. 响应,服务器将处理结果发送给客户端

对比一个单机版计算器和Web版计算器

客户端处理响应

  • 服务器响应完毕后,客户端继续处理:
    • 浏览器:解析服务器返回的数据
    • iOS、Android 客户端,解析服务器返回的数据,并且通过iOS或Android的UI技术实现界面的展示功能

关于 C/S(Client/Server) 和 B/S(Browser/Server)

C/S: 客户端服务器

B/S: 浏览器服务器

Client / Server

五、node.js 介绍

node.js 是什么?

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

PHP开发技术栈: LAMP - Linux Apache MySQL PHP

node.js 全栈开发技术栈: MEAN - MongoDB Express Angular Node.js

node.js 有哪些特点?

  1. 事件驱动(当事件被触发时,执行传递过去的回调函数)
  2. 非阻塞 I/O 模型(当执行I/O操作时,不会阻塞线程)
  3. 单线程
  4. 拥有世界最大的开源库生态系统 —— npm。

node.js 网站

  1. node.js官方网站
  2. node.js中文网
  3. node.js 中文社区

为什么要学习Node.js?

  1. 通过学习Node.js开发深入理解服务器开发Web请求和响应过程了解服务器端如何与客户端配合
  2. 学习服务器端渲染
  3. 学习服务器端为客户端编写接口
  4. 现在前端工程师面试,对 Node.js 开发有要求
  5. 补充提问:
    • 在Node.js平台开发时,能使用Dom API吗?比如:document.getElementById('id'); window.location 等?
  6. 复习 浏览器端 JavaScript 组成:ECMAscript、Dom、Bom

学习目标

  1. 了解服务器开发过程
  2. 会使用 node.js 开发基本的 http 服务程序(Web应用程序)

Node.js安装和配置

  1. 下载地址

  2. 官网术语解释

    • LTS 版本:Long-term Support 版本,长期支持版,即稳定版。
    • Current 版本:Latest Features 版本,最新版本,新特性会在该版本中最先加入。
  3. 注意:

    • 安装完毕后通过命令:node -v来确定是否安装成功【注意:打开”命令窗口”的时候建议使用”管理员方式”打开】
    • 如果需要则配置环境变量。

配置环境变量

  1. 通过 nvm-windows 管理一台计算机上的多个 node 版本

Node.js 开发 Web 应用程序 和 PHP、Java、ASP.Net等传统模式开发Web应用程序区别

  1. 传统模式
    • 有 Web 容器

有Web容器开发模型

  1. Node.js开发Web应用程序
    • 没有 Web 容器

Node.js无Web容器开发模型

  1. 补充提问:
  • 什么是动态网页?什么是静态网页?

六、基于nodejs编写程序(在 node.js 上编写程序)

REPL介绍

  1. REPL 全称: Read-Eval-Print-Loop(交互式解释器)

    • R 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
    • E 执行 - 执行输入的数据结构
    • P 打印 - 输出结果
    • L 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
  2. 在REPL中编写程序 (类似于浏览器开发人员工具中的控制台功能)

    • 直接在控制台输入 node 命令进入 REPL 环境
  3. 按两次 Control + C 退出REPL界面 或者 输入 .exit 退出 REPL 界面

    • 按住 control 键不要放开, 然后按两下 c 键

创建 JavaScript 文件编写程序

编程注意事项

  • 配置一下Sublime Text 的代码缩进格式为2个空格
  • 方式一

    @配置方式1

  • 方式二

    @配置方式2 - 第一步

    配置方式2 - 第二步

JavaScript 文件名命名规则

  • 不要用中文
  • 不要包含空格
  • 不要出现node关键字
  • 建议以 ‘-‘ 分割单词

案例

  1. 案例1:编写一个简单的函数, 实现数字相加
1
2
3
4
5
6
7
8
9
10
var n = 10;
var m = 100;

function add(x, y) {
return x + y;
}

var result = add(m, n);

console.log('计算结果是:' + result);
  1. 案例2:编写一个输出’三角形’的程序
1
2
3
4
5
6
7
8
9
10
11
12

// process 对象是一个 global (全局变量),提供有关信息,控制当前 Node.js 进程。
// 作为一个对象,它对于 Node.js 应用程序始终是可用的,故无需使用 require()。

for (var i = 0; i < 10; i++) {
for (var j = 0; j <= i; j++) {
// 注意:console.log()输出完毕后是带换行的,所以这样做不可以
// console.log('*');
process.stdout.write('* ');
}
process.stdout.write('\n');
}
  1. 案例3:文件读写案例(带同学们打开官方文档查阅)

    • 使用到的模块var fs = require('fs');

    • 1、写文件:fs.writeFile(file, data[, options], callback);

      • 参数1:要写入的文件路径,必填
      • 参数2:要写入的数据,必填
      • 参数3:写入文件时的选项,比如:文件编码,选填。
      • 参数4:文件写入完毕后的回调函数,必填
      • 写文件注意:
        • 该操作采用异步执行
        • 如果文件已经存在则替换掉
        • 默认写入的文件编码为utf8
        • 回调函数有1个参数:err,表示在写入文件的操作过程中是否出错了。
          • 如果出错了err != null,否则 err === null
    • 2、读文件:fs.readFile(file[, options], callback)

      • 参数1:要读取的文件路径,必填
      • 参数2:读取文件时的选项,比如:文件编码。选填。
      • 参数3:文件读取完毕后的回调函数,必填
      • 读文件注意:
        • 该操作采用异步执行
        • 回调函数有两个参数,分别是err和data
        • 如果读取文件时没有指定编码,那么返回的将是原生的二进制数据;如果指定了编码,那么会根据指定的编码返回对应的字符串数据。
    • 注意:
      • 文件操作中的./表示当前路径,相对的是执行node命令的路径,而不是当前被执行的*.js文件的实际路径。
      • __dirname才永远表示当前被执行的*.js文件的实际路径
      • /表示根目录, 读取文件或写入文件的时候写/目录,在Windows下相当于当前磁盘根目录(比如:c:\ 或 d:\ 或 e:\ 等,在Mac下相当于硬盘根目录 /
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
// --------------------------------- 写文件 -----------------------------
// 加载文件操作模块
var fs = require('fs');

// 创建要写入的文件的字符串
var msg = '你好,世界!你好 Node.js.';
// 执行文件写入操作
fs.writeFile('./data.txt', msg, 'utf8', function (err) {
console.log('---' + err + '----');
// /判断是否写入文件出错了
if (err) {
console.log('文件写入出错了,详细错误信息:' + err);
// 把错误继续向上抛出
throw err;
} else {
console.log('文件写入成功!');
}
});


// --------------------------------- 读文件 -----------------------------
// 加载文件操作模块
var fs = require('fs');

// 执行文件读取操作
fs.readFile('./data.txt', 'utf8', function (err, data) {
// 输出err 和 data
// console.log('error: ' + err);
// console.log('data: ' + data);

if (err) {
console.log('文件读取出错啦!详细信息: ' + err);
} else {
console.log('文件读取成功,以下是文件内容:');
console.log(data);
}
});
  1. 案例4:创建目录案例
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
// 创建一个文件夹


// 加载文件操作模块
var fs = require('fs');

// 创建一个目录
fs.mkdir('./test-mkdir', function (err) {
if (err) {
console.log('创建目录出错了,详细信息如下:');
console.log(err);
} else {
console.log('目录创建成功!');
}

});



// ----------------------------------------------------------

// 加载文件操作模块
var fs = require('fs');

// 1. 创建 '01-教学资料' 目录
fs.mkdir('./01-教学资料', function (err) {

if (err) {
throw err;
}

// 1.1 创建 '01-笔记大纲' 目录
fs.mkdir('./01-教学资料/01-笔记大纲');

// 1.2 创建 '02-作业任务' 目录
fs.mkdir('./01-教学资料/02-作业任务');

// 1.3 创建 '03-素材资料' 目录
fs.mkdir('./01-教学资料/03-素材资料');

// 1.4 创建 '04-随堂笔记' 目录
fs.mkdir('./01-教学资料/04-随堂笔记');

});



// 2. 创建 '02-源代码' 目录
fs.mkdir('./02-源代码', function (err) {

if (err) {
throw err;
}

// 2.1 创建 '预习代码'目录
fs.mkdir('./02-源代码/预习代码');

// 2.2 创建 '课堂代码'目录
fs.mkdir('./02-源代码/课堂代码');
});


// 3. 创建 '03-视频' 目录
fs.mkdir('./03-视频');


// 4. 创建 '04-其他资料' 目录
fs.mkdir('./04-其他资料');

注意:

  1. 异步操作无法通过 try-catch 来捕获异常,要通过判断 error 来判断是否出错。
  2. 同步操作可以通过 try-catch 来捕获异常。
  3. 不要使用 fs.exists(path, callback) 来判断文件是否存在,直接判断 error 即可
  4. 文件操作时的路径问题
    • 在读写文件的时候 ‘./‘ 表示的是当前执行node命令的那个路径,不是被执行的js文件的路径
    • __dirname, 表示的永远是”当前被执行的js的目录”
    • __filename, 表示的是”被执行的js的文件名(含路径)”
  5. error-first 介绍(错误优先)

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

步骤:

  1. 加载http模块
  2. 创建http服务
  3. 为http服务对象添加 request 事件处理程序
  4. 开启http服务监听,准备接收客户端请求

注意:

  1. 浏览器显示可能是乱码,所以可以通过 res.setHeader('Content-Type', 'text/plain; charset=utf-8');设置浏览器显示时所使用的编码。

  2. Chrome 浏览器默认无法手动设置编码,需要安装”Set Character Encoding”扩展。

  3. 演示一下设置Content-Type=text/htmlContent-Type=text/plain的区别。

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// 1. 加载http模块
var http = require('http');

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

// 3. 开始监听'request'事件
// 详细解释一下request对象和response对象
server.on('request', function (req, res) {
// body...
console.log('有人请求了~~');
});

// 4. 启动服务,开始监听
server.listen(9000, function () {
console.log('服务已经启动,请访问: http://localhost:9000');
});

案例6:通过 node.js 编写 http 服务程序 - 根据不同请求作出不同响应

说明:
  • 根据不同请求,显示index页面、login页面、register页面、list页面。
  • 请求 / 或 /index
  • 请求 /login
  • 请求 /register
  • 请求 /list
参考代码
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
// 加载http模块
var http = require('http');

// 创建http server
var server = http.createServer(function (req, res) {
// body...
console.log(req.url);


if (req.url === '/' || req.url === '/index') {
// 表示请求网站首页
res.end('这是 index.html');

} else if (req.url === '/login') {
// 表示请求登录页面
res.end('这是 login.html');

} else if (req.url === '/register') {
// 表示请求注册页面
res.end('这是 register.html');

} else if (req.url === '/list') {
// 表示请求列表页面
res.end('这是 list.html');

} else {
// 表示请求的页面不存在
res.writeHead(404, 'Not Found');
res.end('Sorry, page not found.');
}
});

// 监听端口的网络请求
server.listen(9000, function () {
console.log('http://localhost:9000');
});

案例7:通过 node.js 编写 http 服务程序 - 通过读取静态 HTML 文件来响应用户请求

步骤:

  1. 创建index.html、login.html、register.html、list.html、404.html文件。
  2. 演示通过读取最简单的 HTML 文件来响应用户。
  3. 演示通过读取”具有引入外部CSS样式表”的HTML文件来响应用户。
  4. 演示通过读取”具有img标签”的HTML文件来响应用户。

注意:

  • 1、注意在发送不同类型的文件时,要设置好对应的Content-Type

  • 2、HTTP状态码参考

  • 3、在html页面中写相对路径’./‘ 和 绝对路径 ‘/‘的含义 。

    • 网页中的这个路径主要是告诉浏览器向哪个地址发起请求用的
    • ‘./‘ 表示本次请求从相对于当前页面的请求路径(即服务器返回当前页面时的请求路径)开始
    • ‘/‘ 表示请求从根目录开始

补充知识点:

  1. path 模块的 join() 方法

参考代码:

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

// 1. 加载 http 模块
var http = require('http');
// 加载文件操作模块
var fs = require('fs');
// 加载path模块,这个模块主要用来处理各种路径。
var path = require('path');



// 2. 创建http server
var server = http.createServer(function (req, res) {
// 1. 获取用户请求的URL
var url = req.url.toLowerCase();

// 2. 根据用户的不同请求,做出不同响应
if (url === '/' || url === '/index') {
// 读取index.html文件,把该文件响应给用户
fs.readFile(path.join(__dirname, 'index.html'), function (err, data) {
if (err) {
throw err;
}
res.writeHead(200, 'OK', {
'Content-Type': 'text/html; charset=utf-8'
});
// res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(data);

});
} else if (url === '/login') {
// 读取login.html文件,把该文件响应给用户
fs.readFile(path.join(__dirname, 'login.html'), function (err, data) {
if (err) {
throw err;
}
res.writeHead(200, 'OK', {
'Content-Type': 'text/html; charset=utf-8'
});
// res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(data);

});
} else if (url === '/register') {
// 读取register.html文件,把该文件响应给用户
fs.readFile(path.join(__dirname, 'register.html'), function (err, data) {
if (err) {
throw err;
}
res.writeHead(200, 'OK', {
'Content-Type': 'text/html; charset=utf-8'
});
// res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(data);

});
} else if (url === '/404') {
// 读取register.html文件,把该文件响应给用户
fs.readFile(path.join(__dirname, '404.html'), function (err, data) {
if (err) {
throw err;
}
res.writeHead(200, 'OK', {
'Content-Type': 'text/html; charset=utf-8'
});
// res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(data);

});
}

});



// 3. 启动服务
server.listen(9090, function () {
// body...
console.log('please visit: http://localhost:9090');
});

案例8:模拟 Apache 实现静态资源服务器

步骤:

  • 单独创建一个目录来实现,比如:创建一个”07-Apache”的目录。
  • 在该目录下新建 public 目录,假设该目录为静态资源目录。
  • 根据用户请求的路径在 public 目录下寻找对应路径下的资源。
  • 如果找到了,那么将该资源返回给用户,如果没找到则返回404错误。
  • 通过 mime 模块设置不同类型资源的Content-Type
  • 实现完毕后把素材中的’An Ocean of Sky’ 和 ‘Hacker News’分别拷贝到静态资源目录下, 测试是否成功

其他:

  • 介绍 NPM
  • 介绍 mime 第三方模块
    • npm install mime
    • 在代码中直接 var mime = require('mime')

参考代码:

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

// 1. 加载对应模块
// 1.1 加载http模块
var http = require('http');
// 1.2 加载path模块,方便路径拼接
var path = require('path');
// 1.3 加载文件读取模块
var fs = require('fs');
// 1.4 加载判断文件MIME类型的模块
var mime = require('mime');


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


// 3. 监听用户request事件
server.on('request', function (req, res) {
// 1. 获取用户的请求路径, 并转换为小写
var url = req.url.toLowerCase();

// 判断如果请求的路径是 '/' 那么等价于 '/index.html'
url = (url === '/') ? '/index.html' : url;

// 2. 根据用户请求的url路径, 去public目录下查找对应的静态资源文件。找到后读取该文件,并将结果返回给用户
// 2.1 根据用户请求的url拼接本地资源文件的路径
var filePath = path.join(__dirname, 'public', url);

// 2.2 根据请求的文件路径设置Content-Type
res.setHeader('Content-Type', mime.lookup(url));

// 2.2 根据路径去读取对应的文件
// 【注意】读取文件前无需判断文件是否已经存在,而是在读取文件的回调函数中根据error的错误信息来判断读取文件是否成功以及发生的错误
fs.readFile(filePath, function (err, data) {
// 判断是否有错误
if (err) {

if (err.code === 'ENOENT') { // 判断是否是请求的文件是否不存在

res.setHeader('Content-Type', 'text/html; charset=utf8');
res.statusCode = 404;
res.statusMessage = 'Not Found';
res.end('<h1>请求的资源不存在!</h1>');

} else if (err.code === 'EACCES') { // 判断文件是否有访问权限

res.setHeader('Content-Type', 'text/html; charset=utf8');
res.statusCode = 403;
res.statusMessage = 'Forbidden';
res.end('<h1>Permission denied!</h1>');
} else {

throw err;
}

} else {

// 如果没有错误则将读取到的文件返回给用户
res.statusCode = 200;
res.statusMessage = 'OK';
res.end(data);
}
})
});



// 4. 启动服务
server.listen(9000, function () {
// body...
console.log('server is running, please visit: http://localhost:9000');
});

Common System Errors - 常见错误号

  • EACCES (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.
    • 给定的路径不是目录

同步文件操作 和 异步文件操作

  • fs.readFile(file[, options], callback)
  • fs.readFileSync(file[, options])

通过设置 http 响应报文头实现弹框下载功能

  1. 设置 Content-Type: application/octet-stream
  2. 设置 Content-Disposition: attachment; filename=demo.txt

七、node.js 学习资源

1. 图书

  • 《深入浅出Node.js》 作者:朴灵
  • 《node.js 实战 中国程序员6》

2. 网站资源

3. Node.js 使用场景 & 实战

八、Node Version Manager(Node 版本管理器)

nvm (Linux、Unix、OS X)

nvm-windows (Windows)

九、Buffer

思考:Buffer 类型产生的原因?主要用来解决什么问题?

看一下什么是 Buffer? 什么是 Stream?

一、类型介绍

  1. JavaScript 语言没有读取或操作二进制数据流的机制。
  2. Node.js 中引入了 Buffer 类型使我们可以操作 TCP流 或 文件流。
  3. Buffer 类型的对象类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。 Buffer 的大小在被创建时确定,且无法调整。( buf.length 是固定的,不允许修改 )
  4. Buffer 是全局的,所以使用的时候无需 require() 的方式来加载

二、如何创建一个 Buffer 对象

常见的 API 介绍

  1. 创建一个 Buffer 对象
1
2
3
4
5
6
7
8
9
10
11
12
// 1. 通过 Buffer.from() 创建一个 Buffer 对象

// 1.1 通过一个字节数组来创建一个 Buffer 对象
var array = [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c];
var buf = Buffer.from(array);
console.log(buf.toString('utf8'));

// 1.2 通过字符串来创建一个 Buffer 对象
// Buffer.from(string[, encoding])
var buf = Buffer.from('你好世界! Hello World!~');
console.log(buf);
console.log(buf.toString());
  1. 拼接多个 Buffer 对象为一个对象
1
2
3
// Buffer.concat(list[, totalLength])
var bufferList = [];
var buf = Buffer.concat(bufferList);
  1. 获取字符串对应的字节个数
1
2
3
4
// Buffer.byteLength(string[, encoding])

var len = Buffer.byteLength('你好世界Hello', 'utf8');
console.log(len);
  1. 判断一个对象是否是 Buffer 类型对象
1
2
3
4
// Buffer.isBuffer(obj)
// obj <Object>
// Returns: <boolean>
// Returns true if obj is a Buffer, false otherwise.
  1. 获取 Buffer 中的某个字节
1
2
// 根据索引获取 Buffer 中的某个字节(byte、octet)
// buf[index]

6、获取 Buffer 对象中的字节的个数

1
2
// buf.length
// 注意:length 属性不可修改
  1. 已过时的 API
1
2
3
4
5
6
// 以下 API 已全部过时
new Buffer(array)
new Buffer(buffer)
new Buffer(arrayBuffer[, byteOffset [, length]])
new Buffer(size)
new Buffer(string[, encoding])

三、Buffer 对象与编码

Node.js 目前支持的编码如下:

  1. ascii
  2. utf8
  3. utf16le
    • ucs2 是 utf16le 的别名
  4. base64
  5. latin1
    • binary 是 latin1 的别名
  6. hex
    • 用两位 16 进制来表示每个字节

示例代码:

1
2
3
4
5
6

var buf = Buffer.from('你好世界,Hello World!', 'utf8');

console.log(buf.toString('hex'));
console.log(buf.toString('base64'));
console.log(buf.toString('utf8'));

四、思考:为什么会有 Buffer 类型?

  1. Buffer 使用来临时存储一些数据(二进制数据)
  2. 当我们要把一大块数据从一个地方传输到另外一个地方的时候可以通过 Buffer 对象进行传输
  3. 通过 Buffer 每次可以传输小部分数据,直到所有数据都传输完毕。

五、补充

  1. Stream

  2. Writable Stream

    • 允许 node.js 写数据到流中
  3. Readable Stream

    • 允许 node.js 从流中读取数据

十、day1总结

1、浏览器的基本概念

  • 浏览器组成部分
  • 浏览器的渲染引擎工作原理
  • 浏览器访问服务器全过程
  • dns解析过程

2、node.js 介绍

  • node.js 是什么
  • node.js 特点
  • node.js 能做什么

3、通过 node.js 编写代码

  • REPL
  • 通过创建 .js 文件来编写代码

4、通过 fs 模块进行文件读写

  • dirname、filename
  • path 模块,用来对文件路径进行操作(path.join() 拼接路径)

5、全局模块

  • process
  • console

6、创建 http 服务

  • 加载 http 模块
  • 创建 http 服务
  • 监听 request 事件
  • 开启服务

7、在用户的 request 事件中,必须要结束请求 。 res.end()

8、服务器端通过用户不同请求(req.url),做出不同响应

-------------本文结束感谢您的阅读-------------
0%