SSR项目学习和使用

今天开会,同事突然说,我们的项目首屏渲染时间太久,引起客诉了。怎么办呢,需要把项目从客户端渲染转为服务端渲染,也就是SSR,大家一起想想研究一下怎么改才好。
我:啊?
于是,打开Google,郑重地打出了三个字母+三个汉字:什么是SSR
把学习笔记记录在这。

————————
最后我们使用了next框架改。

——————————

什么是SSR

  • 先看:https://juejin.cn/post/6844904017487724557
  • 讲的也很详细,带React样例(我的重点参考):https://juejin.cn/post/6844903881390964744
  • Vue项目的SSR demo,但是里面有很多内容是共通的:https://github.com/yacan8/blog/issues/30
  • 补充个,服务端渲染也是分不同渲染方式的:https://zhuanlan.zhihu.com/p/341229054
    (像是官网等等数据不流通且不针对用户特殊处理的就可以使用SSG静态渲染)

    SSR项目学习和使用_第1张图片

如果要动手将一个单页面React应用转为SSR项目应该怎么做?

  • react ssr 服务端渲染入门
    https://juejin.cn/post/6935621064517451812
  • https://cnodejs.org/topic/5b9ce0298f5b0c1c59ea0e5b

大佬们的demo大概是以下几个流程,我先以自己理解写点笔记,然后尝试着写一个简单demo出来。

1、传输静态页面。

使用ReactDOMServer.renderToString 或者ReactDOMServer.renderToNodeStream 方法,将写的JSX解析为html字符串。

2、同构。

什么是同构呢?因为服务端和客户端要获得相同的默认state数据,在初始化的时候,服务端获取了一遍数据,然后注入state,传入客户端,客户端就不用重新请求一遍了(还可能造成数据不统一),服务端和客户端的数据保持一致,就是同构。
同构和数据、js请求、路由都有关,所以我这认为同构是交互层面的。
SSR项目学习和使用_第2张图片

因为我们项目原本就定了使用egg,所以我得在egg的基础上考虑SSR。
首先npm init egg --type=simple 创建一个空的egg项目。
参考
别人写好可使用的egg-react-ssr 脚手架。https://github.com/zhangyuang/egg-react-ssr
其他demo 参考:https://github.com/yjdjiayou/react-ssr-demo (从头创建)

1、node服务器返回静态HTML页面。
对Egg框架稍微了解一点之后,我需要把返回HTML的代码 放在controller/home里。
也就是说,首先建立一个写react代码的文件夹(我建了page文件夹),然后再在node服务器里引用react页面代码,这样才能做到服务器解析html渲染。
然后,因为我们写的都是JSX片段(虚拟DOM),肯定是无法渲染的,第二步就是将它在node端转换为html代码返回给浏览器。

// 新建一个page文件夹, /page/index.js

import React from 'react'

export default function HTML(props){
  return <div>
  <p>SSR</p>
  <p>纯静态渲染</p>
</div>
}

const { renderToString } = require('react-dom/server');renderToString就是react中提供用于编译虚拟DOM的方法,它可以将react的虚拟dom转为真实的html节点。我们使用这个方法帮我们处理一下引入的页面。

// /app/controller/home.js

'use strict';

const Controller = require('egg').Controller;
import Home from '../../page/index';
import { renderToString } from 'react-dom/server';

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    ctx.type = 'text/html'
    ctx.status = 200;
    const content = renderToString(<Home />);

    ctx.body = `
    
      
        ssr
      
      
        
${content}
`
; } } module.exports = HomeController;

看上去好像和资料参考里的一模一样,肯定没问题了!自信满满的我在这个项目终端里打下npm start!
嗯!不出意料,根本启不起来!是因为import无法解析,egg中暂时不支持esm,所以需要把引入方式改成cmd模式,但是egg项目中使用esm没关系,我们的page页面里用cmd根本不好写,所以思来想去,难不成还是得用babel?
查询了一些资料,不少项目都是将node端和客户端代码分别打包,虽然我还是挺懵的,但是搞不懂的话就写bug试一下吧。
于是自信满满(x)的我又在百度打下了几个字:怎么使用webpack babel

总之咱们的pages文件夹下肯定是用webpack编译一下了。
这部分的参考:https://blog.csdn.net/u012443286/article/details/79577545
webpack文件内容参考https://github.com/yjdjiayou/react-ssr-demo
npm i webpack babel-loader babel-core babel-preset-env -D
npm i @babel/core @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties -D

新增三个文件
SSR项目学习和使用_第3张图片

// /config/webpack.base.js

'use strict';

// const path = require('path');
module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: [
            '@babel/preset-env',
            '@babel/preset-react',
          ],
          plugins: [
            '@babel/plugin-proposal-class-properties',
          ],
        },
      },
    ],
  },
};

// /config/webpack.client

'use strict';

const path = require('path');
const merge = require('webpack-merge');
const base = require('./webpack.base');

module.exports = merge(base, {
  entry: './src/client/index.js',
  output: {
    path: path.resolve('public'),
    filename: 'client.js',
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
        ],
      },
    ],
  },
});
// config/webpack.server

'use strict';

const path = require('path');
const nodeExternal = require('webpack-node-externals');
const merge = require('webpack-merge');
const base = require('./webpack.base');

module.exports = merge(base, {
  // 注意这个值
  target: 'node',
  entry: './src/server/index.js',
  output: {
    path: path.resolve('build'),
    filename: 'server.js',
  },
  externals: [ nodeExternal() ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 服务端渲染不能用 style-loader,因为 node 没有 document 对象,无法插入 style 标签
          // 服务端本来就不能渲染 dom,只是提供 html/css/js 代码给浏览器,交给浏览器去渲染
          // 服务端返回的 html 源码里,没有 style 标签
          // 而在浏览器中的 html 源码里,有 style 标签,是通过 js 插入进去的
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
        ],
      },
    ],
  },
});


你可能感兴趣的:(前端,react.js,javascript,前端)