问题描述
我正在尝试获得一个同构的Node.js,Express,Webpack,React应用程序。我收到以下错误。任何关于如何解决它的建议?
警告:尝试在容器中重用标记,但校验和无效。这通常意味着您正在使用服务器呈现,并且在服务器上生成的标记不是客户端期望的。重新注入新的标记来补偿哪些工作,但是您已经失去了服务器呈现的许多好处。相反,找出为什么生成的标记在客户端或服务器上不同:
(客户端)rgin:0; display:flex; -webkit-align-items:
(server)rgin:0;显示:flex; align-items:center; j
警告@ warning.js:45
ReactMount._mountImageIntoNode @ ReactMount.js:807
wrapper @ ReactPerf.js:66
mountComponentIntoNode @ ReactMount.js:268
Mixin.perform @ Transaction.js:136
batchedMountComponentIntoNode @ ReactMount.js:282
Mixin.perform @ Transaction.js:136
ReactDefaultBatchingStrategy.batchedUpdates @ ReactDefaultBatchingStrategy.js:62
batchedUpdates @ ReactUpdates.js:94
ReactMount._renderNewRootComponent @ ReactMount.js:476
wrapper @ ReactPerf.js:66
ReactMount._renderSubtreeIntoContainer @ ReactMount.js:550
ReactMount.render @ ReactMount.js:570
wrapper @ ReactPerf.js:66
(匿名函数)@ client.jsx:14
(匿名函数)@ iso.js:120
e ach @ iso.js:21
bootstrap @ iso.js:111
(匿名函数)@ client.jsx:12
__webpack_require__ @ bootstrap d56606d95d659f2e05dc:19
(匿名函数) @ bootstrap d56606d95d659f2e05dc:39
(匿名函数)@ bootstrap d56606d95d659f2e05dc:39
这是什么最初由服务器传送到浏览器:
<!doctype html>
< html lang =>
< head>
< title>我的标题< / title>
< meta name =apple-mobile-web-app-titlecontent =我的标题data-react-helmet =true/>
< meta name =apple-mobile-web-app-status-bar-stylecontent =blackdata-react-helmet =true/>
< meta name =apple-mobile-web-app-capablecontent =yesdata-react-helmet =true/>
< meta name =mobile-web-app-capablecontent =yesdata-react-helmet =true/>
< meta name =viewportcontent =width = device-width,initial-scale = 1,user-scalable = nodata-react-helmet =true/>
< meta name =descriptioncontent =我的描述。 data-react-helmet =true/>
< meta http-equiv =X-UA兼容content =IE = edgedata-react-helmet =true/>
< meta charset =utf-8data-react-helmet =true/>
< link rel =stylesheethref =/ assets / styles / reset.cssdata-react-helmet =true/>
< link rel =stylesheethref =/ assets / styles / base.cssdata-react-helmet =true/>
< link rel =stylesheethref =/ assets / styles / Carousel.cssdata-react-helmet =true/>
< link rel =stylesheethref =/ assets / styles / main.cssdata-react-helmet =true/>
< link rel =stylesheethref =https://fonts.googleapis.com/css?family=Roboto+Condensedtype =text / cssdata-react-helmet =true/ >
< link rel =iconhref =/ assets / 185bb6f691241307862b331970a6bff1.icotype =image / x-icondata-react-helmet =true/>
SCRIPT
< / head>
< body>
< script src =https://cdn.firebase.com/js/client/2.2.7/firebase.js>< / script>
< script src =https://cdn.firebase.com/libs/reactfire/0.4.0/reactfire.min.js>< / script>
< div class =app>
< div class =___ iso-html ___data-key =_ 0>< div data-reactid =1hkqsbm9n9cdata-react-checksum =794698749>< div data- reactid =。1hkqsbm9n9c.0>< div data-reactid =1hkqsbm9n9c.0。$ = 10< / div>< div style =position:fixed; z-index:顶部:0;左:0;右:0;高度:60像素;颜色:RGB(219219219);字体家庭:mainnextcondensed_ultralight;字体大小:知识+;溢出:隐藏; data-reactid =。1hkqsbm9n9c.0。$ / = 11>< div style =position:absolute; left:0; top:0; background-color:rgba(27,27,27,0.92);填充右:35px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 10>< div style =float:left; height:60px; width:13px; border-left:5px solid rgb 45164); data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 10。$ / = 10>< / div>< div style =float:left; height:60px; width:227px;背景图像:URL(数据:图像/ PNG; BASE64,iVBORw0KGgoAAAANSUhEUgAAAOMAAAAhCAYAAAArrhzzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACxNJREFUeNrsXF9sHEcZHx927MZJvU5cSw4FrxXahja451Q88ADeq5Aq0aKeKyh9oMqdyFNA9PzAY3U2vPDE3SGBxNOdaZHKC3c8FCgqvXP73PjqFiSSIl9oSSTXJOe4juv8M / OTV / F9O57ZP + dzaWF + ysjZ3dmd2dn5ze / 7vpm5LtYGFr / 0otV7wsrdXr1x8dbl6zV + qj7 + 9nebUZ6xtbXFDAwMWoiFJZ90qjjw7eOp0fJj2XtfeLR6d9Je4nls05wGBu2jO4CENlfABZ7YInsxwU + B + mV77x9Ixg73uHl67xtgR39w0rpWaQAZG6ZJDQw6TEZQw77xo + WRn33Fih3qYQfG7l6Av / 2TIwz + euSVHx / + 5miOE3aam6s106wGBtHR5UPG4vDzj6QOP / 750A9be / mf7Mqv / PA / tbwxG + RDGp / RwCAEGTkRU + AXgin6uV8 / yrpHDoZ + 4J0Pb7LlH79Z / + idK43Dj486zRfOpzkxK4aMBgYRyciJGOd / qjy5QZtjv / wqu + vUUNsF / PsXf21yQiY4IeuGjAYGIX1GjJoWBRFBEVVE3Di3wtZfv8RunF9loJ79Xzum9CUBR7 // kHV7eaPM / cmJqNMfXV1dofJhveN46JlmwcHFfR / jzxp8asjIkSOdWklE8AuXf / Km59z6 / GUWy / ewI2e + YAA + c3zXPUM / ethee + U9MH3z + / QeZZ4c / D9EfWtIRAdVPtBHNjD4xJAR / cQUvdgz0u / JfOvy 9V1E3PEV126yldwi2zzfZMPPP + K59uGr / wKlquzHC / B6zxAiympZNp + 4S + Duhc3 / 2MIK4dZL09Sxg2REUy5HFXGQq1zQXUr8r8NuXx8bwdIpVC10ySPX / szJBm0w4yijsJqAYXOk / rBNyuBegtV7DB2BtEQ3yawvWMYZYSHzomToGowX + iLA1us7 + RwK0iTPsX6vzHs8S / XX7 + 8c3zo6 / daHVSrJHZegYoUhLIU59vFpGh4JA6YKrOYmholzRIigulaArOUJ + goY1ivmuiM2Elr + GEthbln0 + 8TBUiOKWyTXJuDHsOBSIc0aXsZBTKg0PcS / aESQMbnUCzgOZaswPicDJq5eU0b7AcRGekDRfm7tYOYymcEvPfsa2wlv8i8pus9JGLD / 63f8EZNv3eKsYO3SZT1Uss5HTkI0dYsLyujWHge1TylpgGQ4y3JhGBEyfYKYbLFedmuucpJNYOpoTEpBaZkSwCOeYLzQFKqNHNY96SiMwrrhe2BkHUV2UMEQuKoik2f5wtfOI73yH5rXfFewnyf8ylfWCVzqDwNRYwgSdrv40YDCWn5mKvhySjPLVJlA99v80LLTzw4OUIcyBhb + 8uSNxp0T7 + rmDtZLnh9zMEzJ + yxV5 / IwfarPdS5TOoq1EaFqQ6ZxCWigK7Jx9tsCdVZhVHRCTVk1XVoUc5pRWer + AUuQuL3xP / X + FqKlCImP5MGOx3eku6R1dGWfOIwpncW89QI4Ww0vZlUXlT1O616d78gi + a75YlV44QY4LTtHUObPK67efWlVgQVdmjQucf1Py2z1T / 8fed4s3GVM7A1lbcpB3zQf + x76IjdpipmiF / rp3z5Tk3wI6HAtMwTUtpIyrjPrc02ie8IdcHOa + 3zqK8KJjQ6XEaFDjMK2Nq6LMVic8tpkLcb9uJ6YcAdaxjf7H9fGFde9PfwNEu4l57 + ALY / OCZE + 7EPpitYukcTmG4W69k3Fi6FinCqJjGqKj2RpJRKSwSUpTUj7 x5tntOEd4h4fcu / Foa69RQ + GFTeF1nGlakCGhY5MigUkZVL6DC6NqtqklxKdIrlofBih4XRHETKkXyGWj8TO9MiKCVeE6W1DFBVHwJ63cV + w8QdeJjWqOaD4jDaNu7S + r8RaZZcgQEA / LtBRenXpl98M9PzeAIEZj / 7fHfWJL5HPlX6FBZBUFqGsKrTPSmvAcTg0gNaYuWdvuWXI // hS1cqC477 / sJXSgeJ / GFT81C8S5FZ1vQZR6aHnd3ZcBCcCCnav + iLxmf + GPpweq30mHJaGDw / wSZjBkWcskUmKVHM + ORfgUAltldOvsGbDKuGzIaGEgBHIU / Ewowoc + JtWvJHAXMT9LdG / KKHAMDA40yRlVHAfo7OUDA7S1UH + wQ0Q3w3GfBTo40RjONmWpgEERGJOQMcIi19t / 5AsgmNhOjKborGMK2N + OWiJNtWt / AIIiMEjGTzDtRrQWoI0xtSIsBgIjm1 + EMDPZKxr2Yr6y1n0 / 1swSm9Q0MCGJhM + Lkt26 + BsxPmLKASCn9uYNEB3f2GxgYZZRMVlj1ICZ9a2x7eVojasFGGQ0M9kBGQkrwHy3ZDzRkNDBoH / 8RYAC6QbxY8FBYtQAAAABJRU5ErkJggg ==);背景重复:不重复;背景位置:中心; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 10。$ / = 11>< / div>< div style =display:none; width:0; height:0; border-style:solid; border-width:6px 6px 0 6px; border-color:rgb(117,117,117)transparent transparent transparent; -webkit-transform:rotate(360deg); float:left; margin-left:6px; margin-top :26px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 10。$ / = 12>< / div>< / div>< div style =position:absolute; top:0px ;左:280像素;宽度:340px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11>< div style =background-color:rgba(27,27,27,0.92); height:10px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11。$ / = 10>< / div>< div style =background-color:rgba(53,53,53, 0.84);高度:40像素;位置:相对的; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11。$ / = 11>< div style =position:absolute; top:0; bottom:0; left:0; right :0;填充:0;余量:0;显示:弯曲;对齐项:中心;证明含量:中心; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11。$ / = 11。$ = 10>< div style =background-image:url(&#x27; / assets /3bec3e57cb5ee05658440d21984fb7b7.png');background-repeat:no-repeat;background-position:-58px -194px; width:23px; height:22px; position:absolute; top:50%; left:10px; margin-顶部:-11px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11。$ / = 11。$ = 10. $ icon>< / div>< / div>< div style =位置:绝对;左:40像素;右:40像素;顶:0像素;底部:0像素; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11。$ / = 11。$ / = 12>< input type =textstyle =width:100% 100%;字体大小:14px的;字型家族:mainnext_regular;背景色:透明;颜色:#FFFFFF; placeholder =SEARCH ARTISTS,TRACKS,ALBUMSdata-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11。$ / = 11。$ / = 12.0/>< / div> / div>< div style =background-color:rgba(27,27,27,0.92); height:10px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 11。$ / = 12>< / div>< / div>< div style =position:absolute; top:0px ;左:620px;右:0像素;背景色:RGBA(27,27,27,0.92);高度:60像素;线高度:60像素;溢出:隐藏;最小宽度:500像素;填充左:10px的; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 12>< div style =position:absolute; top:0px; bottom:0px; right:0px; width:357px; padding-左:141px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 12.0>< a class =href =/ importdata-reactid =1hkqsbm9n9c.0 $ / = 11。 $ / = 12.0。$ / = 10>< div style =padding-left:40px; position:absolute; left:0px; top:10px; bottom:10px; cursor:pointer; line-height:40px;颜色:RGB(255,255,255); data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 12.0。$ / = 10. $ import>< div style =position:absolute; top:0; bottom:0; left: 0;右:0;填充:0;余量:0;显示:弯曲;对齐项:中心;证明含量:中心; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 12.0。$ / = 10. $ import。$ = 10< div style =background-image:url(&#x27 ; /assets/3bec3e57cb5ee05658440d21984fb7b7.png&#x27;); background-repeat:no-repeat; background-position:0px -194px; width:28px; height:28px; position:absolute; top:50%; left:0px;边距:-14px;数据reactid = 1hkqsbm9n9c.0 $ / = 11 $ / = 12.0 $ / = 10 $进口$ = 10 $图标。。。 >< / DIV>< / DIV><跨度data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 12.0。$ / = 10. $ import.1>导入播放列表< / span>< / div>< / a> div style =margin-left:10px; data-reactid =。1hkqsbm9n9c.0。$ / = 11。$ / = 12.0。$ admin / = 1 $ admin>< div style =cursor:pointer; float:left;数据reactid = GT 1hkqsbm9n9c.0 $ / = 11 $ / = 12.0 $管理员/ = 1 $管理员$登录。。。。;登录< / DIV>< / DIV>< / DIV>< / div>< / div>< / div>< noscript data-reactid =1hkqsbm9n9c.1>< / noscript>< / div>< / div>
< div class =___ iso-state ___data-key =_ 0data-meta ={}data-state =& quot; {\& quot; UserStore\& QUOT;:{\&安培; QUOT; user\&安培; QUOT;:{\&安培; QUOT; authenticated\&安培; QUOT;:假,\&安培; QUOT; isWaiting\&安培; QUOT;:假}},\&安培; QUOT; SearchStore\&安培; QUOT;:{\&安培; QUOT; focused\&安培; QUOT;:假,\&安培; QUOT; input\&安培; QUOT;:\ &安培; QUOT; \&安培; QUOT;,\&安培; QUOT; timeout\&安培; QUOT;:空,\&安培; QUOT; searchRequests\&安培; QUOT;:[],\&安培; QUOT; artists\&安培; QUOT;:空,\&安培; QUOT; artistsFailed\&安培; QUOT;:假,\&安培; QUOT; artistsLoading\&安培; QUOT;:假,\&安培; QUOT; tracks\\ \\&安培; QUOT;:空,\&安培; QUOT; tracksFailed\&安培; QUOT;:假,\&安培; QUOT; tracksLoading\&安培; QUOT;:假,\&安培; QUOT; albums\&安培; QUOT;:空,\&安培; QUOT; albumsFailed\&安培; QUOT;:假,\&安培; QUOT; albumsLoading\&安培; QUOT;:假,\&安培; QUOT;播放列表\\&安培; QUOT;:空,\&安培; QUOT; playlistsFailed\&安培; QUOT;:假,\&安培; QUOT; playlistsLoading\&安培; QUOT;:假,\&安培; QUOT; youtubes\&安培; QUOT;:空,\&安培; QUOT; youtubesFailed\&安培; QUOT;:假,\&安培; QUOT; youtubesLoading\&安培; QUOT;:假,\&安培; QUOT; soundclouds\&安培; QUOT ;:空,\&安培; QUOT; soundcloudsFailed\&安培; QUOT;:假,\&安培; QUOT; soundcloudsLoading\&安培; QUOT;:假},\&安培; QUOT; PlayerStore\&安培; QUOT; :{\&安培; QUOT; player\&安培; QUOT;:空,\&安培; QUOT; playerSecond\&安培; QUOT;:空,\&安培; QUOT; playingTrack\&安培; QUOT;:空, \&安培; QUOT; playingTrackSecond\&安培; QUOT;:空,\&安培; QUOT; videoId\&安培; QUOT;:空,\&安培; QUOT; videoIdSecond\&安培; QUOT;:空,\ &安培; QUOT; makingPlayingTrackPlayable\&安培; QUOT;:假,\&安培; QUOT; radio\&安培; QUOT;:假,\&安培; QUOT; startSeconds\&安培; QUOT;:0,\&安培; QUOT; current\&安培; QUOT;:0,\&安培; QUOT; total\&安培; QUOT;:0,\&安培; QUOT; perc\&安培; QUOT;:0,\&安培; QUOT; currentSecond\&安培; QUOT;:0,\&安培; QUOT; totalSecond\\ \\&安培; QUOT;:0,\&安培; QUOT; percSecond\&安培; QUOT;:0,\&安培; QUOT; playing\&安培; QUOT;:假,\&安培; QUOT; playingSecond\&安培; QUOT;:假,\&安培; QUOT; secondsListened\&安培; QUOT;:0,\&安培; QUOT; secondsListenedSecond\&安培; QUOT;:0,\&安培; QUOT; expand\&安培; QUOT ;:假,\&安培; QUOT; source\&安培; QUOT;:空,\&安培; QUOT; tracksQueue\&安培; QUOT;:[],\&安培; QUOT; tracksPrevQueue\&安培; QUOT; :[],\&安培; QUOT; favorite\&安培; QUOT;:假,\&安培; QUOT; random\&安培; QUOT;:假,\&安培; QUOT; repeat\&安培; QUOT ;:假,\&安培; QUOT; mute\&安培; QUOT;:假,\&安培; QUOT; volume\&安培; QUOT;:100,\&安培; QUOT; mode\&安培; QUOT;:\ &安培; QUOT; standard\&安培; QUOT;},\&安培; QUOT; ImportStore\&安培; QUOT;:{\&安培; QUOT; url\&安培; QUOT;:\&安培; QUOT; \ &一个熔点; QUOT;,\&安培; QUOT; error\&安培; QUOT;:假,\&安培; QUOT; focused\&安培; QUOT;:假,\&安培; QUOT; loading\&安培; QUOT; :假,\&安培; QUOT; loaded\&安培; QUOT;:假,\&安培; QUOT; playlist\&安培; QUOT;日期null}}&安培; QUOT;>< / DIV>
< / div>
<! - Google Analytics(分析):将UA-XXXXX-X更改为您的网站ID - >
<! -
< script>
(function(i,s,o,g,r,a,m){i ['GoogleAnalyticsObject'] = r; i [r] = i [r] || function(){
(i [r] .q = i [r] .q || [])。push(arguments)},i [r] .l = 1 * new Date(); a = s.createElement(o),
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a,m)
})(window,document,'script' , '// www.google-analytics.com/analytics.js','ga');
ga('create','UA-XXXXX-X','auto');
ga('send','pageview');
< / script>
- >
< script src =https://cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.3/fastclick.min.js>< / script>
< script type =text / javascript>
if('addEventListener'in document){
document.addEventListener('DOMContentLoaded',function(){
FastClick.attach(document.body);
},false) ;
}
< / script>
< script type =text / javascriptcharset =utf-8src =/ assets / app.js>< / script>
< / body>
< / html>
这是我的server.jsx:
<$从iso导入Iso; p $ p>
import来自react的反应;
从react-dom / server导入ReactDomServer;
import {RoutingContext,match} from'react-router'
从'history / lib / createLocation'导入createLocation;
import alt from'altInstance';
从routes.jsx导入路由;
从'base.html'导入html;
/ *
* @param {AltObject}一个Alt对象的实例
* @param {ReactObject}反应路由器中指定的路由
* @param { Object}使用
* @param {Object} req从Express / Koa服务器传递的数据引导我们的altStores
* /
const renderToMarkup =(alt,state,req,res)=> {
让标记,内容;
let location = new createLocation(req.url);
alt.bootstrap(state);
match({routes,location},(error,redirectLocation,renderProps)=> {
if(redirectLocation)
res.redirect(301,redirectLocation.pathname + redirectLocation .search)
else if(error)
res.status(500).send(error.message)
else if(renderProps == null)
res.status(404 ).send('Not found')
else
content = ReactDomServer.renderToString(< RoutingContext {... renderProps} />);
markup = Iso.render(content, alt.flush());
});
返回标记;
};
/ *
*导出要在服务器/ config / routes.js中使用的渲染功能
*我们从服务器获取的状态和Express / Koa
*并将其传递到Router.run功能。
* /
导出默认函数render(state,req,res){
const markup = renderToMarkup(alt,state,req,res);
return html.replace('CONTENT',markup);
};
这是我的client.jsx:
import反应的反应;
从react-dom导入ReactDOM;
import iso from'iso';
从'history / lib / createBrowserHistory'导入createBrowserHistory;
import {Router} from'react-router';
import alt from'altInstance';
从routes.jsx导入路由;
/ *
*客户端引导与iso和alt
* /
Iso.bootstrap((state,_,container)=> {
alt.bootstrap(state);
ReactDOM.render(< Router history = {createBrowserHistory()} children = {routes} />,container);
});
我的routes.jsx:
import反应的反应;
import'from-feedback-router';
从'components / App'导入应用程序;
从'components / ImportPlaylist'导入ImportPlaylist;
import从'components / Login'登录;
import从'components / Logout'注销;
从'components / Player / Player'导入Player;
import从'components / Test'测试;
export default(
< Route path =/component = {App}>
< Route path =logincomponent = {Login} />
< Route path =logoutcomponent = {Logout} />
< Route name =testpath =testcomponent = {Test} />
< ; route name =importpath =importcomponent = {ImportPlaylist} />
< Route name =playerpath =/:playlistcomponent = {Player} />
< / Route>
);
警告: -shop-faunt是正确的。虽然这种解决方案似乎导致了服务器端和客户端DOM树之间的奇偶校验,但是在生产中吞没了错误,整个树仍将被重新呈现!如果您选择遵循以下解决方案,请记住这一点。
这可能听起来很简单,但是在您的服务器端模板中,将您的React标记包装在额外的< div>
:
<! - 车把模板 - >
< section role =mainclass =react-container>< div> {{{reactMarkup}}}< / div>< / section>
为什么这样工作?在客户端,React有一个倾向,用一个多余的div来包装你的根组件的渲染。 ReactDOMServer.render
似乎没有以这种方式表现,因此当同一个容器同构化时,您的DOM的Adler-32校验和不同。
I'm trying to get an isomorphic Node.js, Express, Webpack, React app working. I'm getting the following error. Any suggestions on how to fix it?
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) rgin:0;display:flex;-webkit-align-items:
(server) rgin:0;display:flex;align-items:center;j
warning @ warning.js:45
ReactMount._mountImageIntoNode @ ReactMount.js:807
wrapper @ ReactPerf.js:66
mountComponentIntoNode @ ReactMount.js:268
Mixin.perform @ Transaction.js:136
batchedMountComponentIntoNode @ ReactMount.js:282
Mixin.perform @ Transaction.js:136
ReactDefaultBatchingStrategy.batchedUpdates @ ReactDefaultBatchingStrategy.js:62
batchedUpdates @ ReactUpdates.js:94
ReactMount._renderNewRootComponent @ ReactMount.js:476
wrapper @ ReactPerf.js:66
ReactMount._renderSubtreeIntoContainer @ ReactMount.js:550
ReactMount.render @ ReactMount.js:570
wrapper @ ReactPerf.js:66
(anonymous function) @ client.jsx:14
(anonymous function) @ iso.js:120
each @ iso.js:21
bootstrap @ iso.js:111
(anonymous function) @ client.jsx:12
__webpack_require__ @ bootstrap d56606d95d659f2e05dc:19
(anonymous function) @ bootstrap d56606d95d659f2e05dc:39
(anonymous function) @ bootstrap d56606d95d659f2e05dc:39
This is what is being delivered by the server to the browser initially:
<!doctype html>
<html lang="">
<head>
<title>my title</title>
<meta name="apple-mobile-web-app-title" content="my title" data-react-helmet="true" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" data-react-helmet="true" />
<meta name="apple-mobile-web-app-capable" content="yes" data-react-helmet="true" />
<meta name="mobile-web-app-capable" content="yes" data-react-helmet="true" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" data-react-helmet="true" />
<meta name="description" content="my description." data-react-helmet="true" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" data-react-helmet="true" />
<meta charset="utf-8" data-react-helmet="true" />
<link rel="stylesheet" href="/assets/styles/reset.css" data-react-helmet="true" />
<link rel="stylesheet" href="/assets/styles/base.css" data-react-helmet="true" />
<link rel="stylesheet" href="/assets/styles/Carousel.css" data-react-helmet="true" />
<link rel="stylesheet" href="/assets/styles/main.css" data-react-helmet="true" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Condensed" type="text/css" data-react-helmet="true" />
<link rel="icon" href="/assets/185bb6f691241307862b331970a6bff1.ico" type="image/x-icon" data-react-helmet="true" />
SCRIPT
</head>
<body>
<script src="https://cdn.firebase.com/js/client/2.2.7/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/reactfire/0.4.0/reactfire.min.js"></script>
<div class="app">
<div class="___iso-html___" data-key="_0"><div data-reactid=".1hkqsbm9n9c" data-react-checksum="794698749"><div data-reactid=".1hkqsbm9n9c.0"><div data-reactid=".1hkqsbm9n9c.0.$=10"></div><div style="position:fixed;z-index:2;top:0;left:0;right:0;height:60px;color:rgb(219,219,219);font-family:mainnextcondensed_ultralight;font-size:17px;overflow:hidden;" data-reactid=".1hkqsbm9n9c.0.$/=11"><div style="position:absolute;left:0;top:0;background-color:rgba(27,27,27,0.92);padding-right:35px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10"><div style="float:left;height:60px;width:13px;border-left:5px solid rgb(210,45,164);" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=10"></div><div style="float:left;height:60px;width:227px;background-image:url(data:image/png;base64,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);background-repeat:no-repeat;background-position:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=11"></div><div style="display:none;width:0;height:0;border-style:solid;border-width:6px 6px 0 6px;border-color:rgb(117,117,117) transparent transparent transparent;-webkit-transform:rotate(360deg);float:left;margin-left:6px;margin-top:26px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=12"></div></div><div style="position:absolute;top:0px;left:280px;width:340px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11"><div style="background-color:rgba(27,27,27,0.92);height:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=10"></div><div style="background-color:rgba(53,53,53,0.84);height:40px;position:relative;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11"><div style="position:absolute;top:0;bottom:0;left:0;right:0;padding:0;margin:0;display:flex;align-items:center;justify-content:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$=10"><div style="background-image:url('/assets/3bec3e57cb5ee05658440d21984fb7b7.png');background-repeat:no-repeat;background-position:-58px -194px;width:23px;height:22px;position:absolute;top:50%;left:10px;margin-top:-11px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$=10.$icon"></div></div><div style="position:absolute;left:40px;right:40px;top:0px;bottom:0px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$/=12"><input type="text" style="width:100%;height:100%;font-size:14px;font-family:mainnext_regular;background-color:transparent;color:#ffffff;" placeholder="SEARCH ARTISTS, TRACKS, ALBUMS" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$/=12.0"/></div></div><div style="background-color:rgba(27,27,27,0.92);height:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=12"></div></div><div style="position:absolute;top:0px;left:620px;right:0px;background-color:rgba(27,27,27,0.92);height:60px;line-height:60px;overflow:hidden;min-width:500px;padding-left:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12"><div style="position:absolute;top:0px;bottom:0px;right:0px;width:357px;padding-left:141px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0"><a class="" href="/import" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10"><div style="padding-left:40px;position:absolute;left:0px;top:10px;bottom:10px;cursor:pointer;line-height:40px;color:rgb(255,255,255);" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import"><div style="position:absolute;top:0;bottom:0;left:0;right:0;padding:0;margin:0;display:flex;align-items:center;justify-content:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.$=10"><div style="background-image:url('/assets/3bec3e57cb5ee05658440d21984fb7b7.png');background-repeat:no-repeat;background-position:0px -194px;width:28px;height:28px;position:absolute;top:50%;left:0px;margin-top:-14px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.$=10.$icon"></div></div><span data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.1">Import Playlists</span></div></a><div style="margin-left:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$admin/=1$admin"><div style="cursor:pointer;float:left;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$admin/=1$admin.$login">Login</div></div></div></div></div></div><noscript data-reactid=".1hkqsbm9n9c.1"></noscript></div></div>
<div class="___iso-state___" data-key="_0" data-meta="{}" data-state=""{\"UserStore\":{\"user\":{\"authenticated\":false,\"isWaiting\":false}},\"SearchStore\":{\"focused\":false,\"input\":\"\",\"timeout\":null,\"searchRequests\":[],\"artists\":null,\"artistsFailed\":false,\"artistsLoading\":false,\"tracks\":null,\"tracksFailed\":false,\"tracksLoading\":false,\"albums\":null,\"albumsFailed\":false,\"albumsLoading\":false,\"playlists\":null,\"playlistsFailed\":false,\"playlistsLoading\":false,\"youtubes\":null,\"youtubesFailed\":false,\"youtubesLoading\":false,\"soundclouds\":null,\"soundcloudsFailed\":false,\"soundcloudsLoading\":false},\"PlayerStore\":{\"player\":null,\"playerSecond\":null,\"playingTrack\":null,\"playingTrackSecond\":null,\"videoId\":null,\"videoIdSecond\":null,\"makingPlayingTrackPlayable\":false,\"radio\":false,\"startSeconds\":0,\"current\":0,\"total\":0,\"perc\":0,\"currentSecond\":0,\"totalSecond\":0,\"percSecond\":0,\"playing\":false,\"playingSecond\":false,\"secondsListened\":0,\"secondsListenedSecond\":0,\"expand\":false,\"source\":null,\"tracksQueue\":[],\"tracksPrevQueue\":[],\"favorite\":false,\"random\":false,\"repeat\":false,\"mute\":false,\"volume\":100,\"mode\":\"standard\"},\"ImportStore\":{\"url\":\"\",\"error\":false,\"focused\":false,\"loading\":false,\"loaded\":false,\"playlist\":null}}""></div>
</div>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
<!--
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-X', 'auto');
ga('send', 'pageview');
</script>
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.3/fastclick.min.js"></script>
<script type="text/javascript">
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
</script>
<script type="text/javascript" charset="utf-8" src="/assets/app.js"></script>
</body>
</html>
This is my server.jsx:
import Iso from 'iso';
import React from 'react';
import ReactDomServer from 'react-dom/server';
import { RoutingContext, match } from 'react-router'
import createLocation from 'history/lib/createLocation';
import alt from 'altInstance';
import routes from 'routes.jsx';
import html from 'base.html';
/*
* @param {AltObject} an instance of the Alt object
* @param {ReactObject} routes specified in react-router
* @param {Object} Data to bootstrap our altStores with
* @param {Object} req passed from Express/Koa server
*/
const renderToMarkup = (alt, state, req, res) => {
let markup, content;
let location = new createLocation(req.url);
alt.bootstrap(state);
match({ routes, location }, (error, redirectLocation, renderProps) => {
if (redirectLocation)
res.redirect(301, redirectLocation.pathname + redirectLocation.search)
else if (error)
res.status(500).send(error.message)
else if (renderProps == null)
res.status(404).send('Not found')
else
content = ReactDomServer.renderToString(<RoutingContext {...renderProps} />);
markup = Iso.render(content, alt.flush());
});
return markup;
};
/*
* Export render function to be used in server/config/routes.js
* We grab the state passed in from the server and the req object from Express/Koa
* and pass it into the Router.run function.
*/
export default function render(state, req, res) {
const markup = renderToMarkup(alt, state, req, res);
return html.replace('CONTENT', markup);
};
And this is my client.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import Iso from 'iso';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import { Router } from 'react-router';
import alt from 'altInstance';
import routes from 'routes.jsx';
/*
* Client side bootstrap with iso and alt
*/
Iso.bootstrap((state, _, container) => {
alt.bootstrap(state);
ReactDOM.render(<Router history={createBrowserHistory()} children={routes} />, container);
});
And my routes.jsx:
import React from 'react';
import Route from 'react-router';
import App from 'components/App';
import ImportPlaylist from 'components/ImportPlaylist';
import Login from 'components/Login';
import Logout from 'components/Logout';
import Player from 'components/Player/Player';
import Test from 'components/Test';
export default (
<Route path="/" component={App}>
<Route path="login" component={Login} />
<Route path="logout" component={Logout} />
<Route name="test" path="test" component={Test} />
<Route name="import" path="import" component={ImportPlaylist} />
<Route name="player" path="/:playlist" component={Player} />
</Route>
);
Warning: @alex-shop-faunt is correct. While it appears that this solution results in parity between the server and client-side DOM trees, the error is swallowed in production and the entire tree will still be re-rendered! Please proceed with this in mind if you do choose to follow the solution below.
This may sound crazily simple, but in your server-side template, wrap your React markup in an extra <div>
:
<!-- hypothetical handlebars template -->
<section role="main" class="react-container"><div>{{{reactMarkup}}}</div></section>
Why does this work? On the client, React has a propensity to wrap its rendering of your root component with a superfluous div. ReactDOMServer.render
doesn't seem to behave in this manner, thus when one renders into the same container isomorphically, the Adler-32 checksum of your DOM differs.
这篇关于警告:尝试在容器中重新使用标记,但校验和无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!