技术博客 SSR 技术选型

由于采用了前后端分离的技术方案,导致我的博客完全没有了 SEO(虽然也没啥阅读量 O_o),但还是想要从这个点切入折腾优化一下。

前端是基于 React 技术栈的,那么最热门的 SSR 框架肯定是 next.js 了。初步刷了一下文档,由于之前没有考虑过 SSR,现在迁移到 next.js 需要对代码有不小的改动(参考文档:Migrating from Create React App),主要有以下几点:

  1. 更新 package.json
    将 start/build/dev 脚本从 react-scripts 替换为 next,并安装 next 相关的依赖。

  2. 修改静态资源的输出

  3. 将基于 React-router-dom 的路由方案,转换到基于文件路径的路由方案
    这一点是我觉得最难受的,本来可以按个人喜好业务逻辑组织路由/组件的,现在非得按 next 的规则放到 page 目录下。

  4. 处理 client 端的一些 API
    window, localStorage 这些都要在使用前进行边界条件判断。

其中第 3 点的改动是我最不能接受的,不想让我自己的前端架构受到 next 的约束。
那就去研究一番,其实本质上 SSR 就是在 server-side 直接生成 HTML 发给 client,免去 JavaScript 在 client 执行的过程,这里的核心的就是要求我们的软件是同构的(isomorphic),即编译后的 JS 代码能同时在 Node 环境和 Browser 环境运行。

开工!

  1. 先是修改程序入口文件 index.tsx,这一步也是实现同构的重要步骤

// ReactDOM.render(
//   <React.StrictMode>
//     <App />
//   </React.StrictMode>,
//   document.getElementById('root')
// );
ReactDOM.hydrate(<App />, document.getElementById('root'));

  1. 然后修改路由相关代码,将需要 Dom 的 BrowserRouter 替换为能同时运行在 Node 环境的 Router
function App() {
  const history = isBrowser
      ? createBrowserHistory()
      : createMemoryHistory({initialEntries: ['/']});

  return (
    <Router history={history}>
      {/* <BrowserRouter> */}
      <Layout className="App">
        <Header />
        <Body />
        <Footer />
      </Layout>
    </Router>
    // </BrowserRouter>
  );
}
  1. 搭建 Express 服务器
    基本思路为配置好监听的路由,将后端 Node 环境运行后的结果返回给客户端,由客户端的 ReactDOM.hydrate 处理后端的同构代码。
// ...
const app = express();
app.get('/', (req, res) => {
  const app = ReactDOMServer.renderToString(<App />);

  const indexFile = path.resolve('./build/index.html');
  fs.readFile(indexFile, 'utf8', (err, data) => {
    if (err) {
      return res.status(500).send('something wrong');
    }

    return res.send(
      data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
    );
  });
});

app.use(express.static('./build'));

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});
  1. 配置开发调试环境
    配置 webpack、Babel 以及 npm 脚本,本以为这一步是最不重要的,结果却被打了脸。不是 babel-loader 版本对不上,就是 babel preset 有问题,好不容易处理完了错误,能跑起来并在浏览器中打开 server rendered 页面,还没来得及开心就发现 CSS 的解析有问题。
    好吧,只能说 webpack 的配置是博大精深,认栽了,还是滚回去用 nextjs 框架吧😭
posted @ 2021-08-16 23:08  zion03  阅读(202)  评论(0编辑  收藏  举报