2016-02-12 42 views
1

在我的反應應用程序中,我目前使用服務器端渲染。我目前得到的錯誤是:React checksum invalid server rendering

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) <noscript data-reacti 
(server) <div data-reactid=".1 

Server.js:

import 'babel-polyfill'; 
import path from 'path'; 
import express from 'express'; 
import React from 'react'; 
import ReactDOM from 'react-dom/server'; 
import { match, RouterContext } from 'react-router'; 
import assets from './assets'; 
import { port } from './config'; 
import routes from './routes'; 
import ContextHolder from './core/ContextHolder'; 
import Html from './components/Html'; 

const server = global.server = express(); 

// 
// Register Node.js middleware 
// ----------------------------------------------------------------------------- 
server.use(express.static(path.join(__dirname, 'public'))); 

// 
// Register API middleware 
// ----------------------------------------------------------------------------- 
server.use('/api/content', require('./api/content').default); 

// 
// Register server-side rendering middleware 
// ----------------------------------------------------------------------------- 
server.get('*', async (req, res, next) => { 
    try { 
    match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { 
     if (error) { 
     throw error; 
     } 
     if (redirectLocation) { 
     const redirectPath = `${ redirectLocation.pathname }${ redirectLocation.search }`; 
     res.redirect(302, redirectPath); 
     return; 
     } 
     let statusCode = 200; 
     const data = { title: '', description: '', css: '', body: '', entry: assets.main.js }; 
     const css = []; 
     const context = { 
     insertCss: styles => css.push(styles._getCss()), 
     onSetTitle: value => data.title = value, 
     onSetMeta: (key, value) => data[key] = value, 
     onPageNotFound:() => statusCode = 404, 
     }; 
     data.body = ReactDOM.renderToString(
     <ContextHolder context={context}> 
      <RouterContext {...renderProps}/> 
     </ContextHolder> 
     ); 
     data.css = css.join(''); 
     const html = ReactDOM.renderToStaticMarkup(<Html {...data} />); 
     res.status(statusCode).send(`<!doctype html>\n${html}`); 
    }); 
    } catch (err) { 
    next(err); 
    } 
}); 

// 
// Launch the server 
// ----------------------------------------------------------------------------- 
server.listen(port,() => { 
    /* eslint-disable no-console */ 
    console.log(`The server is running at http://localhost:${port}/`); 
}); 

client.js:

import 'babel-polyfill'; 
import React from 'react'; 
import { match, Router } from 'react-router'; 
import { render } from 'react-dom'; 
import FastClick from 'fastclick'; 
import routes from './routes'; 
import Location from './core/Location'; 
import ContextHolder from './core/ContextHolder'; 
import { addEventListener, removeEventListener } from './core/DOMUtils'; 

let cssContainer = document.getElementById('css'); 
const appContainer = document.getElementById('app'); 
const context = { 
    insertCss: styles => styles._insertCss(), 
    onSetTitle: value => document.title = value, 
    onSetMeta: (name, content) => { 
    // Remove and create a new <meta /> tag in order to make it work 
    // with bookmarks in Safari 
    const elements = document.getElementsByTagName('meta'); 
    [].slice.call(elements).forEach((element) => { 
     if (element.getAttribute('name') === name) { 
     element.parentNode.removeChild(element); 
     } 
    }); 
    const meta = document.createElement('meta'); 
    meta.setAttribute('name', name); 
    meta.setAttribute('content', content); 
    document.getElementsByTagName('head')[0].appendChild(meta); 
    }, 
}; 

function run() { 
    const scrollOffsets = new Map(); 
    let currentScrollOffset = null; 

    // Make taps on links and buttons work fast on mobiles 
    FastClick.attach(document.body); 

    const unlisten = Location.listen(location => { 
    const locationId = location.pathname + location.search; 
    if (!scrollOffsets.get(locationId)) { 
     scrollOffsets.set(locationId, Object.create(null)); 
    } 
    currentScrollOffset = scrollOffsets.get(locationId); 
    // Restore the scroll position if it was saved 
    if (currentScrollOffset.scrollY !== undefined) { 
     window.scrollTo(currentScrollOffset.scrollX, currentScrollOffset.scrollY); 
    } else { 
     window.scrollTo(0, 0); 
    } 
    }); 

    const { pathname, search, hash } = window.location; 
    const location = `${pathname}${search}${hash}`; 

    match({ routes, location }, (error, redirectLocation, renderProps) => { 
    render(
     <ContextHolder context={context}> 
     <Router {...renderProps} children={routes} history={Location} /> 
     </ContextHolder>, 
     appContainer 
    ); 
    // Remove the pre-rendered CSS because it's no longer used 
    // after the React app is launched 
    if (cssContainer) { 
     cssContainer.parentNode.removeChild(cssContainer); 
     cssContainer = null; 
    } 
    }); 

    // Save the page scroll position 
    const supportPageOffset = window.pageXOffset !== undefined; 
    const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat'); 
    const setPageOffset =() => { 
    if (supportPageOffset) { 
     currentScrollOffset.scrollX = window.pageXOffset; 
     currentScrollOffset.scrollY = window.pageYOffset; 
    } else { 
     currentScrollOffset.scrollX = isCSS1Compat ? 
     document.documentElement.scrollLeft : document.body.scrollLeft; 
     currentScrollOffset.scrollY = isCSS1Compat ? 
     document.documentElement.scrollTop : document.body.scrollTop; 
    } 
    }; 

    addEventListener(window, 'scroll', setPageOffset); 
    addEventListener(window, 'pagehide',() => { 
    removeEventListener(window, 'scroll', setPageOffset); 
    unlisten(); 
    }); 
} 

// Run the application when both DOM is ready and page content is loaded 
if (['complete', 'loaded', 'interactive'].includes(document.readyState) && document.body) { 
    run(); 
} else { 
    document.addEventListener('DOMContentLoaded', run, false); 
} 

這是我第一次探討了服務器端呈現。我知道這意味着服務器和客戶端正在推銷兩種不同的東西,所以客戶端必須重新渲染任何東西。它沒有破壞任何東西,但我想知道如何解決這個問題,這樣警告就會消失。

+0

我並不真的相信我的答案修復該問題。添加另一個'div'只是讓信息消失。如果您以慢動作觀看頁面加載,則會顯示服務器端,然後變爲白色,然後加載到客戶端,就像獲取錯誤一樣。我對這個問題的描述和你的一樣,我們都在使用這麼多的插件,因爲我沒有這樣做,所以我在裏面添加了一個

+0

看起來這實際上是由反應路由器中的異步路由引起的。目前我唯一的解決方案是刪除它們。當我得到真正的解決方案時,我會提交一個錯誤並正確更新我的答案。 – Sawtaytoes

回答

1

此問題與異步路由有關。我找不到其他解決方案,而不是更改所有要同步的路由。在加載異步路由時,客戶端添加的<noscript>標記放置在那裏。

例如,更改:

const routes = { 
    path: '/', 
    getComponent: function(location, cb) { 
     require.ensure([], function(require) { 
      return cb(null, require('./views/homepage')); 
     }) 
    }; 
}; 

進入這個:

const routes = { 
    path: '/', 
    getComponent: function(location, cb) { 
     return cb(null, require('./views/homepage')); 
    }; 
}; 

編輯:要重新啓用異步路線,在客戶端路由器做到這一點:

match({ history, routes }, (error, redirectLocation, renderProps) => { 
    render(<Router {...renderProps} />, mountNode) 
}) 

答案編輯感謝@firasd這裏:Async Routes Causes Server-Side Checksum Invalid Error