美文网首页
21.同构应用中你所忽略的细节

21.同构应用中你所忽略的细节

作者: ikonan | 来源:发表于2021-06-15 09:26 被阅读0次

不管是服务端渲染还是服务端渲染衍生出的同构应用,现在来看已经并不新鲜了,实现起来也并不困难。可是有的开发者认为:同构应用不就是调用一个 renderToString(React 中)类似的 API 吗?

讲道理确实是这样的,但是讲道理你也许并没有真正在实战中领会同构应用的精髓。

同构应用能够完成的本质条件是虚拟 DOM,基于虚拟 DOM 我们可以生成真实的 DOM,并由浏览器渲染;也可以调用不同框架的不同 APIs,将虚拟 DOM 生成字符串,由服务端传输给客户端。

但是同构应用也不只是这么简单。拿面试来说,同构应用的考察点不是「纸上谈兵」的理论,而是实际实施时的细节。这一讲我们就来聊一聊「同构应用中往往被忽略的细节」,需要读者提前了解服务端渲染和同构应用的概念。

相关知识点如下:


打包环境区分

第一个细节:我们知道同构应用实现了客户端代码和服务端代码的基本统一,我们只需要编写一种组件,就能生成适用于服务端和客户端的组件案例。可是你是否知道,服务端代码和客户端代码大多数情况下还是需要单独处理?比如:

  • 路由代码差别:服务端需要根据请求路径,匹配页面组件;客户端需要通过浏览器中的地址,匹配页面组件。
    客户端代码:
const App = () => {
  return (
    <Provider store={store}>
      <BrowserRouter>
        <Route path="/" component={Home} />
        <Route path="/product" component={Product} />
      </BrowserRouter>
    </Provider>
  );
};
ReactDom.render(<App />, document.querySelector('#root'));

BrowserRouter 组件根据 window.location 以及 history API 实现页面切换,而服务端肯定是无法获取 window.location 的,服务端代码如下:

const App = () => {
  return <Provider store={store}>
    <StaticRoute location={req.path} context={context}>
      <Route path="/" component={Home}/>
    </StaticRoute>
  </Provider>
 }
 return ReactDom.renderToString()

需要使用 StaticRouter 组件,并将请求地址和上下文信息作为 location 和 context 这两个 props 传入 StaticRouter 中。

  • 打包差别:服务端运行的代码如果需要依赖 Node 核心模块或者第三方模块,就不再需要把这些模块代码打包到最终代码中了。因为环境已经安装这些依赖,可以直接引用。这样一来,就需要我们在 webpack 中配置:target:node,并借助 webpack-node-externals 插件,解决第三方依赖打包的问题。
  • 对于图片等静态资源,url-loader 会在服务端代码和客户端代码打包过程中分别被引用,因此会在资源目录中生成了重复的文件。当然后打包出来的因为重名,会覆盖前一次打包出来的结果,并不影响使用,但是整个构建过程并不优雅。

由于路由在服务端和客户端的差别,因此 webpack 配置文件的 entry 会不相同:

{
   entry: './src/client/index.js',
}

{
   entry: './src/server/index.js',
}

注水和脱水

什么叫做注水和脱水呢?这个和同构应用中数据的获取有关:在服务器端渲染时,首先服务端请求接口拿到数据,并处理准备好数据状态(如果使用 Redux,就是进行 store 的更新),为了减少客户端的请求,我们需要保留住这个状态。一般做法是在服务器端返回 HTML 字符串的时候,将数据 JSON.stringify 一并返回,这个过程,叫做脱水(dehydrate);在客户端,就不再需要进行数据的请求了,可以直接使用服务端下发下来的数据,这个过程叫注水(hydrate)。用代码来表示:

ctx.body = `
<meta charset="UTF-8">
<script>
window.context = {
  initialState: ${JSON.stringify(store.getState())}
}

</script>
<div id="app">
  //...
</div>
`;

客户端:

export const getClientStore = () => {
 const defaultState = JSON.parse(window.context.state)
 return createStore(reducer, defaultState, applyMiddleware(thunk))
}

这一系列过程非常典型,但是也会有几个细节值得探讨:在服务端渲染时,服务端如何能够请求所有的 APIs,保障数据全部已经请求呢?

一般有两种方法:

  • react-router 的解决方案是配置路由 route-config,结合 matchRoutes,找到页面上相关组件所需的请求接口的方法并执行请求。这就要求开发者通过路由配置信息,显式地告知服务端请求内容。

在服务端代码中:

const routes = [
  {
    path: '/',
    component: Root,
    loadData: () => getSomeData(),
  },
  // etc.
];

import { routes } from './routes';

function App() {
  return (
    <Switch>
      {routes.map((route) => (
        <Route {...route} />
      ))}
    </Switch>
  );
}

在服务端代码中:

import { matchPath } from "react-router-dom"

const promises = []
routes.some(route => {
 const match = matchPath(req.path, route)
 if (match) promises.push(route.loadData(match))
 return match
})

Promise.all(promises).then(data => {
 putTheDataSomewhereTheClientCanFindIt(data)
})
  • 另外一种思路类似 Next.js,我们需要在 React 组件上定义静态方法。 比如定义静态 loadData 方法,在服务端渲染时,我们可以遍历所有组件的 loadData,获取需要请求的接口。这样的方式借鉴了早期 React-apollo 的解决方案,我个人很喜欢这种设计。这里贴出我为 Facebook 团队 react-apollo 开源项目贡献的改动代码,其目的就是遍历组件,获取请求接口:

注水和脱水,是同构应用最为核心和关键的细节点。

相关文章

  • 21.同构应用中你所忽略的细节

    不管是服务端渲染还是服务端渲染衍生出的同构应用,现在来看已经并不新鲜了,实现起来也并不困难。可是有的开发者认为:同...

  • 不只是同构应用(isomorphic 工程化你所忽略的细节)

    不管是服务端渲染还是服务端渲染衍生出的同构应用,现在来看已经并不新鲜了,实现起来也并不困难。但是社区上相关文章质量...

  • 12.28日精进

    工作中还是要注意细节,有很多细节已经被我们所忽略,,有些客户能看到的我们却浑然不知,把握好每一个细节,客户都看在眼里

  • 懂婚姻的人越过越美满

    1.不要忽略见证爱情的小节 注意生活中的细节,就是对你所敬爱的人,表示你对她的思念,并希望她快乐。而她的快乐,也会...

  • Applications 2

    2.以下问题在前面章节的应用练习中已经出现过了。接下来,你将把本章所讨论的方法应用于其中的一个问题中(忽略你之前对...

  • 下犬式中常犯的错误,看看你中了几个?

    在瑜伽的练习中,有时候看似简单基础的体式,总会有一些细微的细节被我们所忽略,而这些小细节有时正是瑜伽灵魂的所在...

  • 轻触开关存在的操作误区说明

    常规轻触开关的应用在日常是经常需使用到的,但是据了解到许多一些用户平时都会容易忽略一些细节的情况,所导致造成故障隐...

  • 生活中的小确幸

    当你注意到平时所忽略的那些细节,你会发现其中有诸多美好。 在繁忙都市中,人们忙着去赶上自己的重复的生活,没有为什么...

  • 学会版式的N个Tips:你知道画册中这个细节处理吗?细节不容忽略

    无论你做任何设计,细节确实是不容忽略的。今天分享的内容主要总结画册中页眉页码的细节处理,通过案例为大家分析细节的妙...

  • 成也细节,败也细节

    生活中,决定我们能够成功的,细节很重要,有句话说:细节决定成败。 在日常生活中,人们往往忽略了细节,殊...

网友评论

      本文标题:21.同构应用中你所忽略的细节

      本文链接:https://www.haomeiwen.com/subject/cxomeltx.html