zhangyuang / egg-react-ssr

最小而美的Egg + React + SSR 服务端渲染应用骨架,同时支持JS和TS
http://doc.ssr-fc.com/
MIT License
1.91k stars 212 forks source link

CSR渲染使用useReactToString或者isRax会报错 #198

Closed bravekingzhang closed 3 years ago

bravekingzhang commented 4 years ago
const renderToStream = async (ctx: Context, config: Config) => {
  const csr = ctx.request?.query?.csr ? ctx.request.query.csr : false // 兼容express和koa的query获取

  if (config.type !== 'ssr' || csr) {
    const stream = await renderLayout(ctx, config)
    const doctypeStream = new ReadableString('<!DOCTYPE html>')
    // @ts-ignore
    return mergeStream(doctypeStream, stream)
  }

这里如果是csr就会走到renderLayout方法,而renderLayout返回不一定是stream,请看:

const renderLayout = async (ctx: Context, config: Config) => {
  const { useCDN, layout } = config
  const isLocal = process.env.NODE_ENV === 'development' || config.env === 'local' // 标志非正式环境
  ///....
    ///....
  const Layout = typeof LAYOUT_PATH === 'string' ? require(LAYOUT_PATH).default : LAYOUT_PATH

  const stream = reactToStream(Layout, props, config)
  return stream
}

会继续调到reactToStream:

const reactToStream = (Component: React.FunctionComponent, props: object, config: Config) => {
  const { baseDir } = config
  const BASE_DIR = baseDir || process.cwd()
  if (config.useReactToString) {
    return reactRenderToString(React.createElement(Component, props))
  } else {
    if (config.isRax) {
      const renderToString = require(BASE_DIR + '/node_modules/rax-server-renderer').renderToString
      return renderToString(React.createElement(Component, props))
    } else {
      return renderToNodeStream(React.createElement(Component, props))
    }
  }
}

所以,如果config中配置 useReactToString 或者 使用Rax ,那就出来一个String,而非Stream。

会报错

TypeError: source.once is not a function
    at add (/Users/xx/intl_pro/web-sign/node_modules/merge-stream/index.js:27:12)
    at Array.forEach (<anonymous>)
    at module.exports (/Users/xx/intl_pro/web-sign/node_modules/merge-stream/index.js:16:41)
    at Object.<anonymous> (/Users/xx/intl_pro/web-sign/local-test/mergerString.js:6:1)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at internal/main/run_main_module.js:17:47
zhangyuang commented 4 years ago

可以来个PR

发自我的iPhone

------------------ 原始邮件 ------------------ 发件人: hz <notifications@github.com> 发送时间: 2020年11月7日 13:06 收件人: ykfe/egg-react-ssr <egg-react-ssr@noreply.github.com> 抄送: Subscribed <subscribed@noreply.github.com> 主题: 回复:[ykfe/egg-react-ssr] CSR渲染使用 (#198)

const renderToStream = async (ctx: Context, config: Config) => { const csr = ctx.request?.query?.csr ? ctx.request.query.csr : false // 兼容express和koa的query获取 if (config.type !== 'ssr' || csr) { const stream = await renderLayout(ctx, config) const doctypeStream = new ReadableString('<!DOCTYPE html>') // @ts-ignore return mergeStream(doctypeStream, stream) }

这里如果是csr就会走到renderLayout方法,而renderLayout返回不一定是stream,请看: const renderLayout = async (ctx: Context, config: Config) => { const { useCDN, layout } = config const isLocal = process.env.NODE_ENV === 'development' || config.env === 'local' // 标志非正式环境 ///.... ///.... const Layout = typeof LAYOUT_PATH === 'string' ? require(LAYOUT_PATH).default : LAYOUT_PATH const stream = reactToStream(Layout, props, config) return stream }

会继续调到reactToStream: const reactToStream = (Component: React.FunctionComponent, props: object, config: Config) => { const { baseDir } = config const BASE_DIR = baseDir || process.cwd() if (config.useReactToString) { return reactRenderToString(React.createElement(Component, props)) } else { if (config.isRax) { const renderToString = require(BASE_DIR + '/node_modules/rax-server-renderer').renderToString return renderToString(React.createElement(Component, props)) } else { return renderToNodeStream(React.createElement(Component, props)) } } }

所以,如果config中配置 useReactToString 或者 使用Rax ,那就出来一个String,而非Stream。

会报错 TypeError: source.once is not a function at add (/Users/xx/intl_pro/web-sign/node_modules/merge-stream/index.js:27:12) at Array.forEach (<anonymous>) at module.exports (/Users/xx/intl_pro/web-sign/node_modules/merge-stream/index.js:16:41) at Object.<anonymous> (/Users/xx/intl_pro/web-sign/local-test/mergerString.js:6:1) at Module._compile (internal/modules/cjs/loader.js:1063:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10) at Module.load (internal/modules/cjs/loader.js:928:32) at Function.Module._load (internal/modules/cjs/loader.js:769:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) at internal/main/run_main_module.js:17:47

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe.