React 服务器端渲染 - 如何使用传入的 :p roductId 参数从服务器渲染
React Server Side Rendering - how to render from the server with :productId params passed in?
我有一个在客户端和服务器(节点和快速(上呈现的React应用程序。如果有人输入 url http://webaddress.com/products/1,我正在尝试让渲染工作。如果我输入并按回车键(或刷新页面(,应用程序崩溃,因为它不知道如何获取 url 中的 1 来解析和获取正确的产品。
如果我单击导航中链接到产品/1的链接,则应用程序运行正常并显示正确的产品。
如何让 React-Router 从访问者 http://webaddress.com/products/1 输入的 url 中获取:productsId
(/products/1 中的 1(参数?
这是我的服务器.js:
import express from 'express';
import http from 'http';
var PageNotFound = require('./js/components/PageNotFound.react');
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import { routes } from './routes';
const app = express();
app.use(express.static('public'));
app.set('view engine', 'ejs');
/* We call match(), giving it the routes object defined above and the req.url, which contains the url of the request. */
app.get('*', (req, res) => {
// routes is our object of React routes defined above
match({ routes, location: req.url }, (err, redirectLocation, props) => {
if (err) {
// something went badly wrong, so 500 with a message
res.status(500).send(err.message);
} else if (redirectLocation) {
// we matched a ReactRouter redirect, so redirect from the server
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (props) {
// console.log("props on server.js: ", props);
// if we got props, that means we found a valid component to render
// for the given route. renderToString() from ReactDOM takes that RoutingContext
// component and renders it with the properties required.
const markup = renderToString(<RouterContext {...props} />);
// render `index.ejs`, but pass in the markup we want it to display
res.render('index', { markup })
} else {
// no route match, so 404. In a real app you might render a custom
// 404 view here
console.log("not found page");
res.sendStatus(404);
// respond with html page
if (req.accepts('html')) {
res.render('404', { url: req.url });
return;
}
// respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}
// default to plain-text. send()
res.type('txt').send('Not found');
}
});
});
const server = http.createServer(app);
app.set('port', (process.env.PORT || 3000))
app.get('/', (req, res) => {
var result = 'App is Running'
res.send(result);
}).listen(app.get('port'), () => {
console.log('App is running, server is listening on port', app.get('port'));
});
以下是路由.js文件:
import TopLevelContainerApp from './js/components/TopLevelContainerApp.react'
import Home from './js/components/Home.react';
import Product from './js/components/Product.react';
const routes = {
path: '',
component: SolidBroadheadApp,
childRoutes: [
{
path: '/',
component: Home
},
{
path: '/products/:productId',
component: Product
}
}
export { routes };
下面是客户端渲染 js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';
import { routes } from './../routes';
ReactDOM.render(
<Router routes={routes} history={browserHistory} />, document.getElementById('website-app')
);
以下是产品商店.js:
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var articles = null;
var links = null;
var product = null;
function setArticles(receivedArticles) {
articles = receivedArticles;
return articles;
}
function setLinks(receivedLinks) {
links = receivedLinks;
return links;
}
function setProduct(productId) {
console.log("products store productId: ", productId);
function filterById(obj) {
return obj.id === productId;
}
var filteredArticlesArr = articles.filter(filterById);
product = filteredArticlesArr[0];
return product;
};
function emitChange() {
ProductsStore.emit('change');
}
var ProductsStore = assign({}, EventEmitter.prototype, {
addChangeListener: function(callback) {
this.on('change', callback);
},
removeChangeListener: function(callback) {
this.removeListener('change', callback);
},
getArticles: function() {
return articles;
},
getLinks: function() {
return links;
},
getProduct: function() {
return product;
}
});
function handleAction(action) {
switch (action.type) {
case 'received_products_articles':
setArticles(action.articles);
emitChange();
break;
case 'get_links':
setLinks(action.articles);
emitChange();
break;
case 'get_product':
setProduct(action.productId);
emitChange();
break;
}
}
ProductsStore.dispatchToken = AppDispatcher.register(handleAction);
module.exports = ProductsStore;
下面是呈现特定产品的产品组件:
var React = require('react');
var ProductArticle = require('./products-solid-components/ProductArticle.react');
var ProductsStore = require('./../stores/ProductsStore');
// gets all the products
var ProductsArticlesWebUtilsAPI = require('./../../utils/ProductsArticlesWebUtilsAPI');
ProductsArticlesWebUtilsAPI.initializeArticles();
var Product = React.createClass({
getInitialState: function() {
return {
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
};
},
componentDidMount: function() {
ProductsStore.addChangeListener(this.onProductChange);
},
componentWillUnmount: function() {
ProductsStore.removeChangeListener(this.onProductChange);
},
onProductChange: function() {
this.setState({
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
});
},
render: function() {
if (this.state.articles)
if (this.state.product) {
return (
<div className="product">
<section className="container-fluid">
<ProductArticle name={this.state.product.name} largeImage={this.state.product.largeImage} description={this.state.product.description} />
</section>
);
} else {
return (
<div>Product is on the way</div>
);
}
}
});
module.exports = Product;
以下是获取信息的文件:
var ProductsActionCreators = require('../js/actions/ProductsActionCreators');
var productArticles = [
{
"id": 1,
"name": "product 1",
"largeImage": "1.png",
"largeVideo": "1.mp4",
"description": "1 description",
},
{
"id": 2,
"name": "product 2",
"largeImage": "2.png",
"largeVideo": "2.mp4",
"description": "2 description",
},
{
"id": 3,
"name": "product 3",
"largeImage": "3.png",
"largeVideo": "3.mp4",
"description": "3 description",
},
];
var products = [];
function separateProductIdsAndNamesOut(productArticles) {
console.log("productArticles: " + productArticles);
products = productArticles.map(function(product) {
return { id: product.id, name: product.name };
});
return products;
}
function initializeArticles() {
return ProductsActionCreators.receiveArticles(productArticles);
}
// to build links in a nav component without payload of video and large img etc
function initializeProductsForNav() {
return ProductsActionCreators.receiveArticlesIdsAndNames(separateProductIdsAndNamesOut(productArticles));
}
module.exports = {
initializeArticles: initializeArticles,
initializeProductsForNav: initializeProductsForNav
};
更新:控制台.log来自服务器.js当我手动输入 url 或在页面上点击刷新一次时:
props on server.js: { routes:
[ { path: '', component: [Object], childRoutes: [Object] },
{ path: '/products/:productId', component: [Object] } ],
params: { productId: '4' },
location:
{ pathname: '/products/4',
search: '',
hash: '',
state: null,
action: 'POP',
key: '8b8lil',
query: {},
'$searchBase': { search: '', searchBase: '' } },
components:
[ { [Function] displayName: 'SolidBroadheadApp' },
{ [Function] displayName: 'Product' } ],
history:
{ listenBefore: [Function],
listen: [Function],
transitionTo: [Function],
push: [Function],
replace: [Function],
go: [Function],
goBack: [Function],
goForward: [Function],
createKey: [Function],
createPath: [Function],
createHref: [Function],
createLocation: [Function],
setState: [Function],
registerTransitionHook: [Function],
unregisterTransitionHook: [Function],
pushState: [Function],
replaceState: [Function],
isActive: [Function],
match: [Function],
listenBeforeLeavingRoute: [Function] },
router:
{ listenBefore: [Function: listenBefore],
listen: [Function: listen],
transitionTo: [Function: transitionTo],
push: [Function: push],
replace: [Function: replace],
go: [Function: go],
goBack: [Function: goBack],
goForward: [Function: goForward],
createKey: [Function: createKey],
createPath: [Function: createPath],
createHref: [Function: createHref],
createLocation: [Function: createLocation],
setState: [Function],
registerTransitionHook: [Function],
unregisterTransitionHook: [Function],
pushState: [Function],
replaceState: [Function],
__v2_compatible__: true,
setRouteLeaveHook: [Function: listenBeforeLeavingRoute],
isActive: [Function: isActive] } }
this.state.searchResult: null
this.state.productLinks in component: null
更新2:我已经删除了404和splat路由,服务器控制台.log显示:
App is running, server is listening on port 3000
props.params on server.js: { productId: '4' } // looks good; proper productId is passed.
this.state.productLinks in component: null
this.props in product component: { productId: '4' } // looks good; should render properly
props.params on server.js: { productId: 'build.js' } // why is this assigned 'build.js' causing the problem???
this.state.productLinks in component: null
this.props in product component: { productId: 'build.js' } // why is this assigned 'build.js'? This is probably the cause of the problem.
该过程似乎运行了两次,第一次分配了正确的id,第二次分配了由webpack构建的"build.js"?跆拳道。
显示道具从 productId 重写为"build.js"的要点:https://gist.github.com/gcardella/1367198efffddbc9b78e
Webpack 配置:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: path.join(process.cwd(), 'client/client-render.js'),
output: {
path: './public/',
filename: 'build.js'
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel'
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
APP_ENV: JSON.stringify('browser')
}
})
]
}
更新 3:我通过删除状态检查修复了双循环,但这在渲染服务器端时失败,因为尚未设置状态。所以我添加了一个检查以查看产品组件中是否存在 state.product:
var React = require('react');
var ProductArticle = require('./products-solid-components/ProductArticle.react');
var ProductPresentation = require('./products-solid-components/ProductPresentation.react');
var ProductLargeImage = require('./products-solid-components/ProductLargeImage.react');
var LargeLeftImageArticle = require('./reusable-tool-components/LargeLeftImageArticle.react');
var LargeRightImageArticle = require('./reusable-tool-components/LargeRightImageArticle.react');
var ProductsStore = require('./../stores/ProductsStore');
// if (process.env.APP_ENV === 'browser') {
var ProductsArticlesWebUtilsAPI = require('./../../utils/ProductsArticlesWebUtilsAPI');
ProductsArticlesWebUtilsAPI.initializeArticles();
// }
var Product = React.createClass({
getInitialState: function() {
return {
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
};
},
componentDidMount: function() {
ProductsStore.addChangeListener(this.onProductChange);
},
componentWillUnmount: function() {
ProductsStore.removeChangeListener(this.onProductChange);
},
onProductChange: function() {
this.setState({
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
});
},
render: function() {
console.log("this.props in product component: ", this.props);
// if (this.state.articles)
console.log("after check for this.state.product on component this.props.params.productId: ", this.props.params.productId);
if (this.state.product) {
return (
<div className="product">
<section className="container-fluid">
<ProductArticle name={this.state.product.name} largeImage={this.state.product.largeImage} description={this.state.product.description} />
</section>
</div>
);
} else {
console.log("no state");
return (
// if there is no state, how do I render something on the server side?
);
}
}
});
module.exports = Product;
这根本不是路由问题。不同领域存在许多问题:
我不得不将bundle.js
重命名为另一个名称,例如 client-bundle.js
.
然后,我必须修复产品商店中的一个问题,以过滤产品数组以匹配产品 Id:
function setProduct(productId) {
var filteredProductsArr = products.filter(function(product) {
return product.id == productId;
});
product = filteredProductsArr[0];
return product;
};
然后我必须在产品组件的组件DidMount((中放置一个动作创建器: ProductsActionCreators.getProduct(this.props.params.productId);
所有这一切,它就像一个魅力:)
您应该能够使用
this.props.params.productId
一个很好的例子:https://github.com/rackt/react-router/blob/master/examples/query-params/app.js
- 使用JSP从服务器检索和显示图像
- 如何使用skip参数使用angular ui引导进行服务器端分页
- 客户端服务器REST API captcha实现
- 正在将base64 jpeg从input-type=file上传到服务器
- Webpack开发服务器和React服务器端渲染
- 提示使用服务器端事件处理程序激活JavaScript
- 使用谷歌应用程序脚本将服务器端数据表返回到客户端
- 如何使用Socket.io将命令从客户端发送到服务器
- jQuery blueimp文件上传:将N-1个文件上传到IE中的服务器
- 使用jasmine模拟对服务器的调用
- 如何轻松地将服务器端变量从Java代码转移到客户端代码
- 从客户端获取修改后的对象,并将其与服务器上的原始对象组合
- 通过ajax将坐标传递到php服务器端,并在处理后检索到javascript
- 如何使用Javascript将空数组发送到PHP服务器
- 如何在HTML中显示服务器对象变量
- 如何配置分析以将数据发送到我自己的服务器
- 我无法使用angularJs($http)访问服务器
- 如何检测第三方广告服务器请求
- 如何:浏览器将JSON发送到服务器
- React 服务器端渲染 - 如何使用传入的 :p roductId 参数从服务器渲染