Backbone和React单页应用的架构

15

到目前为止,我一直在大多数前端JavaScript项目中使用backbone.js,但是听说过 Facebook 的react.js后我就对它产生了兴趣,并开始探索。

我想知道我是否应该再使用Backbone.View类,还是用 react 的 "组件"替换每个"视图",甚至包括布局页面的"基本视图" ...

我在互联网上找到了一些帖子仍然使用Backbone.View - 其他则仅创建了 React 类。

也许有人可以指点我正确的方向......什么时候使用什么,以及如何在单页应用程序中最好实现多个“页面/状态”,而不仅是通常(我无法忍受)的 TODO 示例。

这是我想到的一些代码:

backbone 初始化:

require(
  [
    "jsx!app/view/base",
    "react",
    "app/router",
    "backbone"
  ],
  function (BaseView, React, Router, Backbone) {
    "use strict";

    var router = new Router();
    var base = new BaseView({router: router});

    React.renderComponent(base, document.getElementById("page"));

    router.on("route", function(action) {
      base.setProps({path: action});
    });

    Backbone.history.start({pushState: true});
  }
);

app/router:

define(function(require) {

  "use strict";

  var Backbone = require("backbone");

  /**
   *
   */
  var Router = Backbone.Router.extend({
    routes: {
      "": "home",
      "test": "test",
      "*error": "404"
    }
  }); // end Router


  return Router;
});

app/view/base.jsx:


define(function(require) {

  "use strict";

  var React = require("react");

  var mixins = require("app/utils/mixins");

  var Header = require("jsx!app/view/header");
  var ContentHome = require("jsx!app/view/content_home");
  var ContentTest = require("jsx!app/view/content_test");
  var ContentLogin = require("jsx!app/view/content_login");

  /**
   *
   */
  var BaseView = React.createClass({

    render: function() {
      var content;

      switch (this.props.path) {
        case "home":
          content = <ContentHome />
          break;
        case "test":
          content = <ContentTest />
          break;
        case "login":
          content = <ContentLogin />
          break;

        case "404":
        default:
          content = "Error, page not found";
          break;
      } 

      return (
        <div id="base" onClick={this.onClick}>
          <Header />
          {content}
        </div>
      );
    },

    onClick: function(event) {
      if (event.target.tagName.toLowerCase() === "a" &&
          event.target.className === "main") {
        event.preventDefault();
        this.props.router.navigate(event.target.pathname, {trigger: true});
      }
    }

  }); // end BaseView

  return BaseView;
});

但我并不完全满意这个设置。欢迎任何意见。


我在这里写了一个类似的答案:https://dev59.com/SGEh5IYBdhLWcg3wwlu8#22063571。告诉我是否有帮助? - chenglou
谢谢提供链接 - 你的回答看起来和我的方法差不多。 - Philipp Kyeck
2个回答

2
不要像你现在这样保留组件的引用。如果需要,使用refs,或者更好的做法是在回调函数中渲染组件。React会检查是否需要更新DOM。
require(
  [
    "jsx!app/view/base",
    "react",
    "backbone"
  ],
  function (BaseView, React, Backbone) {
    "use strict";

    var router = Backbone.Router.extend({
      routes: {
        "": "home",
        "test": "test",
        "login": "login",
        "*error": "404"
      }
    }).on('route', function(action) {
      React.renderComponent(
        <BaseView router={router} path={action} />,
        document.getElementById("page")
      );
    });

    Backbone.history.start({pushState: true});
  }
);

编辑: 由于BaseView中的逻辑与Router中的逻辑非常耦合,因此将它们保留在同一个文件中是最有意义的。
var BaseView = React.createClass({
  render: function() {
    return (
      <div id="base" onClick={this.onClick}>
        <Header />
        {this.props.children}
      </div>
    );
  },

  onClick: function(event) {
    if (event.target.tagName.toLowerCase() !== "a") return;
    if (event.target.className !== "main") return;

    event.preventDefault();
    this.props.router.navigate(event.target.pathname, {trigger: true});
  }
});

应用程序路由器:
var router = Backbone.Router.extend({
  routes: {
    "": "home",
    "test": "test",
    "login": "login",
    "*error": "404"
  }
}).on('route', function(action) {
  var pathMapping = {
    "home": ContentHome,
    "test": ContentTest,
    "login": ContentLogin
  };
  var Content = pathMapping[action] || Content404;

  React.renderComponent(
    <BaseView router={router}>
      <Content />
    </BaseView>,
    document.getElementById("page")
  );
});

Backbone.history.start({pushState: true});

但是你如何处理BaseView内部的path呢?你仍然需要switch,不是吗? - Philipp Kyeck
我认为你决定如何实现分支逻辑,即switch语句,比你把它放在哪里更重要,这更多是个人选择。没有所谓的“正确”答案。 如果你想知道我的看法,我更喜欢Khaled Jouda答案中的pathToComponent函数。 - Yazad D

1
  var BaseView = React.createClass({
    pathToComponent: function(){
        var paths = {
             'home': ContentHome,
             'login': ContentLogin
        };
        return paths[path] || Content404
    },
    render: function() {
      var ContentComponent = this.pathToComponent(this.props.path);
      return (
        <div id="base" onClick={this.onClick}>
          <Header />
          <ContentComponent />
        </div>
      );
    },

  }); // end BaseView

  return BaseView;
});

不错的语法。但需要考虑到 switch 不会执行所有的 case,而在这种方法中,即使以后那些页面并没有真正使用,你也会执行所有内容(包括 <ContentHome/><ContentLogin/>)。为了避免这种情况(同时也避免使用 switch),你可以为每个路径定义一个函数(例如 'home': function() { return <ContentHome/>; })。 - Mariano Desanze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接