0 NodeJS背景

0.1 NodeJS是什么

我们先看看官网上如何评价

  • Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
  • Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。

首先为什么说是基于Chrome V8 引擎?
因为在此之前js只能作为前端开发,并且运行在浏览器中。直到Chrome V8 引擎出现才把js分离出来运行在后台。其中Chrome V8 引擎可以理解为JVM,是js的运行环境。

0.2 NodeJS和JS有什么区别

其实两者底层基本一样,语法都是基于ECMAScript。

  • NodeJS
    1. 没有BOM和DOM操作。
    2. 提供了一些os(操作系统)、file system(文件系统)、net(网络系统)、database(数据库)的API。
    3. 直接运行在命令窗口,需要NodeJS环境。
  • WebJS
    1. 具有BOM和DOM操作。
    2. 不具有其他的服务端API。
    3. 运行在浏览器上。

我们可以在这里查看Node的API。

0.3 NodeJS安装

去官网下载之后下一步下一步即可
运行脚本方法:

node 文件.js

1 NodeJS基本用法

1.1 NodeJS读取文件

在操作文件之前我们先引用包(类似于java中引用jar包)

var fs = require(‘fs’)

这里的fs就是我们的操作文件包

  1. 写文件
    拿到fs对象后调用writeFile方法
    具体代码参考如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var fs = require('fs');
    // 第一个参数:文件路径
    // 第二个参数:文件内容
    // 第三个参数:回调函数
    // 成功时:error是null
    // 失败时:error是错误对象
    fs.writeFile('./data/test.md',"hello word",function(error) {
    console.log('文件写入成功')
    })
  2. 读文件
    拿到fs对象后调用readFile方法
    具体代码参考如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var fs = require('fs');
    // 第一个参数:文件路径
    // 第二个参数:回调函数
    // 成功时:error是null,data是对象
    // 失败时:error是错误对象,data是空
    fs.readFile('./data/test.md',function(error,data) {
    // 这里返回的是二进制数据,我们需要通过toString方法把其转换为我们认识的字符
    if(error){
    console.log('文件读取失败了')
    }else{
    console.log(data.toString())
    }
    })

1.2 HTTP核心模块

  1. 加载http核心模块
  2. 使用htto.createServer()方法创建一个web服务器,返回一个Server实例
  3. 服务器可以接收请求和发送请求
    • 通过request.url判断请求路径
    • 通过response.write()来发送相应数据(一定要使用end结束,否则客户端会一直等待
  4. 绑定端口号,启动服务

我们参考下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 加载http核心模块
var http = require('http')
// 使用http.createSrever()方法创建一个web服务器,返回一个Server实例
var server = http.createServer();
// 服务器可以接收请求和发送请求
server.on('request',function(request,response) {
console.log('收到客户端的请求了');
// 通过request.url判断请求路径
console.log('收到客户端的请求路径是'+request.url);
// 发送返回数据
response.wirte('hello');
// 结束会话
response.end();
// 上面的方法也可以这么写 response.end('hello');
})
// 绑定端口号,启动服务
server.listen(3000,function() {
console.log('服务启动了');
})

根据不同路径判断输出不同的值
响应只能是字符串或二进制,所以我们需要把JSON转成字符串

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
// 加载http核心模块
var http = require('http')
// 使用http.createSrever()方法创建一个web服务器,返回一个Server实例
var server = http.createServer();
// 服务器可以接收请求和发送请求
server.on('request',function(request,response) {
var url = request.url;
if(url === '/'){
response.end('index')
}else if(url === '/login'){
response.end('login')
}else if(url === '/products'){
var products = [
{
name:'12',
price: 12,
},
{
name:'22',
price: 22,
},
]
// 响应只能是字符串或二进制,所以我们需要把JSON转成字符串
response.end(JSON.stringify(products));
}else{
response.end('404')
}
})
// 绑定端口号,启动服务
server.listen(3000,function() {
console.log('服务启动了');
})


1.3 其他API

  1. 获取机器信息
    直接看代码

    1
    2
    3
    4
    5
    6
    //用来获取机器信息的
    var os = require('os');
    //获取当前机器的cpu
    console.log(os.scpus());
    //获取内存
    console.log(os.totalmem());
  2. 操作路径
    直接看代码

    1
    2
    3
    4
    //用来获取机器信息的
    var path = require('path');
    //获取一个路径中的扩展部分
    console.log(path.extname('c:/a/b/c/d/hello.text'));

2 Node中的几点说明

2.1 模块化编程

首先现在js无论前端还是后端都是模块化编程,ES6中用importexport来进行模块导入导出
在nodeJS中我们用requireexport来实现导入导出

  1. require是一个方法,它是用来加载模块的
  2. node中有两种模块
    • 具名的核心模块(fs,http)
    • 用户自己编写的的文件模块
  3. 相对路径必须加./
    • 不过可以省略后缀名(.js)
  4. 在Node中,没有全局作用域,只有模块作用于
    • 外部访问不到内部
    • 内部也访问不到外部
  5. exports可以把所需要的被外部访问的成员在此定义
    • exports默认是一个空对象
      我们来看一个例子,创建两个js(a.js,b.js)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      var foo = 'aaa'
      console.log('a start')
      function add(x,y){
      return x + y;
      }
      var ret = require('./b,js')
      console.log(ret.foot) // 调用b中变量
      console.log(ret.add(10,30)) // 调用b中变量
      console.log('a end')
1
2
3
4
5
6
7
console.log('b start')
// console.log(add(10,20)) 这个是错误的,因为b中无法调用a中方法
exports.foo='hello'; // 交给exports让外部可以调用
exports.add = function(x,y){
return x + y;
}
console.log('b end')

2.2 模块化编Content-Type

在我们发送返回消息(response)的时候,我们返回给浏览器信息时通常加上请求头

response.setHeader(‘Content-Type’,’text/html; charset=utf-8’)

  1. Content-Type我们可以理解为header的Key
  2. text/html传输的文本类型
  3. charset=utf-8传输的字节编码,这里为utf-8

这时候我们可以再浏览器返回值值中查看如图所示:
如图所示

请看下面一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 加载http核心模块
var http = require('http')
// 使用http.createSrever()方法创建一个web服务器,返回一个Server实例
var server = http.createServer();
// 服务器可以接收请求和发送请求
server.on('request',function(request,response) {
var url = request.url;
if(url === '/plain'){
// text/plain就是普通文本
res.setHeader('Content-Type','text/plain; charset=utf-8');
response.end('hello 世界')
}else if(url === '/html'){
// text/html就是html标记语言,浏览器会进行渲染
res.setHeader('Content-Type','text/html; charset=utf-8');
response.end('<p>hello world <a herf="">点击</a></p>')
}else{
response.end('404')
}
})
// 绑定端口号,启动服务
server.listen(3000,function() {
console.log('服务启动了');
})

而我们通查不把HTML写在js中,而是通过fs模块进行读取.html

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
// 加载http核心模块
var http = require('http')
var fs = require('fs')
// 使用http.createSrever()方法创建一个web服务器,返回一个Server实例
var server = http.createServer();
// 服务器可以接收请求和发送请求
server.on('request',function(request,response) {
var url = request.url;
if(url === '/'){
fs.readFile('./resource/index.html',function(err,data) {
if(err){
res.setHeader('Content-Type','text/plain; charset=utf-8');
response.end('文件读取失败')
}else{
res.setHeader('Content-Type','text/html; charset=utf-8');
response.end(data)
}
})
}else{
response.end('404')
}
})
// 绑定端口号,启动服务
server.listen(3000,function() {
console.log('服务启动了');
})

Content-Type的更多类型请点击这里

3 NodeJs实现服务器

3.1 访问路径访问本地数据

这里我们可以通过http获取访问的路径,之后用fs来读取我们该路径下固定文件夹下的具体文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var http = require('http')
var fs = require('fs')
var server = http.createServer();
// 定义访问路径
var wwwDir = 'D:/Movie/www'
server.on('request',function(request,response) {
var url = request.url;
var filePath = '/index.html';
if(url !== '/'){
filePath = url;
}
fs.readFile(wwwDir + filePath,function(err,data) {
if(err){
res.setHeader('Content-Type','text/plain; charset=utf-8');
response.end('文件读取失败')
}else{
res.setHeader('Content-Type','text/html; charset=utf-8');
response.end(data)
}
})
})
server.listen(3000,function() {
console.log('服务启动了');
})

3.2 替换模板文件

要做到替换模板文件我们需要做两个操作

  1. 得到wwwDir目录列表中的文件名和目录名(fs.readdir)
  2. 将得到的文件名和目录名替换到template.html中
    • 在template.html中需要替换的位置预留一个特殊标记
    • 根据files生成需要的HTML内容

我们创建一个.html,下面是一段代码片段

1
2
3
<td data-value="apple/">
<a class="icon dir" herf="D:/Movie/www/apple/">^-^</a>
</td>

我们把^-^特殊标记替换成我们自己想要的名字
可以用replace替换

1
2
3
data = data.toString()
data = data.replace('^-^','苹果')
res.end(data)

3.3 Art-Template

我们用art-template插件来完成替换功能,官网
art-template既可以用在web上也可以用在Nodejs中
art-template是将文件看成字符串,即使在js中的模板也会被替换掉,至于执行js那是浏览器的事儿

  1. 安装
    • npm install art-template
    • 该命令在哪里执行就会把包下载到哪里。默认下载到node_moudules目录中
  2. web引用
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>111</title>
</head>
<body>
<!--注意:在浏览器中需要引用/lib/template-web.js文件-->
<script src="node_modules/art-template/lib/template-web.js>"
<script type="text/template" id="tp1">
名字: {{ name }}
年龄: {{ age }}
来自: {{ provice }}
// 这里是循环
喜欢: {{each hobbies}} {{$value}} {{/each}}
</script>
<script>
var ret = template('tp1',{
name : 'Jack',
age: 18,
provice: '北京',
hobbies:['写代码','唱歌','打游戏']
})
console.log(ret)
</script>
</body>
</html>
  1. Node引用
    • 在需要使用的地方加载art-template模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var template = require('art-template')
var tplStr = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>111</title>
</head>
<body>
<p>名字: {{ name }}</p>
<p>年龄: {{ age }}</p>
<p>来自: {{ provice }}</p>
<p>喜欢: {{each hobbies}} {{$value}} {{/each}}</p>
</body>
</html>`

var ret = template(tplStr,{
name : 'Jack',
age: 18,
provice: '北京',
hobbies:['写代码','唱歌','打游戏']
})
console.log(ret)

3.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
var http = require('http')
var fs = require('fs')
var template = require('art-template')
var server = http.createServer();
// 定义访问路径
var wwwDir = 'D:/Movie/www'
server.on('request',function(request,response) {
var url = request.url;
fs.readFile('./template',function(err,data) {
if(err){
res.setHeader('Content-Type','text/plain; charset=utf-8');
response.end('404 Not Found')
}else{
res.setHeader('Content-Type','text/html; charset=utf-8');
fs.readdir(wwwDir,function(err,files) {
if(err){
res.setHeader('Content-Type','text/plain; charset=utf-8');
response.end('Can not find wwDir.')
}
})
// 接下来就是文本替换了
var htmlStr = template.render(dara.toString(),{
// 要替换的文本
})
res.end(htmlStr)
}
})
})
server.listen(3000,function() {
console.log('服务启动了');
})

3.5 静态文件资源

作为我们的服务端,不可能让用户能访问我们所有的文件,所以我们需要管理哪些资源用户可以访问,哪些不可以,能访问的就是我们所谓的静态资源

当我们访问服务器发生了以下几个重大事件

1.浏览器服务器
2.服务器读取模板文件
3.模板文件替换后在浏览器中渲染
4.浏览器运行代码,当读到link,script等标签时需要再去服务器获取资源
5.服务器判断请求路径是否可以访问

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
var http = require('http')
var fs = require('fs')
var server = http.createServer();
// 定义访问路径
server.on('request',function(request,response) {
var url = request.url;
if(url === '/'){
fs.readFile('./views/index.html',function(err,data) {
if(err){
res.setHeader('Content-Type','text/plain; charset=utf-8');
response.end('文件读取失败')
}else{
res.setHeader('Content-Type','text/html; charset=utf-8');
response.end(data)
}
})
} else if(url.indexOf('/public/') === 0 ){
// 判断当访问路径含有/public/
fs.readFile('.'+ url)
}

})
server.listen(3000,function() {
console.log('服务启动了');
})

这里还需要说明一下,我们在模板引擎中的link,script标签,引用地址应该是一个相对路径一个url地址,而不是绝对的了
因为在浏览器真正发送请求的时候会把http://127.0.0.1:3000拼上

1
<link rel="stylesheet" href="/public/lib/...."

同理我们的a标签引用如果是页面内部调整也需要考虑到服务器,比如跳转到首页我们可以这样写

1
<a href="/"></a>

4 案例整合

通过这些知识,我们用一个留言板的案例来整合所学习的知识

4.1 HTML模板部分

我们用Art-Template来作为我们的模板

  1. 消息列表页面
1
2
3
4
5
6
7
8
9
<div class="comments container">
<ul class="list-group">
{{each comments}}
<li class="list-group-item">
{{$value.name}}说{{$value.message}}
<span class="pill-right">{{$value.dataTime}}</span>
</li>
</ul>
</div>
  1. 添加消息页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 表单中需要提交的元素必须具有name属性
// 1.默认提交行为
// action 就是表单提交的地址,说白了就是url的请求地址
// method 请求方法
// 2.表单异步提交(ajax)
<from action="/pinglun" method="get">
<div class="from-group">
<label for="input_name">你的名字</label>
<input type="text" class="form-control" required minlength="2" maxlength="10"
id="input_name" placeholder="请输入姓名" name="name">
</div>
<div class="form-group">
<label for="textarea_message">留言</label>
<textarea class="form-control" name="message" id="textarea_message" cols="30" rows="10"
required minlength="5" maxlength="20"></textarea>
</div>
<button type="submit" class="btn btn-default">发表</button>
</from>

4.2 url.parse()

url.parse()用来解析url和参数
我们可以在命令行输入node进入node环境测试

  1. node
  2. url.parse(‘http://127.0.0.1:3000/pinglun?name=zhangsan&message=1234456')

结果如图所示如图所示

  1. url.parse(‘http://127.0.0.1:3000/pinglun?name=zhangsan&message=1234456',true)

结果如图所示如图所示

可以看出url.parse不近能帮我们解析出url还能帮我们解析出参数

4.3 NodeJs部分

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
var http = require('http')
var fs = require('fs')
var template = require('art-template')
// 引入url解析
var url = require('url')
var server = http.createServer();

var comments = [
{
name:"张三",
message: '今天天气不错',
dataTime: '2015-10-16',
},
{
name:"张三2",
message: '今天天气不错',
dataTime: '2015-10-16',
},
{
name:"张三3",
message: '今天天气不错',
dataTime: '2015-10-16',
},
]

server.on(function(req,res) {
//使用url.parse方法将路径解析为一个方便操作的对象
var parseObj = url.parse(req.url,true);
//获取不包含字符串部分路径
var pathname = parseObj.pathname;
if(pathname === '/'){
fs.readFile('./views/index.html',function(err,data){
if(err){
return res.end('404 Not Found')
}
var htmlStr = template.render(data.toString(),{
comments:comments
})
res.end(htmlStr)
})
}
else if(pathname === '/pinglun'){
// 这里我们需要做到以下几步
// 1. 获取表单提交的数据
// 2. 生成日期到数据对象中,然后存储到数组中
// 3. 让用户重定向跳转到首页/
var comment = parseObj.query
comment.dataTime = '2018-12-12'
comments.push(comment);
//这时候服务端已经把数据存储好了,接下来让用户重定向到首页
//1. 状态码设置为302临时重定向
// statusCode
//2. 在响应头中通过Location告诉客户端往哪从定性
// setHeader
//如果客户端发现收到服务器端的响应状态码是302就会自动去响应头中找Location
//所以就能看到客户端自动跳转了
res.statusCode = 302;
res.setHeader('Location','/');
// 一次请求对应一次响应结束
res.end();
}
else{
// 其他的都处理成404找不到
fs.readFile('./vies/post.html',function(err,data) {
if(err){
return res.end('404 Not Found')
}
res.end(data)
})
}
})
server.listen(3000,function() {
console.log('服务启动了');
})

最后更新: 2018年12月16日 14:09

原始链接: http://linjiad.github.io/2018/12/15/NodeJs1/

× 请我吃糖~
打赏二维码