高效地构建具有多个视图的大型 React.js 应用程序
Posted
技术标签:
【中文标题】高效地构建具有多个视图的大型 React.js 应用程序【英文标题】:Efficiently structuring large React.js applications with multiple views 【发布时间】:2015-06-21 08:34:55 【问题描述】:我有一个带有多个视图的反应应用程序(例如Profile
、Followers
)。这些视图中的每一个当前都作为它自己的 React 组件存在,该组件在服务器端和客户端执行。我开始难以理解构建代码的正确方法。
例如,假设 Profile
已加载并且用户点击“查看关注者”。此时,调度员会收到一条消息,上面写着gotoFollowers
。在顶层,我们渲染一个全新的 React 组件(即Followers
组件)。不幸的是,Follower
和 Profile
组件都需要类似的数据(例如会话数据、用户照片、用户名,甚至关注者,因为我们在 Profile
上显示了关注者的 sn-p)。因此,每次用户更改页面时都必须在顶层重新渲染(包括进行 ajax 调用)感觉非常浪费。
我认为我没有正确执行此操作。我应该使用单个父 React 组件吗? (我也看到了问题)我应该如何构建具有许多视图的大型 React 应用程序?
window.render = function (page, id, pushState)
var stateURL = '';
switch (page)
case 'Profile':
stateURL = '/' + id;
async.parallel(
profileData: function (callback) MemberLoader.load(id, callback); ,
followerData: function (callback) FollowerLoader.load(id, callback); ,
,
sessionData: function (callback) Session.get(callback); ,
, function (err, results)
var component = React.createFactory(Profile);
React.render(component( member: results.profileData, followers: results.followerData, session: results.sessionData ), mountNode);
);
break;
case 'Followers':
stateURL = '/' + id + '/followers';
async.parallel(
profileData: function (callback) MemberLoader.load(id, callback); ,
sessionData: function (callback) Session.get(callback); ,
followerData: function (callback) FollowerLoader.load(id, callback); ,
, function (err, results)
var component = React.createFactory(Followers);
React.render(component( member: results.profileData, followers: results.followerData, session: results.sessionData ), mountNode);
);
break;
;
if (pushState)
window.history.pushState( page: page, id: id , null, stateURL);
else
window.history.replaceState( page: page, id: id , null, stateURL);
;
Dispatcher.register(function(event)
if (event.action === 'gotoProfile')
window.render('Profile', event.username, true);
else if (event.action === 'gotoFollowers')
window.render('Followers', event.username, false);
);
注意:我们的应用程序在服务器端和客户端都呈现。
【问题讨论】:
【参考方案1】:要解决多次获取相同数据的问题,您应该查看Promises- 这可以替换您现在正在使用的async
库。
Promises 允许您进行一次异步调用,但即使在调用成功后仍附加回调。您可以维护对 Promise 的引用并稍后在您的应用程序中使用它——而无需检查 Promise 是否已经解决。
我在下面为您整理了一个(未经测试的)示例实现 - 它做了几件事。
-
为每个数据存储创建对 Promise 的引用
创建一个方法,如果尚未进行异步调用,则返回该方法,然后返回该承诺
将您的
async.parallel
代码替换为 Promise.all
,这将创建一个新的 Promise,当所有传递的 Promise 都成功解析后,该 Promise 将被解析。
将您的 React.render
放入对承诺的 then
的调用中 - 这样只有在所有异步调用完成时才会调用它。
见下文:
var profilePromise;
var followerPromise;
var sessionPromise;
var getProfilePromise = function()
//If the profile promise already exists (ie, the async call has already been started)
//then we return the promise, otherwise we make the async call.
if(!profilePromise)
profilePromise = new Promise(function(resolve, reject)
MemberLoader.load(id, function()
//Depending on the result you either call `resolve` and pass
//in the data, or call `reject` with the error
);
);
return profilePromise;
;
//Repeat a version of the above function for your follower and session promise
window.render = function (page, id, pushState)
var stateURL = '';
switch (page)
case 'Profile':
stateURL = '/' + id;
Promise.all([
getProfilePromise(),
getFollowerPromise(),
getSessionPromise()
]).then(function(results)
var component = React.createFactory(Profile);
React.render(component( member: results[0], followers: results[1], session: results[2] ), mountNode);
).catch(function(err)
/** handle errors here */
);
break;
case 'Followers':
stateURL = '/' + id + '/followers';
Promise.all([
getProfilePromise(),
getFollowerPromise(),
getSessionPromise()
]).then(function(results)
var component = React.createFactory(Followers);
React.render(component( member: results[0], followers: results[1], session: results[2] ), mountNode);
).catch(function(err)
/** handle errors here */
);
break;
if (pushState)
window.history.pushState( page: page, id: id , null, stateURL);
else
window.history.replaceState( page: page, id: id , null, stateURL);
;
【讨论】:
以上是关于高效地构建具有多个视图的大型 React.js 应用程序的主要内容,如果未能解决你的问题,请参考以下文章