React 同构 前后端渲染内容不同 如何优雅地解决?

发布于 2022-09-01 22:32:09 字数 1360 浏览 14 评论 0

使用 Meteor react-router-ssr 构建同构 react

  render() {
    return (
      <li>
        <h3>
          <Link to={`/post/${this.props._id}`}>{this.props.title}</Link>
          
          <small>{this.props.author}</small>
         {/* 注意这里的时间格式化 */}
          <small>{new Date(this.props.createAt).toLocaleDateString()}</small>
        </h3>
      </li>
    )
  }

前台浏览器 给出的是 2015/12/11
后台服务器 给出的是 Friday, December 11, 2015

这样react 就会提示

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) G9PCSif8CbL9Gi.0.4">2015/12/11</small></
 (server) G9PCSif8CbL9Gi.0.4">Friday, December 11,

说两端渲染内容不一致,会丢失服务端渲染的内容

我有一个hack方法就是使用 componentDidMout 来避免这个提示

但是这样时间并不能第一时间显示,会等到渲染完成后才出来.

请问怎么避免这类同构问题?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

戏蝶舞 2022-09-08 22:32:09

前段时间正好在做React同构相关的项目,分享一些经验给题主,其实同构要解决的核心问题就是:

  1. 前后端路由统一的处理

  2. 前后端数据同步

题主这边要解决的是第二个问题,其实这个问题无论是成熟的iso库还是自己实现,思路都是同一个,那就是:后端进行渲染的时候,将数据同步到前端,最简单暴力的方式是直接置入window.YOURDATA

前端代码

componentDidMount() {
      this.getUnreadMessagesCount();
    }

    componentWillUnmount() {
        this.Components = null;
        super.componentWillUnmount();
    }

    getUnreadMessagesCount() {
        let count = this.Components.length;
        if(!cUtil.isNode) {
          // 这段代码会在前后端一起执行,所以需要判断是否在node环境下
          store.set('unread', window.G.props.unread); // 直接挂载到window.G
        }
    }

    updateTabPanels() {
      this.forceUpdate();
    }

 
    getTabPanels(activeTab)  {
        if(this.props.unread) { store.set('unread', this.props.unread); } else {
          // 保证只会在client端才会执行
          this.getUnreadMessagesCount();
        }

        let unread = store.get('unread');

        return this.Components.map((Component, index) => {
            let key = cUtil.capCase(Component.key);
            let isActive = index === activeTab;
            let newMessageCount = unread[key] && unread[key].data !== 0;

            return <div key={key} tabTitle={Component.label} tabClassName={!isActive && newMessageCount ? 'new-messages' : ''}>
                <Component isActive={isActive} updateTabPanels={this.updateTabPanels.bind(this)} />
            </div>;
        });
    }

后端代码

这里我选取获取首页的控制器

* getIndex() {
    let allUnread = yield getAllCounts(this);
    let props = {
      'appName': this.params.appName,
      'unread': allUnread
    };

    // 挂载给同构中间件
    this.locals.data = props;
  }

同构部分的中间件代码逻辑

'use strict';
const Router = require('react-router');
const routes = require('../client/routes.jsx');
const React = require('react');
const renderToString = React.renderToString;
const createFactory = React.createFactory;
const debug = require('debug')('iso');

module.exports = function() {

  // 同构
  let getComponent = (routes, pathname) => {
    return new Promise((resolve, reject) => {
      Router.match({routes, location: { pathname: pathname }}, (err, rl, rp) => {
        if(err) {
          // 处理异常
          reject(err);
        } else if(rl) {
          // 处理重定向
          resolve(rl);
        } else {
          // 处理正确路由
          resolve(rp);
        }
      });
    });
  };

  return function*(next) {
    // 直接递交控制权给下一级中间件
    yield next;
    let com;

    // 处理自身逻辑
    if(/(\/static\/(.*))|(\/msg\/(.*))/i.test(this.url)) {
      // 不处理static和api路由
      return;
    }

    // 获取组件
    try {
      com = yield getComponent(routes, this.url);
    } catch(err) {
      this.status = 500;
      this.app.emit('error', err, this);
    }

    let path = com.routes[1].path;

    // debug
    debug(`请求path ${this.url} 匹配前端路由 ${path}`);

    // 如果前端路由走了*逻辑
    // 后端就直接throw 404
    if(path === '*') {
      this.status = 404;
      this.throw(404);
    } else {
      this.status = 200;
    }

    // 拿到props
    let props = this.locals.data;
    // 后端渲染
    yield this.render('index', {
      'reactHTML': renderToString(createFactory(com.routes[1].component)(props)),
      'props': JSON.stringify(props)
    });
  };
};

课外资料

这里还有一些补充资料可能对你有启发:

《React Server Side Rendering 解决 SPA 应用的 SEO 问题》

《React服务端渲染小结》

小苏打饼 2022-09-08 22:32:09

因为前后的js runtime不一致 本地化设定也不一致,建议封装自己的Date格式化函数,记得带上timezone,对unix时间戳进行格式化

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文