我不确定这更多是关于React还是Meteor的问题,也许两者都是,但我目前正在使用这两个框架构建一个Web应用程序并遇到了编程问题。我不是JavaScript开发人员,而是Java开发人员(我每天使用GWT),因此可能犯了一些新手错误。
我的应用程序不断增长,我有越来越多的React组件,大约有二十个左右。现在我已经拥有了一个良好的视图,我已经为我的组件添加了一些功能,但结果是我在React组件中添加了越来越多的逻辑,我相信这违反了MVC原则。
然而,我不知道如何将逻辑移动到“Meteor控制器组件”中。目前,我只使用Meteor作为其模型。我看过很多次这篇Pete Hunt的演讲以及他如何构建应用程序,但它只有一个“简单”的组件。
实际上,如果没有React,视图将在HTML文件中定义为模板。控制器将在JS文件中,逻辑将出现在那里。我可以清楚地看到视图和控制器之间的分离。
HTML文件(来自排行榜示例):
<template name="leaderboard">
...
</template>
<template name="player">
<div class="player {{selected}}">
...
</div>
</template>
Javascript文件(来自排行榜示例):
...
Template.leaderboard.players = function () {
return Players.find({}, {sort: {score: -1, name: 1}});
};
Template.leaderboard.selected_name = function () {
var player = Players.findOne(Session.get("selected_player"));
return player && player.name;
};
...
由于React是Javascript编写的,因此很容易而且很诱人地把我们想要的所有内容都放在React组件中。
我知道这些框架对每个人来说相对较新,但我想知道是否存在一些关于如何设计这样一个MVC应用程序的约定,以便拥有灵活和可维护的Web应用程序,是否有任何指导方针可以遵循? 我不是在寻找“最佳”方法,而是在寻找一些意见。
备注:我故意没有放太多代码在这里,以免重点集中在它上面,但你可以随意使用任何你想要的东西来说明你的答案(代码、图表、链接等)。
这是我正在做的一个示例。在这个例子中,所有的事情都是在React类中完成的,也许这是最好的方法,也许不是,我需要你们的想法。
总之,它从作为输入的数组(类似[{name: itemName1, type:itemType1}, {name: itemName2, type:itemType2} ...]的东西)创建了一个元素列表(Boostrap列表组),生成了一个视图,如下所示:
- itemName1
- itemName2
- ...
每个项目都有其自己的样式,根据其类型进行设置。然后通过输入文本框,用户可以在此列表中进行搜索,它会过滤列表并生成一个由匹配元素组成的新列表(搜索算法不正确,将被更改)。此外,还有某些键盘按键的附加命令。一切都很好,但是你可以注意到,所有的东西都在React类中,我无法弄清楚如何将Meteor与React结合使用。
Meteor文件:
if (Meteor.isClient) {
Meteor.startup(function() {
//Build the view
React.renderComponent(
<Search items={initialItems}/>,
document.getElementById('main')
);
});
}
React文件:
Search = React.createClass({
getInitialState : function() {
return (
{
items : flat(this.props.items),
active : 0
}
);
},
setListVisibility: function(visibility) {
this.refs.list.getDOMNode().style.visibility = visibility;
},
onchangeHandler: function() {
var re = new RegExp(this.refs.search.getDOMNode().value, "i");
var res = [];
//filter on props.items and not state.items
flat(this.props.items).map(function(item){
if(item.name.search(re) >= 0)
res.push(item);
});
this.setState({ items : res, active : 0});
},
onkeydownHandler: function(event){
if(event.key == "ArrowDown" || event.key == "ArrowUp"){
var shift = event.key == "ArrowDown" ? 1 : -1;
var newActive = this.state.active + shift;
if(newActive >= 0 && newActive < this.state.items.length)
this.setState({ active : this.state.active + shift });
} else if(event.key == "ArrowRight"){
if(this.state.items.length > 0){
var item = this.state.items[this.state.active];
var newItems = retrieveItem(this.props.items, item.name, typeToSubType[item.type]);
newItems = flat(newItems);
if(newItems.length > 0)
this.setState({ items : newItems, active : 0 });
}
} else if(event.key == "ArrowLeft"){
this.setState({ items : flat(this.props.items), active : 0});
} else if(event.key == "Enter"){
if(this.state.items.length > 0){
var item = this.state.items[this.state.active];
console.log("Add "+item.name+" "+item.type+" to the view");
}
}
},
render: function () {
return (
<div>
<nav className="navbar navbar-default" role="navigation">
<div className="container-fluid">
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<form className="navbar-form navbar-left" role="search">
<div className="form-group">
<input ref="search" type="text" className="form-control" placeholder="Search" size="100"
onChange={this.onchangeHandler}
onKeyDown={this.onkeydownHandler}
onFocus={this.setListVisibility.bind(this, "visible")}
onBlur={this.setListVisibility.bind(this, "hidden")}/>
</div>
</form>
</div>
</div>
</nav>
<List ref="list" items={this.state.items} active={this.state.active}/>
</div>
);
}
});
List = React.createClass({
render: function () {
var createItem = function(item, index) {
var cl = "list-group-item";
if(index == this.props.active)
cl += " active";
var gly = "glyphicon ";
switch(item.type){
case "dimension":
gly += "glyphicon-certificate";
break;
case "hierarchy":
gly += "glyphicon-magnet";
break;
case "level":
gly += "glyphicon-leaf";
break;
case "measure":
gly += "glyphicon-screenshot";
break;
}
return (<a href="#" className={cl} key={item.type+"/"+item.name}>
<span className={gly}></span>{" "}{item.name}
</a>);
};
return (
<div className="list-group search-list">
{this.props.items.map(createItem, this)}
</div>
);
}
});