# Node服务搭建

Node搭建服务器主要是利用http请求模块和fs文件系统模块进行处理,http启动一个服务,通过拦截浏览器访问的所有请求进行返回相应数据。平时我们访问一个网站,我们只关注Ajax接口请求,其实,不论我们访问的html、css、js文件还是媒体资源其实都是响应,都要通过服务器进行处理返回对应内容。我们从头开始搭建一个简易Node服务器,来搞明白服务端是如何处理各种服务请求进行构建网站的。

# 初始化项目

创建一个项目文件夹来搭建Node服务,通过npm init来初始化项目,初始化好在该文件下创建一个app.js文件当做服务入口文件。然后在package.json文件中配置服务运行命令。

// package.json
{
  "name": "node_tem",
  "version": "1.0.0",
  "description": "node项目",
  "main": "index.js",
  "scripts": {
    "dev": "node app.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "art-template": "^4.13.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.7"
  }
}

我们在app.js中启动一个简单的服务

// app.js
var http = require('http');


var server = http.createServer();


server.on('request', function (req, res) {
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
  res.end('<h1>Hello world!</h1>');
})


server.listen(3000, function () {
  console.log('server is running...')
})

然后,在终端执行下面命令服务就跑起来了。

npm run dev

# 项目结构设计

├── public                   # 资源文件
│   ├── css                  # css文件
│   ├── imgs                 # 图片
│   ├── js                   # js
│   └── lib                  # 资源文件
├── views                    # 页面模板文件
│   ├── common               # 公共模板
│   │   ├── header.html      # 项目图片
│   │   └── footer.html      # 公共less/sass文件
│   ├── index.html           # 首页模板
│   ├── post.html            # 其他页面
│   └── 404.4html            # 404页面
├── .gitignore               # git忽略
├── app.js                   # 服务入口文件
├── nodemon.json             # 文件监听
├── package.json             # 包管理
├── README.md

# 动态监听文件

node服务启动后,如果更改文件,需要重新执行启动命令才会生效,每次重启太麻烦了,我们使用nodemon插件来自动监听文件变化更新服务。

安装:

npm i nodemon -D

在根目录创建nodemon.json文件:

{
  "restartable": "rs",
  "ignore": [".git", ".svn", "node_modules/**/node_modules"],
  "verbose": true,
  "execMap": {
    "js": "node --harmony"
  },
  "watch": [],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js json njk css js "
}

在package.json文件添加启动命令:

{
  "name": "node_tem",
  "version": "1.0.0",
  "description": "node项目",
  "main": "index.js",
  "scripts": {
    "start": "nodemon app.js",
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "art-template": "^4.13.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.7"
  }
}


然后执行npm run start命令就可以自动监听文件改变了。

# 响应文件内容

在浏览器输入一个地址,响应对应的页面,简单的HTML内容我们可以在 res.end() 中直接返回,如果想返回一个完整的HTML页面就需要借助 fs 模块读取文件内容,返回给客户端进行响应。

// app.js
var http = require('http')
var fs = require('fs')


var server = http.createServer()


server.on('request', function (req, res) {
  // 读取html模板渲染页面
  fs.readFile('./views/index.html', function (err, data) {
    if (err) {
      return res.end('404 Not Fond')
    }
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    res.end(data)
  })
})


server.listen(3000, function () {
  console.log('server is running...')
})

html模板返回内容

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>tpl模板</title>
</head>
<body>
  <div class="index">
    <div class="index-header">
      <h1>大家好,我叫张三</h1>
    </div>
    <div class="index-wrap">111</div>
  </div>
</body>
</html>

# 区分响应路由

因为服务可以监听到所有请求,不论是静态资源还是页面路由。在根据响应进行返回内容时就要对响应进行区分,大概分为三大类,页面路由、接口请求、静态资源,根据不同的请求URL返回不同的资源。

我们可以借助 url 模块来获取请求的url,一般页面路由和请求单独区分出来来响应每个请求,静态资源只需要判断是否是public文件夹下的资源,就直接读取返回,如果没有对应的返回就响应404页面。

// app.js
var http = require('http')
var fs = require('fs')


// url模块的parse()方法可以解析url地址为一个json数据
var url = require('url')


var server = http.createServer()


server.on('request', function (req, res) {
  // 获取url对象
  var pathObj = url.parse(req.url, true)
  var pathName = pathObj.pathname // 获取路由


  console.log(pathName)
  if (pathName === '/' || pathName === '/index.html') {
    // 首页
  } else if (pathName === '/pinglun') {
    // 接口路由
  } else if (pathName.indexOf('/public') === 0) {
    // 静态资源
  } else {
    // 其他错误地址
    fs.readFile('./views/404.html', function (err, data) {
      if (err) {
        return res.end('404 Not Found')
      }
      res.end(data)
    })
  }
})


server.listen(3000, function () {
  console.log('server is running...')
})

# 静态资源使用

在响应的HTML文件中引入public下的CSS文件或者图片直接加载是不生效的,因为这些静态资源也是一个请求,如果我们没有做对应的请求响应是加载不到的,所以要对每一个静态资源做请求响应。我们把这些静态文件统一放在public文件夹下,只需要监听请求地址包含public目录就返回对应的文件内容就行。

var http = require('http')
var fs = require('fs')


// url模块的parse()方法可以解析url地址为一个json数据
var url = require('url')


var server = http.createServer()




server.on('request', function (req, res) {
  // 获取url对象
  var pathObj = url.parse(req.url, true)
  var pathName = pathObj.pathname // 获取路由


  console.log(pathName)
  if (pathName === '/' || pathName === '/index.html') {
    // 首页
    // 读取html模板渲染页面
    fs.readFile('./views/index.html', function (err, data) {
      if (err) {
        return res.end('404 Not Fond')
      }
      res.setHeader('Content-Type', 'text/html; charset=utf-8')
      res.end(data)
    })
  } else if (pathName.indexOf('/public') === 0) {
    // 读取静态文件,这里要单独配置才能读取静态文件
    // 把public文件下的静态资源全部开放出去
    fs.readFile('.' + pathName, function (err, data) {
      if (err) {
        return res.end('404 Not Found')
      }
      res.end(data)
    })
  } else {
    // 其他
    fs.readFile('./views/404.html', function (err, data) {
      if (err) {
        return res.end('404 Not Found')
      }
      res.end(data)
    })
  }
})


server.listen(3000, function () {
  console.log('server is running...')
})

如果是其他文件夹下的文件也可以做同样处理。

# HTML模板引擎

纯原生HTML进行数据渲染写的话会很不方便,可以借助和Vue类似的模板引擎来开发。这里我们使用 art-template 模板引擎。art-template 不仅可以在浏览器中使用,也可以在node中使用。

下载插件:

npm i art-template

在app.js中引入插件:

// app.js
var http = require('http')
var fs = require('fs')
// 引入art-template
var template = require('art-template')


// url模块的parse()方法可以解析url地址为一个json数据
var url = require('url')


var server = http.createServer()


server.on('request', function (req, res) {
  // 获取url对象
  var pathObj = url.parse(req.url, true)
  var pathName = pathObj.pathname // 获取路由


  console.log(pathName)
  if (pathName === '/tmp.html') {
    // 读取html模板渲染页面
    fs.readFile('./views/tmp.html', function (err, data) {
      if (err) {
        return res.end('404 Not Fond')
      }
      // 使用模板引擎
      var htmlStr = data.toString()
      var htmlRet = template.render(htmlStr, {
        name: 'jack',
        age: 19,
        hobby: 'dance'
      })
      res.setHeader('Content-Type', 'text/html; charset=utf-8')
      res.end(htmlRet)
    })
  }
})


server.listen(3000, function () {
  console.log('server is running...')
})

在HTML中使用:

// tmp.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>模板引擎</title>
  </head>
  <body>
    <div class="index">
      <p>大家好,我叫{{name}}</p>
      <p>我今年{{age}}岁</p>
      <p>我喜欢{{hobby}}</p>
    </div>
  </body>
</html>

# 表单提交

这里表单提交还是很原始的form表单提交,只做简单演示GET请求,提交的action地址自定义。

// app.js
var http = require('http')
var fs = require('fs')
var template = require('art-template')


// url模块的parse()方法可以解析url地址为一个json数据
var url = require('url')


var server = http.createServer()


// 渲染数据
var comments = [
  {
    name: '张三',
    message: '你好,我是张三',
    time: '2020/01/22'
  }
]


server.on('request', function (req, res) {
  // 获取url对象
  var pathObj = url.parse(req.url, true)
  var pathName = pathObj.pathname // 获取路由


  if (pathName === '/pinglun') {
    // 表单提交,获取数据
    var comment = pathObj.query
    comment.time = (new Date()).toLocaleDateString()
    comments.unshift(comment)
    // 获取数据后通过服务器让页面重定向到首页更新数据
    //     1、状态码设置为 302 临时重定向
    //      statusCode
    //     2、在响应头中告诉客户端往哪儿重定向
    //       setHeader
    res.statusCode = 302
    res.setHeader('Location', '/')
    res.end()
  }
})


server.listen(3000, function () {
  console.log('server is running...')
})

在HTML中提交form表单

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>发表留言</title>
  </head>
  <body>
    <div class="index">
      <form action="/pinglun" method="POST">
        <div class="form-group">
          <label for="name">你的名字</label>
          <input type="input" name="name" class="form-control" id="name" required minlength="2" maxlength="10" placeholder="name" />
        </div>
        <div class="form-group">
          <label for="textarea">留言内容</label>
          <textarea name="message" class="form-control" rows="5" class="form-control" id="textarea" required minlength="3" maxlength="20"></textarea>
        </div>
        <button type="submit" class="btn btn-default">发表</button>
      </form>
    </div>
  </body>
</html>

# 项目部署

搭建好的项目可以直接拷贝到云服务器上,和本地开启服务一样运行,并把云服务器对应的端口放开,在公网上就可以被访问到了。

参考链接:服务器使用教程

# 项目地址

Node搭建模板 (opens new window)