NodeJs 笔记(二)
1.1 初步实现Apache功能
var http = require('http')
1. 创建Server
var server = http.createServer()
2. 监听Server的request请求,设置请求处理函数
//请求、处理、响应。一个请求对应一个响应
var wwwDir = 'D:/Movie/www'//定义一个路径
server.on('request',function(req,res){
var url = req.url
if (url == '/') {
fs.readFile(wwwDir + '/index.html',function(err,data){
if(err){
return res.end('404 Not Found')
}
res.end(data)
})
} else if(url == '/a.txt'){
fs.readFile(wwwDir + '/a.txt', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})
} else if (url === '/index.html') {
fs.readFile(wwwDir + '/index.html', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})
} else if (url === '/apple/login.html') {
fs.readFile(wwwDir + '/apple/login.html', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})
}
})
3. 绑定端口号,启动服务
server.listen(3000,function(){
console.log('running...')
})
1.2 Apache-文件夹生成目录列表
- 获取某个文件夹下的所有的文件名和目录名,可以使用fs.readdir(路径,function(err,files))
- 在template模板html中添加一个特殊标记,根据这个特殊标记,我们可以在JS文件中替换这个标记(用我们想要显示的内容替换)
- 如何替换?利用readdir形参files去遍历
files.forEach(item)
,给获取到的每一项item。然后利用${}来引用变量
1. 如何得到 wwwDir 目录列表中的文件名和目录名
fs.readdir
2. 如何将得到的文件名和目录名替换到 template.html 中
2.1 在 template.html 中需要替换的位置预留一个特殊的标记(就像以前使用模板引擎的标记一样)
2.2 根据 files 生成需要的 HTML 内容
fs.readdir('H:\www', 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)
})
2.1 模板引擎
- art-template安装:
npm install art-template
- 在需要使用的文件模块中加载art-template只需要使用require方法加载就可以了:require(‘art-template’)参数中的 art-template,就是你下载的包的名字,也就是说你isntall的名字是什么,则你 require 中的就是什么
- 查文档,使用模板引擎的API
- 注意:模板引擎只关心挂载的html中{{引用对象的某个属性}},其他有错误都不关心。
- 总结:模版引擎的使用,先require加载它,再通过fs.readFile去读取某一个文件,在读操作中写入template.render(data.toString(),{数据对象})。
- 作用:读取的html文件中可以通过
{{}}
来引用数据对象中的数据。
var template = require('art-template')
var fs = require('fs')
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)
}
上边例子调用这个HTML,替换里边的数据内容:
tpl.html:
<!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>
- 例子中使用:template.render(‘要替换的目标’,{包含要替换内容的对象})。如果使用fs.readFile,则第一个参数为形参data.toString()
2.2 服务端渲染页面
- 服务端读取模版HTML文件,然后利用readdir(‘文件夹路径’,function(err,files){})获取files数据(该数据的值files.value为文件名)。使用
template.render(data.toString(),{渲染的数据})。 - 注:其中data是readFile回调函数里的第二个参数,所以要写在readFile中。
fs.readFile('读取的文件',function(err,data){
嵌套fs.readdir读取文件夹中的文件名和目录名
fs.readdir('文件夹路径',function(err,files){
再次嵌套template.render用于渲染模版HTML
template.render(data.toString(),{
渲染的数据
title: '哈哈',
files: files
})
})
})
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...')
})
2.3 总结
- 客户端渲染(至少请求两次)
- 输入网址,浏览器DNS解析出服务器的IP地址,发送响应请求。服务器发送回来一个字符串(包含完整HTML页面结构),浏览器解析字符串。
- 解析到ajax会异步再次发送请求,响应到接口数据,用来渲染模板引擎(数据动态渲染,页面无需卸载,例如:电商的商品评论)。
此处的字符串(完整的HTML页面结构)内容:
<html>
<head></head>
<script type='tpl'>
模板字符串(你要显示的模板,会引用{{数据}})
</script>
<script>
$.ajax({
服务器解析到这会自动再次发送数据请求
})
</script>
</html>
- 服务端渲染(至少一次请求)
- 服务器中响应的时候是页面+数据渲染完成后再发送给浏览器。
- 使用template.render(模板字符串,{解析替换对象})和response.end(发送渲染后的字符串)来完成渲染。
- 其中模版字符串是通过fs.readFile中的回调函数的第二个参数data,通过toString()后获得的。
- 需要重新加载页面,例如:电商的商品展示
这个模板字符串:
<html>
<head></head>
<body>
<h1>hello{{name}}</h1>
</body>
</html>
- 总结:
- 客户端渲染,不利于SEO(搜索引擎优化)
- 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的。
- 真正的网站是两者结合起来的。例如京东的商品列表,是服务端渲染(有利于搜索引擎搜索到),而评论区则采用客户端渲染(动态加载数据,响应更快)
2.3 处理网站中的静态资源
- 利用http创建的Server,再Server.on和Server.listen。都可以通过链式编程,甚至都不用去定义Server。
var http = require('http')
http
.createServer(function(req,res){
//写原先server.on的内容
})
.listen(3000,function(){
console.log('Server is running')
})
- 为了让目录结构保持统一清晰,所以我们约定,把所有的HTML文件都放到views(视图)目录中我们为了方便的统一处理这些静态资源,所以我们约定把所有的静态资源都存放在public目录中哪些资源能被用户访问,哪些资源不能被用户访问,我现在可以通过代码来进行非常灵活的控制
var fs = require('fs')
var http = require('http')
var url = require('url')
var template = require('art-template')
var comments = [
{
name: '张三',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三2',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三3',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三4',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三5',
message: '今天天气不错!',
dateTime: '2015-10-16'
}
]
- 上半部分代码:主要是加载一些核心插件和定义数据对象。
- 下半部分代码:
- 使用到了url核心插件的url.parse()方法将路径解析为一个方便操作的对象,第二个参数为 true 表示直接将查询字符串转为一个对象(通过 query 属性来访问提交的数据)。
url.parse('网址',ture)返回的内容:
URL:{
protocol: 'http:',
host:'127.0.0.1:3000',
port:'3000',
hostname:'127.0.0.1',
query:'/pinglun?name=%E7%9AXXXX&message=%3XXX',
//注意:第二个参数为true时,query会解析为
query:{name:'何勤',message:'套猴子'}
pathname: '/pinglun',
path: '/pinglun?namexxxx',
herf: 'http://127....'
}
- 只需要判定请求路径是/pinglun的时候,就认为提交表单的请求过来 。在该路径下,获取表单提交的内容,将内容放进comments数据对象,并重新定位回到主页。
else if (pathname == '/pinglun'){
两件事:
1. 获取提交过来的内容,并把它放入comments对象中。
var comment = parseObj.query
comment.dateTime = '2017-11-2 17:11:22'
comments.unshift(comment)
2. 修改状态码为302,让其重新定位到首页
res.statusCode = 302
res.setHeader('Location', '/')
res.end()
}
- 这段代码将public文件夹里的文件设置为开放状态,只要输入正确的路径就能访问到public中的所有文件。
if(pathname.indexOf('/public/') === 0){
fs.readFile('.' + pathname, function(err,data){
if (err) {
return res.end('404 Not Found.')
} else {
res.end(data)
}
})
}
http
.createServer(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.')
} else {
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.')
} else {
res.end(data)
}
})
} else if (pathname.indexOf('/public/') === 0){
fs.readFile('.' + pathname, function(err,data){
if (err) {
return res.end('404 Not Found.')
} else {
res.end(data)
}
})
} else if (pathname == '/pinglun'){
对于我们来讲,其实只需要判定,如果你的请求路径是/pinglun的时候,那我就认为你提交表单的请求过来
然后做两件事:
1. 获取提交过来的内容,并把它放入comments对象中。
var comment = parseObj.query
comment.dateTime = '2017-11-2 17:11:22'
comments.unshift(comment)
2. 修改状态码为302,让其重新定位到首页
res.statusCode = 302
res.setHeader('Location', '/')
res.end()
}
})
.listen(3000,function(){
console.log('running')
})
- 下边为post.html,是“发表留言”的页面,主要了解表单如何提交到服务器端的。
<form action="/pinglun" method="get">
form中action的值就是请求的url地址。 - 工作原理:当我们点提交的时候,网址会跳到action的值(即路径)127.0.0.1:3000/pinglun?name=你输入的姓名&message=你留言的内容,因此,我们可以通过判别路径是否为‘/pinglun’,再用parseObj对象.query获取到name和message
post:
<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>