react&node - 服务端渲染(server-render&hydrate) 上

1,440 阅读5分钟

image.png

前言

本文章需要对react和koa并且webpack有一定的基础

相信大家对react或者是vue来说用的更多的是后台管理或者是app之类的,这些应用往往直接一个cli就可以搞定了 既然这样,为什么官方还要出一个服务端渲染的支持呢?闲的没事 当然不是,因为像某些面对用户的web端,通常都要考虑一个东西,也就是seo 如果直接cli create的项目大家肯定也都看过那个网站源代码、空空如也,什么都看不到,中国的大多数搜索引擎(比如百度)是不会看js的,所以这肯定满足不了需求,但是服务端渲染一些常用的比方说ejs什么这简直就是原生、写起来既慢又不舒服、那大家都是怎么实现的呢? 本文章就来说说大厂目前服务端渲染的最优解决方案。

服务端渲染

在前端来说其实听到这个词应该没有 混编 来的更多一点 为什么需要服务端渲染:

  1. 传统webpack-cli打包没法做seo
  2. 首屏渲染优化能力有限
  3. 没法做静态化(也就是访问频率搞的页面提前生成好,请求的时候能直接甩过去、优化性能)

方案

其实后端渲染方案还是挺多的,拿react来说,它其实有自己的解决方案比如:

  1. next - 不成熟。
  2. 这是个react官放推荐的,但是说实话、出来的结果比较让人失望
  3. 自带非常多的东西、很不利于维护、而且很乱、而且对seo也没有特别的友好
  4. 所以目前来说还远远没有到可以在大项目中用的成熟度
  5. 后台渲染
  6. ejs ✅ 1. 其实后台渲染的成熟的东西非常多,我个人比较喜欢ejs,不过都差不太多 1. 咱们的咱们的例子最终也是用ejs来配合react-server-render最终来渲染

这些是服务器渲染 还有就是react 怎么和react-server-render渲染的这些东西怎么来整合 因为后端渲染出去的东西都是死的,包括什么事件都是没有的,这肯定不行

存在的问题和难点预告

首先呢,大家都明白react有点特殊,用的不是js,而是jsx,然后平时的项目中大家也都是用webpack去编译jsx的 但是呢、又有一个问题 在node里写jsx语法都通不过、但是webpack又没法去编译node,因为node里有一些系统自带的模块源码根本打包不进来比方说 fs、http之类的 所以呢,咱们采用的方案很简单、给react那一套单独配上jsx 至于服务的那一块,根本不用想,各种系统包包括什么多进程等等的webpack是打包不了的, 但是至于怎么配合这个就是本篇文章说的重点


react如何用于后台渲染

创建项目

image.png 类似这样 然后终端 cd ./react-server-render  然后执行 yarn init -y  或者 cnpm init -y 

下载依赖

yarn add koa react react-dom webpack 

文件创建

首先先建一个1.jsx放在 components目录下 像这样 image.png 然后随便写一个react组件

import React from 'react';

export default class App extends React.Component {
  render(){
    return (<div>app</div>)
  }
}

然后在根目录建一个server.js

const Koa = require('koa');
const ReactDOMServer = require('react-dom/server')

let app = new Koa();

app.use(ctx=>{
	ctx.body = ???
})

app.listen(9000)

 然后呢?按着咱们正常的思路现在走一遍,首先需要一个服务器,然后可以先随便给一个use请求什么都返回这个就完了,注意这时候有问题了,要返回什么? 直接nodejs里是不能直接引入jsx的会报错,咱们可以先来试一下

从react官网可以看到 image.png

ReactDOMServer里有一个renderToString方法,可以把组件变成字符串,咱们直接试一下

解决node中不能引入jsx问题

 server.js

const Koa = require('koa');
const ReactDOMServer = require('react-dom/server')
const Cp1 = require('./components/1');

let app = new Koa();

app.use(ctx=>{
  ctx.body = ReactDOMServer.renderToString(<Cp1 />)
})

app.listen(9000)

这时候运行会直接爆炸💥 image.png 咱们上文中讲过node中是不能直接写jsx语法的

引入webpack编译jsx

好的,既然这样,咱们就用咱们亲爱的webpack 安装依赖

yarn add babel-loader @babel/core @babel/preset-react

新建一个webpack.config.js

const path=require('path');

module.exports={
  mode: 'development',
  entry: './libs/index',
  target: 'node',

  output: {
    libraryTarget: 'umd',
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [
      {test: /\.jsx$/, exclude: /node_modules/, use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-react']
        }
      }}
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  }
};

咱们这个文章不是主要讲webpack的,直接贴代码了就 大家看webpack配置应该也看到了,里面的entry是 libs/index 没错,咱们需要一个单独的目录来区分一下,哪些是react项目,哪些是node项目 然后再建一个 index.jsx放到libs下  /libs/index.tsx

const React=require('react');
const ReactDOMServer=require('react-dom/server');
const App=require('../components/1');

module.exports=function render(){
  return ReactDOMServer.renderToString(<App />);
}

这个时候,我们来编译一下

webpack

就会看到这样的结果 image.png

然后改咱们server.js

const Koa = require('koa');
const { render } = require('./dist/main');

let app = new Koa();

app.use(async ctx => {
  let str = render();

  ctx.body = str;
});

app.listen(8000);

 /components/1.jsx

import React from 'react';

export default class App extends React.Component {
  constructor(props){
    super(props)
  }
  render(){
    return (<div>app</div>)
  }
}

然后 webpack 一下 然后运行咱们的server.js node server.js 然后访问 http://localhost:8000/ image.png 就会看到咱们想要的结果了

并且点击右键查看源码 image.png 可以看到真正的元素了,不再是想之前cli创建的一样只有一个 <div id="app"></div> 了