【问题标题】:Single page app with REST API architecture具有 REST API 架构的单页应用程序
【发布时间】:2016-02-09 10:44:52
【问题描述】:

我在Node.js 中构建了中型Web 应用程序。起初,我并没有计划它会长到中等大小,也不知道它是否会被使用。现在,随着客户的用户开始使用它,需要另外两个功能:

  • 移动应用程序
  • 单页应用

由于直到现在我使用服务器端模板(即我所有的API 路由都以HTML 响应),我需要做出巨大的改变以支持APIJSON 响应仅用于移动应用程序,所以我决定重构整个应用程序以同时支持这两件事。

阅读了一些在线资源(即Single Page App Book)并比较了可用的JavaScript 框架(Angular vs Backbone vs React vs Ember),我得出了以下结论。我的问题是,我错过了什么吗?所以,这就是我打算如何扩展我的网络应用程序:

  • 我将在React 中重写我所有的 UI 组件
  • 所有当前的API 路由仍会以HTML 响应,并且HTML 将使用服务器端server-rendered 响应server-rendered,但这些React UI 组件也将包含在浏览器端,这将支持单页应用程序功能。
  • 我会写专门的RESTAPI,可能是基于JSON API服务器与单页应用程序和移动Web应用程序通信的标准。
  • API 路由(API 响应页面 - HTML 和数据 - JSON)都将使用 Express 路由器完成,该路由器将执行 控制器(服务器组件) 用于封装与数据访问层(服务器组件)的数据组合操作。
  • 数据访问层基本上由Mongoose 模型组成。

由于实施和重构它需要更长的时间,我想确保我走在正确的轨道上。我在这里错过了什么吗?

【问题讨论】:

    标签: javascript node.js api reactjs


    【解决方案1】:

    如果您包含通量模式,它将使您的应用更易于构建和维护。我建议你看看一些入门项目,然后选择一个符合你自己风格的项目。这是来自https://github.com/calitek/ReactPatternsReact.14/ReFluxSuperAgent 的示例。它可能看起来很复杂,但该模式提供了良好的关注点和灵活性分离。

    server.js

    'use strict';
    
    let bodyParser = require('body-parser');
    let express = require('express');
    let favicon = require('serve-favicon');
    
    let path = require('path');
    let port = Number(3500);
    
    let routes = require('./routes');
    
    let app = express();
    let server = app.listen(port);
    
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    
    app.use('/', express.static('ui-dist'));
    app.use('/routes', routes);
    
    app.use(favicon(path.join(__dirname, '..', 'ui-dist', 'img', 'favicon.ico')));
    app.get('/', function(req, res){ res.sendfile(__dirname + '/index.html', [], null); });
    路由.js

    'use strict';
    
    let express = require('express');
    let router = express.Router();
    
    let getSetData = require('./routes/getsetdata');
    
    router.get('/getData', function(req, res) {
    	let getDataDone = function(data){ res.send(data); };
    	getSetData.getData(getDataDone);
    });
    
    router.post('/setData', function(req, res) {
    	let setDataDone = function(data){ res.send(data); };
    	console.log(req.body);
    	getSetData.setData(req.body, setDataDone);
    });
    
    module.exports = router;

    getsetdata.js

    'use strict';
    
    var fs = require('fs');
    
    var rootDataPath = './data';
    
    var getData = function(doneCallBack) {
    	var filePath = rootDataPath + '/basic.json';
    	var jsonReadCallBack = function(err, data){
    		if (err) doneCallBack('Data readFile error ' + filePath);
    		else {
    			var jsonData = JSON.parse(data.toString());
    			doneCallBack(jsonData);
    		}
    	};
    	fs.readFile(filePath, jsonReadCallBack);
    };
    
    var setData = function(data, doneCallBack) {
    	var filePath = rootDataPath + '/basic.json';
    	var writeFileCallBack = function (err) {
    		if (err) console.log('error saving Data.json file ');
    		doneCallBack('ok');
    	};
    	fs.writeFile(filePath, JSON.stringify(data, null, 2), writeFileCallBack);
    };
    
    module.exports.getData = getData;
    module.exports.setData = setData;

    index.html

    <!doctype html>
    <html lang="en">
    	<head>
    		<meta charset="utf-8">
    		<meta name="viewport" content="width=device-width, initial-scale=1.0">
    		<title>ReactPatterns-ReFluxWebSocket</title>
    
    		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css">
    		<link rel="stylesheet" href="app.min.css"/>
    
    	</head>
    	<body class="bodyStyle main">
    
    		<header class="text-center header" >
    			<span class="Title">ReactPatterns-ReFluxSuperAgent by Janaka</span>
    		</header>
    
    		<section id="react" class="content"></section>
    
    		<script src="app.min.js"></script>
    
    	</body>
    </html>

    app.js

    'use strict';
    
    import React  from 'react';
    import ReactDom  from 'react-dom';
    
    import AppCtrl from './components/app.ctrl.js';
    import Actions from './flux/Actions';
    import ApiStore from './flux/Api.Store';
    
    window.ReactDom = ReactDom;
    
    Actions.apiInit();
    
    ReactDom.render( <AppCtrl />, document.getElementById('react') );

    api.store.js

    import Reflux from 'reflux';
    
    import Actions from './Actions';
    import ApiFct from './../utils/api.js';
    
    let ApiStoreObject = {
    	newData: {
    		"React version": "0.14",
    		"Project": "ReFluxSuperAgent",
    		"currentDateTime": new Date().toLocaleString()
    	},
    	listenables: Actions,
    	apiInit() { ApiFct.setData(this.newData); },
    	apiInitDone() { ApiFct.getData(); },
    	apiSetData(data) { ApiFct.setData(data); }
    }
    const ApiStore = Reflux.createStore(ApiStoreObject);
    export default ApiStore;

    api.js

    import request from 'superagent';
    
    import Actions from '../flux/Actions';
    
    let uri = 'http://localhost:3500';
    
    module.exports = {
    	getData() { request.get(uri + '/routes/getData').end((err, res) => { this.gotData(res.body); }); },
    	gotData(data) { Actions.gotData1(data); Actions.gotData2(data); Actions.gotData3(data); },
    	setData(data) { request.post('/routes/setData').send(data).end((err, res) => { Actions.apiInitDone(); }) },
    };

    basic.store.js

    import Reflux from 'reflux';
    
    import Actions from './Actions';
    import AddonStore from './Addon.Store';
    import MixinStoreObject from './Mixin.Store';
    
    function _GotData(data) { this.data1 = data; BasicStore.trigger('data1'); }
    
    let BasicStoreObject = {
    	init() { this.listenTo(AddonStore, this.onAddonTrigger); },
    	data1: {},
    	listenables: Actions,
    	mixins: [MixinStoreObject],
    	onGotData1: _GotData,
    	onAddonTrigger() { BasicStore.trigger('data2'); },
    	getData1() { return this.data1; },
    	getData2() { return AddonStore.data2; },
    	getData3() { return this.data3; }
    }
    const BasicStore = Reflux.createStore(BasicStoreObject);
    export default BasicStore;

    app.ctrl.js

    import React from 'react';
    
    import BasicStore from './../flux/Basic.Store';
    
    let AppCtrlSty = {
    	height: '100%',
    	padding: '0 10px 0 0'
    }
    
    const getState = () => {
    	return {
    		Data1: BasicStore.getData1(),
    		Data2: BasicStore.getData2(),
    		Data3: BasicStore.getData3()
    	};
    };
    
    class AppCtrlRender extends React.Component {
     	render() {
    		let data1 = JSON.stringify(this.state.Data1, null, 2);
    		let data2 = JSON.stringify(this.state.Data2, null, 2);
    		let data3 = JSON.stringify(this.state.Data3, null, 2);
    		return (
    			<div id='AppCtrlSty' style={AppCtrlSty}>
    				React 1.4 ReFlux with SuperAgent<br/><br/>
    				Data1: {data1}<br/><br/>
    				Data2: {data2}<br/><br/>
    				Data3: {data3}<br/><br/>
    			</div>
    		);
    	}
    }
    
    export default class AppCtrl extends AppCtrlRender {
    	constructor() {
    		super();
    		this.state = getState();
    	}
    
    	componentDidMount() { this.unsubscribe = BasicStore.listen(this.storeDidChange.bind(this)); }
    	componentWillUnmount() { this.unsubscribe(); }
    	storeDidChange(id) {
    		switch (id) {
    			case 'data1': this.setState({Data1: BasicStore.getData1()}); break;
    			case 'data2': this.setState({Data2: BasicStore.getData2()}); break;
    			case 'data3': this.setState({Data3: BasicStore.getData3()}); break;
    			default: this.setState(getState());
    		}
    	}
    }

    【讨论】:

      猜你喜欢
      • 2013-02-21
      • 2018-12-10
      • 2021-06-07
      • 1970-01-01
      • 2014-11-12
      • 2012-04-09
      • 2012-03-25
      • 2012-06-19
      • 1970-01-01
      相关资源
      最近更新 更多