easy-team / egg-view-react-ssr

Egg React Server Side Render (SSR) Plugin
https://easyjs.cn
MIT License
62 stars 9 forks source link

feature: spa ssr渲染模式时layout也需要获取ctx和初始数据 #3

Closed EvanLiu2968 closed 6 years ago

EvanLiu2968 commented 6 years ago

现象:使用spa ssr渲染模式时,页面入口可以获取到服务端的ctx和初始数据,但在layout里面无法获取ctx和初始数据,正常多页面启用loader或者客户端渲染时是可以在layout获取到ctx的,没看出明白问题在哪?

预期:spa ssr渲染模式时layout也需要获取ctx和初始数据

version:egg-view-react-ssr@2.3.1

demo code spa ssr demo entry

const serverRender = (context, options)=> {
  const { ctx, request, helper, url} = context.state;
  console.log("==================context")
  // console.log(context) // 可以拿到ctx
  const branch = matchRoutes(routes, url);
  const promises = branch.map(({route}) => {
    const fetch = route.component.fetch;
    return fetch instanceof Function ? fetch(ctx) : Promise.resolve(null)
  });
  return Promise.all(promises).then(data => {
    const initState = context.state;
    data.forEach(item => {
      Object.assign(initState, item);
    });
    context.state = Object.assign({}, context.state, initState);
    const store = create(initState);
    return () =>(
      <Layout>
        <Provider store={store}>
          <StaticRouter basename="/ssrdemo" location={url} context={{}}>
            <App url={url}/>
          </StaticRouter>
        </Provider>
      </Layout>
    )
  });
};

layout.js

export default class Layout extends React.Component {
  render() {
    let { ctx, request, helper, ...initState } = this.props;
    console.log('===================layout')
    console.log(this.props) // 无法拿到ctx
    // const serialize = require('serialize-javascript');
    // initState = initState.state || initState;
    return (
      <html>
        <head>
          <title>{this.props.title}</title>
          <meta charSet="utf-8"></meta>
          <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"></meta>
          <meta httpEquiv="X-UA-Compatible" content="IE=edge"></meta>
          <meta name="keywords" content={this.props.keywords}></meta>
          <meta name="description" content={this.props.description}></meta>
          { EASY_ENV_IS_PROD && (
            <script dangerouslySetInnerHTML={{__html: baiduTongji}}></script>
          ) }
          { ctx && ctx.query.debug==='vconsole' && (
            <React.Fragment>
              <script src="https://cdnjs.cloudflare.com/ajax/libs/vConsole/3.2.0/vconsole.min.js"></script>
              <script dangerouslySetInnerHTML={{__html:`new VConsole();`}}></script>
            </React.Fragment>
          ) }
          <script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.5.7/core.min.js"></script>
          <script dangerouslySetInnerHTML={{__html:`document.write('${ie9Mixins}')`}}></script>
          { ctx && !/\/lowVersionBrowser/.test(ctx.request.url) && <script dangerouslySetInnerHTML={{__html:`document.write('${ie8Mixins}')`}}></script> }
          <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"></link>
        </head>
        <body>
          <div id="app">{this.props.children}</div>
          {/* <script id="__INITIAL_STATE__" dangerouslySetInnerHTML={{__html: `window.__INITIAL_STATE__= ${serialize(initState, { isJSON: true })};`}}></script> */}
        </body>
      </html>
    )
  }
}
EvanLiu2968 commented 6 years ago

sorry,忘记spa的ssr是手动引入的了,直接在layout传值就可以了<Layout {...context.state}>

完整代码应该是这样的

const serverRender = (context, options)=> {
  const { ctx, request, helper, url} = context.state;
  console.log("==================context")
  // console.log(context)
  const branch = matchRoutes(routes, url);
  const promises = branch.map(({route}) => {
    const fetch = route.component.fetch;
    return fetch instanceof Function ? fetch(ctx) : Promise.resolve(null)
  });
  return Promise.all(promises).then(data => {
    const initState = context.state;
    data.forEach(item => {
      Object.assign(initState, item);
    });
    context.state = Object.assign({}, context.state, initState);
    const store = create(initState);
    return () =>(
      <Layout {...context.state}>
        <Provider store={store}>
          <StaticRouter basename="/ssrdemo" location={url} context={{}}>
            <App url={url}/>
          </StaticRouter>
        </Provider>
      </Layout>
    )
  });
};