大前端-万物皆可JS

Nodejs实现一个简单的服务器

2016-11-05  本文已影响931人  ltaoo

之前使用 nodejs 完成了简陋版的静态服务器,了解了服务器的运行机制:

但这里仅仅是返回了静态的结果,而没有动态的数据。如果需要有动态的数据,整个服务器的运行机制是怎么样的?

这里可以使用模板引擎来替代自己解析html并插入数据。

简单实现

尝试实现最简陋版本的服务端程序,只有index页面并显示固定数据,不从数据库中获取数据、没有静态服务器(意味着没有样式与js脚本)。

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') {
    //只处理请求 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"
      }
      //获取html 文件并将其中的 {{xx}} 和 data 数据进行匹配。
      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

<!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官方文档:

app.get('/', function (req, res) {
    res.render('index', {title: "Express", content: "this is a express project"});
});

这里等同于

if(pathname === '/index') {
    //coding
}

可以从上面的简陋版程序中看到,这一段代码属于响应请求的核心部分,代码量比较多且逻辑相同。所以抽象为函数。即 render() 函数。
该函数接收模板、数据作为参数,并在函数内渲染页面。

render 函数

根据 pathname 来调用 template。比如

function render(template, data) {
    // 这个地方的代码和简陋版的原理差不多,同样是先获取到模板文件,可以是 jade,然后将数据替换掉模板中的预留位置。最后渲染页面。这个地方是请求的终点。
}

这里涉及到模板引擎,如果使用 express 内置引擎,需要现在项目开始位置配置 views 的路径,则 render 函数将会在 views 目录下查找模板。

MVC 架构

OK,上面讲到了渲染页面,但是问题在于数据是怎么得到的,又是怎么传递给 render 函数的。如果 render 函数属于 V,则 M 和 C 都暂时还没有出现。

MVC 模式是软件工程的一种软件架构模式。...专业人员可以通过自身的专长分组:

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 属于和数据库交互的部分,在这里定义了方法来实现增删改查,并封装成模块将方法暴露给外部使用。假设:


// 连接数据库

function fetch() {

    //从数据库中获取全部数据


}

function fetchItem(id) {

    // 根据id 从数据库中获取到指定数据


}

exports.fetch = fetch

exports.fetchItem = fetchItem

然后就可以在 Controller 内调用方法获取数据,这样 Controller 就将 Model 和 View 联系在一起了。

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 data = {
        "title": "MVC project",
        "author": "litao",
        "content": "a project content"
      }*/

      //将 Model 加载进来
      var model = require('model.js');
      // 获取数据
      var data = model.fetch();
      //parse html code and insert data
      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 没有分离出来。

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');

将业务逻辑拿出来单独作为一个文件


exports.index = function () {    

    fs.readFile(path.join(__dirname, './index.html'), 'utf-8', function (err, file){
      if(err) console.log(err)

      //表示将 Model 加载进来
      var model = require('model.js');
      // 加载数据
      var data = model.fetch();
      //parse html code and insert data
      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 应该有很大的帮助。

参考

上一篇 下一篇

猜你喜欢

热点阅读