之前使用 nodejs 完成了简陋版的静态服务器,了解了服务器的运行机制:
1、创建服务器对象并监听用户请求
2、设置路由来根据用户请求返回不同的响应结果
但这里仅仅是返回了静态的结果,而没有动态的数据。如果需要有动态的数据,整个服务器的运行机制是怎么样的?
1、同样是创建服务器对象并监听用户请求
2、用户请求分为静态文件请求和获取动态页面。比如get 127.0.0.1:3000/index.html
和get 127.0.0.1:3000/index
的结果是不同的。
3、当请求127.0.0.1:3000/index
时,触发render()
函数,传入index.html
作为模板,传入{name: 'ltaoo'}
作为页面数据。render()
函数获取index.html
文件内容,遇到{name}
表示需要替换为ltaoo
。
使用模板引擎来替代自己解析html
并插入数据。
简单实现 尝试实现最简陋版本的服务端程序,只有index
页面并显示固定数据,不从数据库中获取数据、没有静态服务器(意味着没有样式与js脚本)。
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 var http = require ('http' )var url = require ('url' )var fs = require ('fs' )var path = require ('path' )http.createServer(function (req, res ) { var pathname = url.parse(req.url).pathname if (pathname === '/index' ) { fs.readFile(path.join(__dirname, './index.html' ), 'utf-8' , function (err, file ) { if (err) console .log(err) var data = { "title" : "MVC project" , "author" : "ltaoo" , "content" : "a project content" } var pattern = /({{)[a-z]+(}})/g ; var ary = file.match(pattern); for (var i = ary.length - 1 ; i >= 0 ; i--) { ary[i] = ary[i].replace(/\W/g , ') } //console.log(ary) // 将 html 文件内的变量替换 for (var j = ary.length - 1; j >= 0; j--) { file = file.replace(' {{' + ary[j] + ' }}', data[ary[j]]) } // 渲染页面 res.writeHead(200, {"Content-Type": "text/html"}) res.write(file) res.end() }) } }).listen(3000) console.log(' server is listening at port 3000 ');
对应的模板文件index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Nodejs study</title > </head > <body > <article > <h1 > {{title}}</h1 > <p > {{author}}</p > <p > {{content}}</p > </article > </body > </html >
运行后页面成功显示预期的数据。
如何从该程序拓展成一个“比较”通用的服务器程序?
拓展 服务器的工作原理即接收请求,返回数据 。查看express官方文档:
1 2 3 app.get('/' , function (req, res ) { res.render('index' , {title : "Express" , content : "this is a express project" }); });
这里等同于
1 2 3 if (pathname === '/index' ) { }
可以从上面的简陋版程序中看到,这一段代码属于响应请求的核心部分,代码量比较多且逻辑相同。所以抽象为函数。即 render() 函数。 该函数接收模板、数据作为参数,并在函数内渲染页面。
render 函数 根据 pathname 来调用 template。比如
get index, index.html
get articles, articles.html
get article/2016/07/26/first, 调用article.html
1 2 3 function render (template, data ) { }
这里涉及到模板引擎,如果使用 express 内置引擎,需要现在项目开始位置配置 views 的路径,则 render 函数将会在 views 目录下查找模板。
MVC 架构 OK,上面讲到了渲染页面,但是问题在于数据是怎么得到的,又是怎么传递给 render 函数的。如果 render 函数属于 V,则 M 和 C 都暂时还没有出现。
MVC 模式是软件工程的一种软件架构模式。…专业人员可以通过自身的专长分组:
控制器(Controller)-负责转发请求,对请求进行处理。
视图(View)-界面设计人员进行图形界面设计。
模型(Model)-程序员编写程序应有功能(实现算法等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
OK,按照维基百科的说明,MVC架构能够让专业人员分组进行工作,各自负责自己擅长的方面。是否能理解成这是属于三个方面,通过函数调用来实现联系。
比如说,用户请求 /index ,这个请求将交给 Controller 来处理,Controller 知道了用户在请求 /index,就让 Model 从数据库中调取数据,调取完成后将数据交给 Controller,Controller拿到这个数据后,把数据和 View 进行组合,最后返回页面给用户。即
V -> C -> M -> C -> V
所以 View 应该仅仅是模板,没有任何的逻辑。上面代码中, render 函数应该是属于 Controller,因为它接受到了请求,并且处理 data 和 template,现在缺的是 Model。 路由是属于 Controller ,即 if(pathname === ‘/html’) 开始,因为这里接收到请求并根据请求来处理数据,即逻辑部分。
Model Model 属于和数据库交互的部分,在这里定义了方法来实现增删改查,并封装成模块将方法暴露给外部使用。假设:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function fetch ( ) { } function fetchItem (id ) { } exports .fetch = fetchexports .fetchItem = fetchItem
然后就可以在 Controller 内调用方法获取数据,这样 Controller 就将 Model 和 View 联系在一起了。
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 var http = require ('http' ), url = require ('url' ), fs = require ('fs' ), path = require ('path' ) http.createServer(function (req, res ) { var pathname = url.parse(req.url).pathname if (pathname === '/index' ) { fs.readFile(path.join(__dirname, './index.html' ), 'utf-8' , function (err, file ) { if (err) console .log(err) var model = require ('model.js' ); var data = model.fetch(); var pattern = /({{)[a-z]+(}})/g ; var ary = file.match(pattern); for (var i = ary.length - 1 ; i >= 0 ; i--) { ary[i] = ary[i].replace(/\W/g , ') } //console.log(ary) // 将 html 文件内的变量替换 for (var j = ary.length - 1; j >= 0; j--) { file = file.replace(' {{' + ary[j] + ' }}', data[ary[j]]) } // output res.writeHead(200, {"Content-Type": "text/html"}) res.write(file) res.end() }) } }).listen(3000) console.log(' server is listening at port 3000 ');
代码整理 我们有了 M ,有了 V,也有 C,但 C 没有分离出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var http = require('http'), url = require('url'), fs = require('fs'), path = require('path') http.createServer(function (req, res){ //解析请求 var pathname = url.parse(req.url).pathname if(pathname === '/index') { //只处理这一种情况 var index = require('index'); index.index(); } }).listen(3000) console.log('server is listening at port 3000');
将业务逻辑拿出来单独作为一个文件
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 exports .index = function ( ) { fs.readFile(path.join(__dirname, './index.html' ), 'utf-8' , function (err, file ) { if (err) console .log(err) var model = require ('model.js' ); var data = model.fetch(); var pattern = /({{)[a-z]+(}})/g ; var ary = file.match(pattern); for (var i = ary.length - 1 ; i >= 0 ; i--) { ary[i] = ary[i].replace(/\W/g , ') } //console.log(ary) // 将 html 文件内的变量替换 for (var j = ary.length - 1; j >= 0; j--) { file = file.replace(' {{' + ary[j] + ' }}', data[ary[j]]) } // output res.writeHead(200, {"Content-Type": "text/html"}) res.write(file) res.end() }) }
这样是否实现了一个 MVC 架构的服务端程序?
总结 对 nodeJs 的 http 模块有了一个基本的了解,对之后学习 koa 应该有很大的帮助。
参考