node 天生適合處理業務層面的邏輯,可是不適合做爲數據計算處理。在作web項目的時候,使用 node 來作中間服務器,處理客戶端的頁面渲染,轉發請求到接口服務器,實現先後端的分離,後端再也不須要管理前端的業務渲染,專一於接口的抽象化和靈活化,前端再也不須要等待路由頁面的添加才能寫頁面,能夠本身往路由添加須要的頁面。html
中間服務器的功能很是簡單,所以先後端能夠共同管理,前端負責添加頁面層級的路由,後端負責轉發到接口服務器的路由前端
上圖是淘寶基於Node的先後端分離分層,以及Node的職責範圍。簡單解釋下:node
最上端是服務端,就是咱們常說的後端。後端對於咱們來講,就是一個接口的集合,服務端提供各類各樣的接口供咱們使用。由於有Node層,也不用侷限是什麼形式的服務。對於後端開發來講,他們只用關心業務代碼的接口實現。web
服務端下面是Node應用。express
Node應用中有一層Model Proxy與服務端進行通信。這一層主要目前是抹平咱們對不一樣接口的調用方式,封裝一些view層須要的Model。json
Node層還能輕鬆實現原來vmcommon,tms(引用淘寶內容管理系統)等需求。後端
Node層要使用什麼框架由開發者本身決定。不過推薦使用express+xTemplate的組合,xTemplate能作到先後端公用。api
怎麼用Node你們本身決定,可是使人興奮的是,咱們終於可使用Node輕鬆實現咱們想要的輸出方式:JSON/JSONP/RESTful/HTML/BigPipe/Comet/Socket/同步、異步,想怎麼整就怎麼整,徹底根據你的場景決定。跨域
瀏覽器層在咱們這個架構中沒有變化,也不但願由於引入Node改變你之前在瀏覽器中開發的認知。瀏覽器
引入Node,只是把本該就前端控制的部分交由前端掌控。
server.js
var express = require('express') var rMiddle = require('./router/rMiddle.js') // remoteapi 開頭的轉發給接口服務器 app.use('/remoteapi', rMiddle); // 前端頁面渲染 app.get('/index', function(req, res) { res.sendFile(__dirname + 'index.html'); }) app.listen('8080', function(err) { if (err) { return; } console.log('Listening at localhost:8080'); });
/router/rMiddle.js
var express = require('express'); var http = require('http'); var querystring = require('querystring'); var router = express.Router(); // 你的接口服務器地址 var connectServer = { port: '9000', protocol: 'http://', host: '192.168.0.1' }; var serverDomainPort = connectServer.port; var serverDomainProtocol = connectServer.protocol; var serverDomainHost = connectServer.host var serverDomain = serverDomainProtocol + serverDomainHost + ':' + serverDomainPort; // 定義一個通用 Get 接口,轉接全部數據,再也不一個個寫 router.get('*', (req, res) => { let reqUrl = serverDomain + req.url; console.log('[GET Request]: ', reqUrl); http.get(reqUrl, (sres) => { var statusCode = sres.statusCode; var contentType = sres.headers['content-type']; let error; if (statusCode !== 200) { error = new Error(`Request Failed.\n` + `Status Code: ${statusCode}`); } else if (!/^application\/json/.test(contentType)) { error = new Error(`Invalid content-type.\n` + `Expected application/json but received ${contentType}`); } if (error) { // consume response data to free up memory sres.resume(); res.status(500).end(); return; } sres.setEncoding('utf8'); let rawData = ''; sres.on('data', (chunk) => rawData += chunk); sres.on('end', () => { try { let parsedData = JSON.parse(rawData); res.json(parsedData); } catch (e) { res.status(500).send(e.message); } }); }).on('error', (e) => { console.log(`Got error: ${e.message}`); res.status(500).end(); }); }) // 定義一個通用的 Post 接口,轉接全部數據 router.post('*', (req, res) => { var reqUrl = serverDomain + req.url; var reqContentType = req.headers['content-type']; var reqBody = req.body; // 根據 請求的 content-type 判斷用哪一種格式化方式 var reqData = reqContentType.indexOf('json') !== -1 ? JSON.stringify(reqBody) : querystring.stringify(reqBody); var postOpt = { host: serverDomainHost, port: serverDomainPort, path: req.url, method: 'POST', headers: { 'Content-Type': reqContentType } }; var sreq = http.request(postOpt, (sres) => { var body = ''; var error; if (sres.statusCode !== 200) { error = new Error(`Request Failed.\n` + `Status Code: ${sres.statusCode}`) } if (error) { console.log(error.message); sres.resume(); res.status(500).end(); return; } sres.on('data', (data) => { body += data; }) .on('end', () => { try { var parsedData = JSON.parse(body); res.json(parsedData); } catch (e) { console.log(e.message); res.status(500).send(e.message); } }) .on('error', () => { console.log('[ERROR] when req url:', reqUrl, reqData); res.status(500).send('error'); }) }) sreq.write(reqData); sreq.end(); }) module.exports = router;
中間服務器在轉發請求的時候,若是隻是單純的請求數據,那麼透傳是很好的方法,可是若是想作進一步的優化,可能就須要中間層作必定的處理。