同构

12 2月

用Node有个好处是同构应用开发起来比较简单,因为Node是基于chrome V8引擎能在服务端运行JS,所以模板组件既能在浏览器渲染,也能在Node渲染。

在前端可以new Function或eval进行模板渲染。在Node层可以用vm模块创建模板引擎进行渲染,现在都是React/Vue框架,会使用框架提供的渲染函数进行服务端渲染:

  • ReactDomServer.rendToString()
  • VueServerRenderer.rendToString()

例如React框架,在服务端渲染:

const app = new (require('koa'))
const ReactDOMServer = require('react-dom/server')  // 服务端渲染引擎用ReactDOMServer

require('babel-register')({     // 服务端渲染,需要将jsx转成js再传给模板引擎
    presets: ['react']
})

app.use(async (ctx) => {
    ctx.status = 200
    ...
    ReactDOMServer.renderToString(
        require('./index.jsx')  // React组件
    )
})
app.listen(3000)

同构应用实现渲染不难,难搞的是数据。前端数据状态负责,需要用Redux,Vuex进行数据状态管理。但服务端只需要关心当前渲染时的数据状态,不需要这些状态管理框架。所以通常使用Next / Nuxt框架来做React/Vue的同构应用。

无论框架做的多好,浏览器和服务端总是有差异,难免要进行些环境判断,不要将处理环境和处理数据的代码耦合在一起。如果你将浏览器端window对象,服务端文件读写代码,业务数据处理耦合在一起,同构将会变得很困难。

服务端预取到的数据可以通过模板引擎挂到页面的window上,供前端React框架使用:

app.use(async (ctx) => {
    ctx.status = 200
    ...
    ctx.body = template({
        reactString: ReactDOMServer.renderToString(
            App(reactData)
        ),
        reactData
    })
})

// html
<div id="reactapp">   // 服务端渲染
    ${reactString}
</div>
<script>
    window.reactInitData = ${reactData ? JSON.stringify(reactData) : ''};   // 服务端数据预取作为初始值
</script>
<script src="./static/main.js"></script>  // 服务端渲染后客户端渲染

发表评论

电子邮件地址不会被公开。 必填项已用*标注