七天搞定node.js---第二天

这是学习node.js第二天的笔记。

  • 代码风格
  • 无分号
    • (
    • [
    • `
    • 最好前面补分号,避免一些问题
    • 《编写可维护的 JavaScript》
    • 不仅是功能,还要写的漂亮
  • 服务端渲染

    • 说白了就是在服务端使用模板引擎
    • 模板引擎最早诞生于服务端,后来才发展到了前端
  • 服务端渲染和客户端渲染的区别

    • 客户端渲染不利于 SEO 搜索引擎优化
    • 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
    • 所以你会发现真正的网站既不是纯异步也不是纯服务端渲染出来的
    • 而是两者结合来做的
    • 例如京东的商品列表就采用的是服务端渲染,目的了为了 SEO 搜索引擎优化
    • 而它的商品评论列表为了用户体验,而且也不需要 SEO 优化,所以采用是客户端渲染

代码风格

分号问题

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
function say() {
console.log('hello world')
}
//say()方法后面不加分号会报一个错误:TypeError: say(...) is not a function
say()

// ;
// (function() {
// console.log('你好,你好')
// })()

// ;
// ['苹果', '香蕉', '牛奶'].forEach(function(item) {
// console.log(item)
// })

// ` 是 EcmaScript 6 中新增的一种字符串包裹方式,叫做:模板字符串
// 它支持换行和非常方便拼接变量

;
`hello`.toString()


//当你采用了无分号的代码风格的时候,只需要注意以下情况就不会有上面的问题了:
// 当一行代码是以:
// (
// [
// `
// 开头的时候,则在前面补上一个分号用以避免一些语法解析错误。
// 结论:无论你的代码风格是否有分号,都建议如果一行代码是以 ( 、 [ 、 ` 开头的最好都在其前面补上分号;
// 也有些代码是花里胡哨的,前面不用分号,而是用 ~ & !等代替分号。

初步实现Apache(阿帕奇)功能01

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
var http = require('http')
var fs = require('fs')
// 1.创建 Server
var server = http.createServer()
// 2.监听 Server 的 request 请求事件,设置请求处理函数
// 请求
// 处理
// 响应
// 一个请求对应一个响应,如果在一个请求的过程中,已经结束响应了,则不能重复发送响应。
// 没有请求就没有响应。
server.on('request', function(req, res) {
// console.log(req.url)
var wwwDir = '../www'
var url = req.url
if (url === '/') {
fs.readFile(wwwDir + '/index.html', function(err, data) {
// if (err) {
// res.end('404 Not Found.')
// } else{
// }
if (err) {
// return 有两个作用:
// 1.方法返回值
// 2.阻止代码继续往后执行
return res.end('404 Not Found.')
}
res.end(data)

})
} else if (url === '/test1') {
fs.readFile(wwwDir + '/test1.txt', function(err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)

})
} else if (url === '/test2') {
fs.readFile(wwwDir + '/test2.txt', function(err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)

})
} else {
res.end('404 Not Found ~~~ .')
}
})
// 3.绑定端口号,启动服务
server.listen(3000, function() {
console.log('running ~ ~')
})

初步实现Apache功能02

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
var http = require('http')
var fs = require('fs')
// 1.创建 Server
var server = http.createServer()
var wwwDir = '../www'
// 2.监听 Server 的 request 请求事件,设置请求处理函数
// 请求
// 处理
// 响应
// 一个请求对应一个响应,如果在一个请求的过程中,已经结束响应了,则不能重复发送响应。
// 没有请求就没有响应。
server.on('request', function(req, res) {
// console.log(req.url)
var url = req.url

var filePath = '/index.html'
if (url !== '/') {
filePath = url
}
// console.log(filePath, wwwDir + filePath)
fs.readFile(wwwDir + filePath, function(err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})

})
// 3.绑定端口号,启动服务
server.listen(3000, function() {
console.log('running ~ ~')
})

Apache完成目录列表渲染

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
var http = require('http')
var fs = require('fs')

var server = http.createServer()

var wwwDir = 'D:/Movie/www'

server.on('request', function(req, res) {
var url = req.url
fs.readFile('./template.html', function(err, data) {
if (err) {
return res.end('404 Not Found.')
}
// 1. 如何得到 wwwDir 目录列表中的文件名和目录名
// fs.readdir
// 2. 如何将得到的文件名和目录名替换到 template.html 中
// 2.1 在 template.html 中需要替换的位置预留一个特殊的标记(就像以前使用模板引擎的标记一样)
// 2.2 根据 files 生成需要的 HTML 内容
// 只要你做了这两件事儿,那这个问题就解决了
fs.readdir(wwwDir, function(err, files) {
if (err) {
return res.end('Can not find www dir.')
}

// 2.1 生成需要替换的内容
var content = ''
files.forEach(function(item) {
// 在 EcmaScript 6 的 ` 字符串中,可以使用 ${} 来引用变量
content += `
<tr>
<td data-value="apple/"><a class="icon dir" href="/D:/Movie/www/apple/">${item}/</a></td>
<td class="detailsColumn" data-value="0"></td>
<td class="detailsColumn" data-value="1509589967">2017/11/2 上午10:32:47</td>
</tr>
`
})

// 2.3 替换
data = data.toString()
data = data.replace('^_^', content)

// 3. 发送解析替换过后的响应数据
res.end(data)
})
})
})
server.listen(3000, function() {
console.log('running...')
})
1
2
3
4
5
6
7
8
var fs = require('fs')

fs.readdir('D:/Movie/www', function(err, files) {
if (err) {
return console.log('目录不存在')
}
console.log(files)
})

在 node 中使用模板引擎

模板引擎不关心内容!

  • 在浏览器中使用模板引擎:
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>06-在浏览器中使用art-template</title>
</head>

<body>
<!--
注意:在浏览器中需要引用 lib/template-web.js 文件

强调:模板引擎不关心你的字符串内容,只关心自己能认识的模板标记语法,例如 {{}}
{{}} 语法被称之为 mustache 语法,八字胡啊。
-->
<script src="node_modules/art-template/lib/template-web.js"></script>
<script type="text/template" id="tpl">
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Document</title>
</head>

<body>
<p>大家好,我叫:{{ name }}</p>
<p>我今年 {{ age }} 岁了</p>
<h1>我来自 {{ province }}</h1>
<p>我喜欢:{{each hobbies}} {{ $value }} {{/each}}</p>
</body>

</html>
</script>
<script>
var ret = template('tpl', {
name: 'Jack',
age: 18,
province: '广东省',
hobbies: [
'写代码',
'唱歌',
'打游戏'
]
})

console.log(ret)
</script>
</body>

</html>
  • 在 node 中使用模板引擎:
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
// art-template
// art-template 不仅可以在浏览器使用,也可以在 node 中使用

// 安装:
// npm install art-template
// 该命令在哪执行就会把包下载到哪里。默认会下载到 node_modules 目录中
// node_modules 不要改,也不支持改。

// 在 Node 中使用 art-template 模板引擎
// 模板引起最早就是诞生于服务器领域,后来才发展到了前端。
//
// 1. 安装 npm install art-template
// 2. 在需要使用的文件模块中加载 art-template
// 只需要使用 require 方法加载就可以了:require('art-template')
// 参数中的 art-template 就是你下载的包的名字
// 也就是说你 isntall 的名字是什么,则你 require 中的就是什么
// 3. 查文档,使用模板引擎的 API


var template = require('art-template')
var fs = require('fs')

// 这里不是浏览器
// template('script 标签 id', {对象})

// var tplStr = `
// <!DOCTYPE html>
// <html lang="en">
// <head>
// <meta charset="UTF-8">
// <title>Document</title>
// </head>
// <body>
// <p>大家好,我叫:{{ name }}</p>
// <p>我今年 {{ age }} 岁了</p>
// <h1>我来自 {{ province }}</h1>
// <p>我喜欢:{{each hobbies}} {{ $value }} {{/each}}</p>
// </body>
// </html>
// `

fs.readFile('./tpl.html', function (err, data) {
if (err) {
return console.log('读取文件失败了')
}
// 默认读取到的 data 是二进制数据
// 而模板引擎的 render 方法需要接收的是字符串
// 所以我们在这里需要把 data 二进制数据转为 字符串 才可以给模板引擎使用
var ret = template.render(data.toString(), {
name: 'Jack',
age: 18,
province: '广东省',
hobbies: [
'写代码',
'唱歌',
'打游戏'
],
title: '个人信息'
})

console.log(ret)
})

在Apache案例中加入模板引擎

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
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 (req, res) {
var url = req.url
fs.readFile('./template-apache.html', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
// 1. 如何得到 wwwDir 目录列表中的文件名和目录名
// fs.readdir
// 2. 如何将得到的文件名和目录名替换到 template.html 中
// 2.1 在 template.html 中需要替换的位置预留一个特殊的标记(就像以前使用模板引擎的标记一样)
// 2.2 根据 files 生成需要的 HTML 内容
// 只要你做了这两件事儿,那这个问题就解决了
fs.readdir(wwwDir, function (err, files) {
if (err) {
return res.end('Can not find www dir.')
}

// 这里只需要使用模板引擎解析替换 data 中的模板字符串就可以了
// 数据就是 files
// 然后去你的 template.html 文件中编写你的模板语法就可以了
var htmlStr = template.render(data.toString(), {
title: '哈哈',
files: files
})

// 3. 发送解析替换过后的响应数据
res.end(htmlStr)
})
})
})
server.listen(3000, function () {
console.log('running...')
})

服务端渲染与客户端渲染的区别

服务端:

服务端

客户端:

客户端

url模块的使用

1
2
3
4
5
var url = require('url')

var obj = url.parse('/pinglun?name=sdfs&message=dsfghdghdghd', true)
console.log(obj)
console.log(obj.query)

简单的留言功能小案例

服务端:

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// app application 应用程序
// 把当前模块所有的依赖项都声明在文件模块最上面
// 为了让目录结构保持统一清晰,所以我们约定,把所有的 HTML 文件都放到 views(视图) 目录中
// 我们为了方便的统一处理这些静态资源,所以我们约定把所有的静态资源都存放在 public 目录中
// 哪些资源能被用户访问,哪些资源不能被用户访问,我现在可以通过代码来进行非常灵活的控制
// / index.html
// /public 整个 public 目录中的资源都允许被访问
// 前后端融会贯通了,为所欲为

var http = require('http')
var fs = require('fs')
var url = require('url')
var template = require('art-template')
var comments = [{
name: '木槿',
message: '今天天气不错!',
dateTime: '2019-08-14'
},
{
name: '木槿2',
message: '今天天气不错!',
dateTime: '2019-08-14'
},
{
name: '木槿3',
message: '今天天气不错!',
dateTime: '2019-08-14'
},
{
name: '木槿4',
message: '今天天气不错!',
dateTime: '2019-08-14'
},
{
name: '木槿5',
message: '今天天气不错!',
dateTime: '2019-08-14'
}
]

http
.createServer(function(req, res) {
// 使用 url.parse 方法将路径解析为一个方便操作的对象,第二个参数为 true 表示直接将查询字符串转为一个对象(通过 query 属性来访问)
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 Founf.')
}
var htmlStr = template.render(data.toString(), {
comments: comments
})
res.end(htmlStr)
})
} else if (pathname === '/post') {
fs.readFile('./views/post.html', function(err, data) {
if (err) {
return res.end('404 Not Found ~')
}
res.end(data)
})
} else if (pathname.indexOf('/public/') === 0) {
// /public/css/main.css
// /public/js/main.js
// /publis/lib/jquery.js
// 统一处理
// 如果请求路径是以 /public/开头的,则我认为你获取 public 中的某个资源
// 所以我们就直接可以把请求路径当作文件路径来直接进行读取
fs.readFile('.' + pathname, function(err, data) {
if (err) {
return res.end('404 Not Founf.')
}
res.end(data)
})
} else if (pathname === '/pinglun') {
// 注意:这个时候无论 /pinglun?xxx 之后是什么,都不用担心了,因为我的 pathname 是不包含 ? 之后的那个路径
// 一次请求对应一次响应,响应结束这次请求也就结束了
// console.log('收到表单请求了~', parseObj.query)

// res.end(JSON.stringify(parseObj.query))

// 我们已经使用 url 模块的 parse 方法把请求路径中的查询字符串给解析成一个对象了
// 所以接下来要做的就是:
// 1. 获取表单提交的数据 parseObj.query
// 2. 将当前时间日期添加到数据对象中,然后存储到数组中
// 3. 让用户重定向跳转到首页 /
// 当用户重新请求 / 的时候,我数组中的数据已经发生变化了,所以用户看到的页面也就变了
var comment = parseObj.query
comment.dateTime = '2019-08-14'
comments.unshift(comment)
// 服务端这个时候已经把数据存储好了,接下来就是让用户重新请求 / 首页,就可以看到最新的留言内容了

// 如何通过服务器让客户端重定向?
// 1. 状态码设置为 302 临时重定向
// statusCode
// 2. 在响应头中通过 Location 告诉客户端往哪儿重定向
// setHeader
// 如果客户端发现收到服务器的响应的状态码是 302 就会自动去响应头中找 Location ,然后对该地址发起新的请求
// 所以你就能看到客户端自动跳转了
res.statusCode = 302
res.setHeader('Location', '/')
res.end()
} else {
fs.readFile('./views/404.html', function(err, data) {
if (err) {
return res.end('404 Not Found ~')
}
res.end(data)
})
}
})
.listen(3000, function() {
console.log('running...')
})

客户端:

评论列表页面:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>留言本</title>
<!--
浏览器收到 HTML 响应内容之后,就要开始从上到下依次解析,
当在解析的过程中,如果发现:
link
script
img
iframe
video
audio
等带有 src 或者 href(link) 属性标签(具有外链的资源)的时候,浏览器会自动对这些资源发起新的请求。
-->
<!--
注意:在服务端中,文件中的路径就不要去写相对路径了。
因为这个时候所有的资源都是通过 url 标识来获取的
我的服务器开放了 /public/ 目录
所以这里的请求路径都写成:/public/xxx
/ 在这里就是 url 根路径的意思。
浏览器在真正发请求的时候会最终把 http://127.0.0.1:3000 拼上

不要再想文件路径了,把所有的路径都想象成 url 地址
-->
<link rel="stylesheet" href="/public/lib/bootstrap/dist/css/bootstrap.css">
</head>

<body>
<!-- <img src="/public/img/ab3.jpg" alt=""> -->
<div class="header container">
<div class="page-header">
<h1>Example page header <small>Subtext for header</small></h1>
<a class="btn btn-success" href="/post">发表留言</a>
</div>
</div>
<div class="comments container">
<ul class="list-group">
{{each comments}}
<li class="list-group-item">{{ $value.name }}说:{{ $value.message }} <span class="pull-right">{{ $value.dateTime }}</span></li>
{{/each}}
</ul>
</div>
</body>

</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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>发表评论</title>
<link rel="stylesheet" href="/public/lib/bootstrap/dist/css/bootstrap.css">
</head>

<body>
<div class="header container">
<div class="page-header">
<h1><a href="/">首页</a> <small>发表评论</small></h1>
</div>
</div>
<div class="comments container">
<!--
以前表单是如何提交的?
表单中需要提交的表单控件元素必须具有 name 属性
表单提交分为:
1. 默认的提交行为
2. 表单异步提交

action 就是表单提交的地址,说白了就是请求的 url 地址
method 请求方法
get
post
-->
<form action="/pinglun" method="get">
<div class="form-group">
<label for="input_name">你的大名</label>
<input type="text" class="form-control" required minlength="2" maxlength="10" id="input_name" name="name" placeholder="请写入你的姓名">
</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>
</form>
</div>
</body>

</html>

404 页面:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>抱歉! 您访问的页面失联啦...</h1>
</body>
</html>

源代码可以去我的 github 克隆

-------------本文结束感谢您的阅读-------------
木槿前端不求人,有空就来坐坐。
0%