前端

React+Koa服务端渲染(SSR同构),服务端端入口文件

2019-08-07聚力创意

// server/index.js

import React from 'react'
import path from 'path'
import Koa from 'koa'
import Static from 'koa-static'
import compress from 'koa-compress'
import {render} from '../utils'
import {getStore} from '../store'
import {matchRoutes} from 'react-router-config'
import routes from '../routes'

const isDev = process.env.NODE_ENV === 'development'

const app = new Koa()

app.use(compress({threshold: 2048}))

if (isDev) {
  app.use(Static(path.join(__dirname, '/')))
}

app.use(async ctx => {
  const context = {}
  const path = ctx.path
  const store = getStore()
  const matchedRoutes = matchRoutes(routes, path) || []

  for (let i = 0, len = matchedRoutes.length; i < len; i++) {
    let matchedRoute = matchedRoutes[i]
    try {
      matchedRoute.route && matchedRoute.route.loadData && await matchedRoute.route.loadData(store, path)
    } catch (e) {
      console.log(e)
    }
  }

  try {
    const html = await render(ctx, store, routes, context)
    // NotFound页面:this.props.staticContext && (this.props.staticContext.NotFound = true)
    if (context.NotFound) {
      ctx.status = 404
    }
    ctx.body = html
  } catch (e) {
    console.log(e)
    ctx.body = e.message
  }
})

app.listen(3000)
// utils/index.js

import React from 'react'
import fs from 'fs'
import path from 'path'
import {renderToNodeStream} from 'react-dom/server'
import {Helmet} from 'react-helmet'
import {renderRoutes} from 'react-router-config'
import {StaticRouter} from 'react-router-dom'
import {Provider} from 'react-redux'

export const render = (ctx, store, routes, context) => {
  return new Promise(async (resolve, reject) => {
    let templateStream
    const dom = (
      <Provider store={store}>
        <StaticRouter location={ctx.path} context={context}>
          {renderRoutes(routes)}
        </StaticRouter>
      </Provider>
    )
    const stream = renderToNodeStream(dom)
    let html = '', template = ''
    templateStream = fs.createReadStream(path.join(__dirname, '../index.html'), {encoding: 'utf8'})
    templateStream.on('data', chunk => {
      template += chunk.toString()
    })
    templateStream.on('end', () => {
      stream.on('data', chunk => {
        html += chunk.toString()
      })

      stream.on('end', () => {
        const helmet = Helmet.renderStatic()
        const title = helmet.title.toString()
        const meta = helmet.meta.toString()
        template = template.replace('<!-- meta -->', meta)
        template = template.replace('<!-- title -->', title)
        template = template.replace('<!-- app -->', html)
        template = template.replace('<!-- state -->', `<script>window.initialState = ${JSON.stringify(store.getState())}</script>`)
        resolve(template)
      })

      stream.on('error', err => {
        reject(err)
      })
    })
  })
}
蜀ICP备17044229号