(1)前言
大纲:
其他:
详见Markdown标记语言
(2)代码风格及封号详见下节
(3)初步实现Apache功能
1、审查node进程
Ctrl+C关闭node服务后,再次审查
此时便没有了node进程
2、Apache服务
Apache服务器软件---默认有一个www目录,所有存放在www目录中的资源都可以通过网址来浏览
Apache默认功能,默认显示index.html
如上所示,在c盘下新建app目录,里面新建多个文件,接下来想实现某个效果:类似于Apache,可以通过文件路径直接访问读取www下的文件,在网页内展示
3、类似于Apache统一处理资源
接续处理,完善读取
4、实例验证如下
①在D盘下新建www目录
②放入相应文件
,
③ 编写代码实现功能
// 引入网络构建服务模块 var http = require('http') // 引入fs文件系统模块 var fs = require('fs') // 创建server服务器 var server = http.createServer() var wwwDir = 'D:/www' //接收请求,处理响应事件 server.on('request',function(req,res){ var url = req.url var filePath = '/index.html' if(url !== '/'){ filePath = url } fs.readFile(wwwDir+filePath,function(error,data){ if(error){ return res.end('404 Not Found') } res.end(data) }) }) // 启动服务器,监听端口 server.listen(3000,function(){ console.log('服务器启动成功,端口号为3000...') })
此时,启动服务器后便可以通过设定好的路径去访问相应文件
需求升级:要求有图形操作页面,如下所示
(4)Apache目录列表模板
首先需要了解Apache默认行为,即如果目录下有index.html,则显示该页面。如果没有则显示目录列表
1、删除index.html文件。显示目录列表
2、编写代码
①访问模板页面(模板页面代码可以直接复制过来,模板代码参加下篇文章)
②问题解决
③读取目录API
④具体渲染见下节(5)
代码及文件结构如下:
// 引入网络构建服务模块 var http = require('http') // 引入fs文件系统模块 var fs = require('fs') // 创建server服务器 var server = http.createServer() var wwwDir = 'D:/www' //接收请求,处理响应事件 server.on('request',function(req,res){ var url = req.url fs.readFile('./template.html',function(error,data){ if(error){ return res.end('404 Not Found') } res.end(data) }) }) // 启动服务器,监听端口 server.listen(3000,function(){ console.log('服务器启动成功,端口号为3000...') })
此时启动服务器,便会查看到网页模板页面
(5)Apache目录列表渲染
1、分析
接下来只需要将获取到的目录下的文件和文件名,替换到template.html即可
2、读取文件目录
var fs = require('fs') fs.readdir('D:/www',function(error,files){ if(error){ return console.log('目录不存在') } console.log(files) })
3、结合Apache目录渲染①
首先在模板里写入标记,后期替换
4、结合Apache目录渲染②
替换标记
验证如下:
5、结合Apache目录渲染③
如何将文件名和目录名替换到template.html里 1、在template.html中需要替换的位置预留一个特殊的标记(类似之前模板引擎的特殊标记) 2、根据files生成需要的HTML内容
第一步:预留位置存放特殊标记
第二步:动态生成HTML内容
(6)浏览器使用模板引擎artTemplate
1、安装
在当前目录下安装模板引擎,会自动下载到新创建的node_modules文件夹下
artTemplate相关的依赖文件都会一起下载下来,所以下载完成后的目录会包含其他文件
2、浏览器中使用模板引擎(注意:模板引擎不关心内容)
模板引擎只关心{{}}八字符里面的内容,验证如下
3、模板遍历语法
注意:mustache八字胡语法
(7)node.js使用模板引擎artTemplate
1、步骤
2、node中引用第三方脚本库,不能使用script,而是使用require导入
3、文档API
这里注意,浏览器的语法在这里无效,没有script标签
浏览官方文档后的API: template.render('模板字符串',替换对象),验证如下
4、替换HTML模板进行测试
j
这样便可以实现node使用模板引擎解析替换对应内容,但这样模板内容太多,过于累赘,所以一般是从外部读取
5、外部引入HTML模板
接下来使用fs文件系统模块读取文件
6、最后修改之前文件,不再手动拼接字符串
接下来使用模板引擎解析替换HTML页面
整体代码如下
// 引入网络构建服务模块 var http = require('http') // 引入fs文件系统模块 var fs = require('fs') //引入模板引擎 var template = require('art-template') // 创建server服务器 var server = http.createServer() var wwwDir = 'D:/www' //接收请求,处理响应事件 server.on('request',function(req,res){ var url = req.url fs.readFile('./template-web.html',function(error,data){ if(error){ res.setHeader('Content-Type','text/plain;charset=utf-8') return res.end('404 Not Found') } fs.readdir(wwwDir,function(error,files){ if(error){ return res.end('目录不存在') } /*使用模板引擎解析替换data里的模板字符串*/ var htmlStr = template.render(data.toString(),{ files:files, title:'网页标题' }) /*发送解析替换过后的响应数据*/ res.end(htmlStr) }) }) }) // 启动服务器,监听端口 server.listen(3000,function(){ console.log('服务器启动成功,端口号为3000...') })
template-web.html代码如下:
<html dir="ltr" lang="zh" i18n-processed=""> <head> <meta charset="utf-8"> <meta name="google" value="notranslate"> <style> h1 { border-bottom: 1px solid #c0c0c0; margin-bottom: 10px; padding-bottom: 10px; white-space: nowrap; } table { border-collapse: collapse; } th { cursor: pointer; } td.detailsColumn { -webkit-padding-start: 2em; text-align: end; white-space: nowrap; } a.icon { -webkit-padding-start: 1.5em; text-decoration: none; } a.icon:hover { text-decoration: underline; } a.file { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC ") left top no-repeat; } a.dir { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAd5JREFUeNqMU79rFUEQ/vbuodFEEkzAImBpkUabFP4ldpaJhZXYm/RiZWsv/hkWFglBUyTIgyAIIfgIRjHv3r39MePM7N3LcbxAFvZ2b2bn22/mm3XMjF+HL3YW7q28YSIw8mBKoBihhhgCsoORot9d3/ywg3YowMXwNde/PzGnk2vn6PitrT+/PGeNaecg4+qNY3D43vy16A5wDDd4Aqg/ngmrjl/GoN0U5V1QquHQG3q+TPDVhVwyBffcmQGJmSVfyZk7R3SngI4JKfwDJ2+05zIg8gbiereTZRHhJ5KCMOwDFLjhoBTn2g0ghagfKeIYJDPFyibJVBtTREwq60SpYvh5++PpwatHsxSm9QRLSQpEVSd7/TYJUb49TX7gztpjjEffnoVw66+Ytovs14Yp7HaKmUXeX9rKUoMoLNW3srqI5fWn8JejrVkK0QcrkFLOgS39yoKUQe292WJ1guUHG8K2o8K00oO1BTvXoW4yasclUTgZYJY9aFNfAThX5CZRmczAV52oAPoupHhWRIUUAOoyUIlYVaAa/VbLbyiZUiyFbjQFNwiZQSGl4IDy9sO5Wrty0QLKhdZPxmgGcDo8ejn+c/6eiK9poz15Kw7Dr/vN/z6W7q++091/AQYA5mZ8GYJ9K0AAAAAASUVORK5CYII= ") left top no-repeat; } a.up { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU0toU0EUPfPysx/tTxuDH9SCWhUDooIbd7oRUUTMouqi2iIoCO6lceHWhegy4EJFinWjrlQUpVm0IIoFpVDEIthm0dpikpf3ZuZ6Z94nrXhhMjM3c8895977BBHB2PznK8WPtDgyWH5q77cPH8PpdXuhpQT4ifR9u5sfJb1bmw6VivahATDrxcRZ2njfoaMv+2j7mLDn93MPiNRMvGbL18L9IpF8h9/TN+EYkMffSiOXJ5+hkD+PdqcLpICWHOHc2CC+LEyA/K+cKQMnlQHJX8wqYG3MAJy88Wa4OLDvEqAEOpJd0LxHIMdHBziowSwVlF8D6QaicK01krw/JynwcKoEwZczewroTvZirlKJs5CqQ5CG8pb57FnJUA0LYCXMX5fibd+p8LWDDemcPZbzQyjvH+Ki1TlIciElA7ghwLKV4kRZstt2sANWRjYTAGzuP2hXZFpJ/GsxgGJ0ox1aoFWsDXyyxqCs26+ydmagFN/rRjymJ1898bzGzmQE0HCZpmk5A0RFIv8Pn0WYPsiu6t/Rsj6PauVTwffTSzGAGZhUG2F06hEc9ibS7OPMNp6ErYFlKavo7MkhmTqCxZ/jwzGA9Hx82H2BZSw1NTN9Gx8ycHkajU/7M+jInsDC7DiaEmo1bNl1AMr9ASFgqVu9MCTIzoGUimXVAnnaN0PdBBDCCYbEtMk6wkpQwIG0sn0PQIUF4GsTwLSIFKNqF6DVrQq+IWVrQDxAYQC/1SsYOI4pOxKZrfifiUSbDUisif7XlpGIPufXd/uvdvZm760M0no1FZcnrzUdjw7au3vu/BVgAFLXeuTxhTXVAAAAAElFTkSuQmCC ") left top no-repeat; } html[dir=rtl] a { background-position-x: right; } #parentDirLinkBox { margin-bottom: 10px; padding-bottom: 10px; } #listingParsingErrorBox { border: 1px solid black; background: #fae691; padding: 10px; display: none; } </style> <title id="title">{{title}}</title> </head> <body> <div id="listingParsingErrorBox">糟糕!Google Chrome无法解读服务器所发送的数据。请
<a href="http://code.google.com/p/chromium/issues/entry">报告错误</a>,并附上<a href="LOCATION">原始列表</a>。</div> <h1 id="header">D:www 的索引</h1> <div id="parentDirLinkBox" style="display:none"> <a id="parentDirLink" class="icon up"> <span id="parentDirText">[上级目录]</span> </a> </div> <table> <thead> <tr class="header" id="theader"> <th onclick="javascript:sortTable(0);">名称</th> <th class="detailsColumn" onclick="javascript:sortTable(1);"> 大小 </th> <th class="detailsColumn" onclick="javascript:sortTable(2);"> 修改日期 </th> </tr> </thead> <tbody id="tbody"> {{each files}} <tr> <td data-value="apple/"><a class="icon dir" href="#">{{$value}}/</a></td> <td class="detailsColumn" data-value="0"></td> <td class="detailsColumn" data-value="1509589967">2018/11/2 上午10:32:47</td> </tr> {{/each}} </tbody> </table> </body> </html>
最终解析结果
7、添加判断
目前为止,已经做到了服务器端渲染。
区别于之前的技术,在前端拿到数据,模板引擎解析替换,是浏览器做的。
注意:这里使用服务器端+模板引擎进行了渲染,最早之前模板引擎是后端技术
8、
(8)客户端渲染+服务器端渲染
1、客户端渲染
所以一般会发两次请求,第一次请求页面字符串,第二次请求数据
2、服务端渲染
当然,ajax等异步请求也会用到,只是看具体应用场景
3、如何判断网页是客户端渲染还是服务器渲染
查看网页源代码,如果源代码里有页面对应内容,则是服务端渲染。如果没有页面对应内容,则是客户端渲染
①查看网页源代码,发现里面有商品信息,所以这里是服务端渲染
②查看商品评价,明显有异步操作,没有刷新页面,但局部更新了页面部分内容,这里便使用了客户端渲染技术
如果是客户端后期动态追加的,便从源码里找不到相应内容
4、小结
分析后得出,商品列表为服务端渲染,在发送响应时,将页面和数据一起给了浏览器,可以在审查源代码里找到。
而商品评价为客户端渲染,是客户端发送请求,后期动态生成的,审查源代码找不到。
所以,一个网站既有服务端渲染,又有客户端渲染。
5、SEO问题
之所以将客户端渲染和服务器渲染结合,这里主要考虑SEO搜索引擎优化问题。
ajax异步渲染的数据,使用爬虫无法获取,即客户端渲染不利于SEO
6、使用场景
如果需要考虑SEO搜索引擎优化,则必须使用服务端渲染,否则使用客户端渲染可以更快一些,用户体验也更好
(9)【留言板案例】---处理网站里的静态资源
需求:对之前文件做下完善,目录是目录样式,文件是文件样式,目录也可以点击进入,但这里已经对服务端渲染有了基本了解,所以接下来不再做处理
接下来做个留言板案例
1、启动服务简写
将创建服务和监听端口合并到一步,简写如下
2、读取模板文件
为了让目录结构保持统一清晰,约定把所有的HTML文件都放到views目录中
3、注意
验证分析:
分析:之所有有这么多请求,首先,页面是一个请求,页面里的相关资源也是请求
此时网站之所以一直卡着不动,是因为没有对静态资源做处理
4、服务端处理静态资源
此时不可能一依次添加判断,为了统一处理这些静态资源,我们约定将所有静态资源都存放在public目录中。
=== >
除此之外,还可以包含lib目录,用于存放第三方包
此时再次启动服务器测试,请求成功
public目录已经开放开来,如果后期还用到静态资源,直接放到对应目录下即可。
这里将bootstrap放到lib目录下,然后修改引用路径
app.js完整代码如下:
var http = require('http') var fs = require('fs') http.createServer(function(req,res){ var url = req.url if(url === '/'){ fs.readFile('./views/index.html',function(error,data){ if(error){ return res.end('404 Not Found.') } res.end(data) }) }else if(url.indexOf('/public/' === 0)){ fs.readFile('.' + url,function(error,data){ if(error){ return res.end('404 Not Found.') } res.end(data) }) } }).listen(3000,function(){ console.log('服务启动完毕,3000端口访问... ...') })
此时便可以正确渲染了
5、小结
public专门用于存放客户端静态资源
例如css、图片、js、jquery(服务端没有DOM,所以不可能用到jquery)
客户端如果想用,引入即可
(10)页面跳转+404
1、输入无效url时,跳转至404页面
这里之所以不用设置请求头信息类型,因为在404.html已经设置了元标签meta标签
var http = require('http') var fs = require('fs') http.createServer(function(req,res){ var url = req.url if(url === '/'){ fs.readFile('./views/index.html',function(error,data){ if(error){ return res.end('401 Not Found.') } res.end(data) }) }else if(url.indexOf('/public/')=== 0){ fs.readFile('.' + url,function(error,data){ if(error){ return res.end('402 Not Found.') } res.end(data) }) }else{ fs.readFile('./views/404.html',function(error,data){ if(error){ return res.end('403 Not Found.') } res.end(data) }) } }).listen(3000,function(){ console.log('服务启动完毕,3000端口访问... ...') })
2、发表留言页面跳转
点击留言时,跳转至留言页面
(11)渲染评论首页
1、首先在服务端写个数组,然后在模板页面进行遍历
结果如下
2、接下来在服务端使用模板引擎
核心模块为node自带,不用安装
第三方模块需要下载安装
(12)处理表单get提交
1、表单提交行为
表单提交行为大概分为两种 1、默认的提交行为(同步提交) 2、异步提交
2、点击后跳转到对应url
3、url模块解析API
将提交后的url进行解析,如下所示
加入true参数,如下所示
url模块解析API url.parse(url链接) url.parse(url链接,true)
4、注意
5、url模块
6、使用url模块
var http = require('http') var fs = require('fs') var template = require('art-template') var url = require('url') var comments = [ {name:'tony',message:'你好tony,感觉人生达到了巅峰',data:'2016-02-12'}, {name:'bob',message:'你好bob,感觉人生达到了巅峰',data:'2016-03-02'}, {name:'jack',message:'你好jack,感觉人生达到了巅峰',data:'2016-11-18'} ] http.createServer(function(req,res){ var parseObj = url.parse(req.url,true) /*单独获取不包含查询字符串的路径部分,该路径不包含?之后的内容*/ var pathname = parseObj.pathname if(pathname === '/'){ fs.readFile('./views/index.html',function(error,data){ if(error){ return res.end('404 Not Found.') } var htmlStr = template.render(data.toString(),{ comments:comments }) res.end(htmlStr) }) }else if(pathname.indexOf('/public/')=== 0){ fs.readFile('.' + pathname,function(error,data){ if(error){ return res.end('404 Not Found.') } res.end(data) }) }else if(pathname === '/post'){ fs.readFile('./views/post.html',function(error,data){ if(error){ return res.end('404 Not Found.') } res.end(data) }) }else if(pathname === '/pinglun'){ /*这里不能再判断url,因为是动态的,所以只需要判断pathname即可*/ res.end(JSON.stringify(parseObj.query)) }else{ fs.readFile('./views/404.html',function(error,data){ if(error){ return res.end('404 Not Found.') } res.end(data) }) } }).listen(3000,function(){ console.log('服务启动完毕,3000端口访问... ...') })
现在已经收到数据,接下来开始进行下一步解析处理
(13)表单提交重定向
接着上面的操作
1、如何通过服务器让客户端重定向(服务器重定向)
先写个死数据测试下
如何通过服务器让客户端重定向? 1、设置状态码为302临时重定向(301为永久重定向) 2、在响应头中通过Location告诉客户端网哪里重定向 3、如果客户端发现收到的服务器响应状态码是302,就会自动去响应头中找Location然后对该地址发送新请求 4、因此便可以看到客户端自动跳
else if(pathname === '/pinglun'){ /*这里不能再判断url,因为是动态的,所以只需要判断pathname即可 测试res.end(JSON.stringify(parseObj.query)) */ /*添加数据-注意:不是持久化存储,后期介绍*/ var comment = parseObj.query comment.dateTime = '2017-11-3 12:12:30' comments.push(comment) /*重定向到首页*/ /* 如何通过服务器让客户端重定向? 1、设置状态码为302临时重定向(301为永久重定向) 2、在响应头中通过Location告诉客户端网哪里重定向 3、如果客户端发现收到的服务器响应状态码是302,就会自动去响应头中找Location然后对该地址发送新请求 4、因此便可以看到客户端自动跳转 */ res.statusCode = 302 res.setHeader('Location','/') res.end()/*结束响应*/ }
接下来做下测试,点击Network中的Preverse log保存重定向日志,然后提交数据
浏览器发现状态码为302,然后去请求头Header里找Location,再次对新路径发起请求,进行重定向跳转
2、接下来即可通过本地局域网ip进行访问测试
(14)小结
1、node底层
2、步骤
3、 接下来
.