1

所以我正在开发 POC 应用程序,它需要有 SEO 优化的内容(=== 在服务器上呈现)。但是在同一条路线中,可能会有来自不同来源的动态数据,并且可能需要一段时间才能捕获所有这些数据。SEO 不需要此动态数据,因此将实施异步加载。

到目前为止,我所拥有的看起来像这样(请记住,这只是示例):

// routes.js
import serverRender from './services/aboutService';

const routes = (
  <Route path="/" component={Layout}>
    <IndexRoute component={Home}/>
    <Route path="/about">
      <Route
        path="/About"
        component={AboutContainer}
        serverRender={serverRender} />
    </Route>
  </Route>
);

// aboutService.js
const serverRender = () => {
    return axios.get("http://localhost:3002/services/about")
      .then(res => res.data)
      .catch(function (error) {
        console.log(error);
      });
}
export default serverRender;

// AboutContainer.js
import React from 'react';

export default class About extends Component {
  constructor(props){
    super(props);

    this.state = {
      data: { username: "", isFetching : true, hasError: false }
    };

    this._fetchNonSEOData = this.fetchNonSEOData.bind(this);
    console.log("CompetitionContainer constructor: ", props)
  }

  componentDidMount(){
    //this._fetchNonSEOData();
  }

  _fetchNonSEOData(){
    // dynamic content, not important for Crawlers
    // will call setState here to update state properties
  }

  render(){
    return (
      <div>
        <h1>User: {this.props.data.username}</h1>
        <div className="non-seo">
          {this._fetchNonSEOData()}
        </div>
      </div>
    )
  }
}

使用 ExpressJS 的 server.js(它的一小部分):

// server.js app.get('*', (req, res) => { ReactRouter.match({ routes, location: req.url }, (err, redirectLocation, renderProps) => {

    if (err) {
      return res.status(500).send(err.message);
    }

    if (redirectLocation) {
      return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
    }

    let markup;
    if (renderProps) {
      preRenderMiddleware(renderProps)
        .then(data => {

          const appElement = (
            <ReactRouter.RouterContext
              {...renderProps}
              createElement={(Component, props) => {
                                return <Component data={data} {...props} />;
                            }}
            />
          );
          //
          let markup = ReactDOMServer.renderToString(appElement);
          console.log("AppString:", markup)

          return res.render('index', { markup, head });
        })
    } else {
      // otherwise we can render a 404 page
      markup = ReactDOMServer.renderToString(<NotFoundPage/>);
      res.status(404);
    }
  });
});

所以你看到有 preRenderMiddleware 它返回已解决的 Promise ,这是我认为我可能错的部分:

//preRenderMiddleware.js
const defaultServerRender = () => Promise.resolve();

function preRenderMiddleware({ routes, location, params }) {
  const matchedRoute = routes[routes.length - 1];
  const serverRenderHandler = matchedRoute.serverRender || defaultServerRender;
  return serverRenderHandler(params);
}

所以我的问题是从服务器获取预期的标记 - 在 AboutContainer this.props.data 正在获取数据。但是,当客户端启动时, this.props.data 是未定义的。

任何人都可以指出一些好的解决方案吗?我确定我不是第一个遇到这个问题的人。我遇到了一些解决方案,但是客户端上所有重新请求的相同数据都是我希望避免的,这又会有点奇怪。

我也不想为此使用 Redux / 任何 Flux。

有什么想法吗?

4

1 回答 1

0

问题是您在服务器端而不是客户端拥有数据道具。您应该将其传递给客户端。我建议您尝试以下解决方案:

在服务器端,在您的示例“AboutContainer”组件中,将道具写入隐藏的 div 元素并在客户端读取它:

var canUseDOM = !!(
  (typeof window !== 'undefined' &&
  window.document && window.document.createElement)
);
export default class About extends Component {
  render(){
    initData(){
      if(!canUseDOM)return <div hidden id="propsData">{this.props.data}</div>
    }
    return (
      <div>
        <h1>User: {this.props.data.username}</h1>
        <div className="non-seo">
          {this._fetchNonSEOData()}
        </div>
        {initData()}
      </div>
    )
  }
}

现在在您的客户端:

const data  = document.getElementById("namee").innerHTML

我希望它有所帮助。

于 2017-03-22T14:49:08.830 回答