## 遇到的一些问题 ### react hooks `Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons` #### 原因 这是由于 react hooks 要求 react 和 react-dom 处于同一环境下,拥有相同的 ReactCurrentDispatcher 在此之前的服务端渲染我并没有保证环境一致,react 使用的是 webpack 打包的模块,而 react-dom 则使用的是 node_modules 下的模块(服务端的模块调用自然使用来自 node_modules 下的模块) 究其原因是 react 把 ReactCurrentDispatcher 对象挂载到了 React.\_\_SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 下,不在同一环境时他们持有的 ReactCurrentDispatcher 对象自然不同 #### 解决方案 lissom 提供了 requireModules 配置参数来决定服务端渲染时如何查找一个来自 node_modules 的模块,是默认去加载 webpack 打包中的模块,还是这里配置下让此模块从 node_modules 中去加载 修复后目前的默认配置为['superagent', 'react'],react 也从 node_modules 中去加载,需要注意的是如果用户需要自定义会覆盖此默认配置不会合并 ## 开发中的差异点 这里的问题在于任何一个页面都可能会在服务端渲染,当涉及动态加载和异步操作时会产生差异,尤其是以条件生产(如按路由区分)的对顺序、状态有要求的全局数据会产生不一致的情况,所以要考虑所有可能的情况,了解其差异点进行开发 ### 服务端首屏渲染 spa 模式下为当前请求路由的首屏渲染 一次请求只渲染一次 ### 客户端首屏渲染 需要保持和服务端直出结果相同,这里需要保证组件所需的 props 和服务端一致,不能出现函数等无法转为 json 格式数据的 props 同服务端一样只渲染一次 ### 客户端渲染 spa 模式下客户端在首屏渲染之后所有的路由跳转渲染走正常的客户端逻辑 渲染大于等于 0 次 ### 动态组件的加载 服务端会提前执行加载动态组件为了读取动态组件的异步函数,客户端不会,正常按需异步加载 ### 例子 这里列举两个遇到的情况 1. 有一个全局变量决定是否渲染,其值依赖服务端的路由匹配结果,除此之外无改变逻辑,当在服务端匹配到路由时值为 true,客户端同构后首屏没有问题,跳转其他路由后再跳回来也依旧渲染,全局变量为 true 没有改变,这种情况没有问题,但在服务端不匹配时值为 false 且渲染的其他页面,当跳进此页面时因为控制渲染的全局变量为 false 所以没有发生渲染 2. 动态加载的路由在服务端会以同步的方式执行 render,和正常组件无区别,按顺序加载渲染,客户端首屏时没有问题,也是同步的方式处理渲染,但在跳入入一个动态加载的路由时此时走正常的客户端逻辑,结果变为了一个异步操作,无法保证顺序 ## 思考如何提供 getInitialProps 的能力 ### 目前的方式 目前的方式提供的其实是和组件逻辑分开的,从 getInitialProps 是个静态方法就能看出来,即无法和组件交互,接收组件传入的 props,整体实现是先获取本次请求需要执行的异步方法,这里获取的异步函数在调用 async 函数时便已注入,然后执行获取每个函数的返回值,这一步是个异步操作,await 同步执行等待返回结果,之后才开始渲染组件,async 组件中需要的值可以正常取到,之后返回字符串走客户端逻辑,首次渲染的值会从服务端同构回来,之后走正常的客户端逻辑 ### 弊端 这里唯一影响比较大的问题就是 getInitialProps 方法和组件逻辑时分开的,当需要获取组件传入的值去完成异步操作时就会有问题,目前有几种解决方案 ### 解决方案 根组件完成所有异步操作所需值的初始化提供操作,根组件返回的值对于任意 async 组件其实都是可见的,可以理解为同 redux 提供数据的能力,这里承载部分逻辑层的能力来达到提供自组件异步函数所需的值 ### 后续 我一直在考虑如何让 react 可以异步渲染,这样就不需要 getInitialProps 这样的静态方法,async 方式的 componentWillMount 便可承载此能力,但目前 react render 是不支持异步渲染的,期待以后 react 能提供类似的机制,也可以选择改造,不过成本很高且不应该由 ssr 框架提供,这里可以参考 next 实现 ## react 异步渲染改造方案 等待 react 的 Suspense 支持 ssr ## 组件级缓存 - [react-dom-stream](https://github.com/aickin/react-dom-stream) - [react-ssr-optimization](https://github.com/walmartlabs/react-ssr-optimization) ### 参考资料 - https://github.com/alibaba/beidou/blob/master/packages/beidou-docs/articles/high-performance-isomorphic-app.md ## 其他异步方案库 - [react-apollo-hooks](https://github.com/trojanowski/react-apollo-hooks) - [react-use-api](https://github.com/RyanRoll/react-use-api) - [react-async-ssr](https://github.com/overlookmotel/react-async-ssr) ## 借鉴的库 - next - koa - html-webpack-plugin - style-loader - dva/dynamic - react-loadable ## 其他服务端渲染库 - [next](https://github.com/zeit/next.js) - [razzle](https://github.com/jaredpalmer/razzle) - [after](https://github.com/jaredpalmer/after.js) - [rogue](https://github.com/alidcastano/rogue.js) - [beidou](https://github.com/alibaba/beidou) - [react-server](https://github.com/redfin/react-server)