面试官:说说React-SSR的原理

前言

所谓同构,简而言之就是,第一次访问后台服务时,后台直接把前端要显示的界面全部返回,而不是像 SPA 项目只渲染一个 <div id="root"></div> 剩下的都是靠 JavaScript 脚本去加载。这样一来可以大大减少首屏等待时间。

同构概念并不复杂,它也非项目必需品,但是探索它的原理却是必须的。

阅读本文需要你具备以下技术基础: Node.js 、 React 、 React Router 、 Redux 、 webpack 。

本文将分以下两部分去讲述:

  1. 同构思路分析,让你对同构有一个概念上的了解;
  2. 手写同构框架,深入理解同构原理。

同构思路

CSR 客户端渲染

CSR 客户端渲染,这个就是很好理解了,使用 React , React Router  前端自己控制路由的 SPA 项目,就可以理解成客户端渲染。它有一个非常大的优势就是,只是首次访问会请求后台服务加载相应文件,之后的访问都是前端自己判断 URL 展示相关组件,因此除了首次访问速度慢些之外,之后的访问速度都很快。

执行命令: create-react-app react-csr 创建一个 React SPA 单页面应用项目 。
执行命令: npm run start 启动项目。

查看网页源代码: image.png 只有一个 <div id="root"></div> 和 一些 script  脚本。最终呈现出来的界面却是这样的: image.png 原理很简单,相信学习过 webpack 的同学都知道,那就是 webpack 把所有代码都打包成相应脚本并插入到 HTML 界面中,浏览器会解析 script 脚本,通过动态插入 DOM 的方式展示出相应界面。参考 前端react面试题详细解答

客户端渲染的优劣势

客户端渲染流程如下: image.png

优势:

  • 前端负责渲染页面,后端负责实现接口,各自干好各自的事情,对开发效率有极大的提升;
  • 前端在跳转界面的时候不需要请求后台,加速了界面跳转的速度,提高用户体验。

劣势:

  • 由于需要等待 JS 文件加载以及后台接口数据请求因此首屏加载时间长,用户体验较差;
  • 由于大部分内容都是通过 JS 加载因此搜索引擎无法爬取分析网页内容导致网站无法 SEO 。

SSR 服务端渲染

SSR 是服务端渲染技术,它本身是一项比较普通的技术, Node.js 使用 ejs 模板引擎输出一个界面这就是服务端渲染。每次访问一个路由都是请求后台服务,重新加载文件渲染界面。

同样我们也来创建一个简单的 Node.js 服务:

mkdir express-ssr
cd express-ssr
npm init -y
touch app.js
npm i express --save

app.js

const express = require('express')
const app = express()

app.get('/',function (req,res) {
   
  res.send(
    `<html>        <head>            <title>express ssr</title>        </head>        <body>            <h1>Hello SSR</h1>        </body>    </html>`
  )
})

app.listen(3000);

启动服务: node app.js

image.png

这就是最简单的服务端渲染一个界面了。服务端渲染的本质就是页面显示的内容是服务器端生产出来的。

服务端渲染的优劣势

服务端渲染流程:

image.png

优势:

  • 整个 HTML 都通过服务端直接输出 SEO 友好;
  • 加载首页不需要加载整个应用的 JS 文件,首页加载速度快。

劣势:

  • 访问一个应用程序的每个界面都需要访问服务器,体验对比 CSR 稍差。

我们会发现一件很有意思的事,服务端渲染的优点就是客户端渲染的缺点,服务端渲染的缺点就是客户端渲染的优点,反之亦然。那为何不将传统的纯服务端直出的首屏优势和客户端渲染站内跳转优势结合,以取得最优解?这就引出了当前流行的服务端渲染( Server Side Rendering ),或者称之为“同构渲染”更为准确。

同构渲染

所谓同构,通俗的讲,就是一套 React 代码在服务器上运行一遍,到达浏览器又运行一遍。
服务端渲染完成页面结构,客户端渲染绑定事件。它是在 SPA 的基础上,利用服务端渲染直出首屏,解决了单页面应用首屏渲染慢的问题。

同构渲染流程

image.png

简单同构案例

要实现同构,简单来说就是以下两步:

  1. 服务端要能运行 React 代码;
  2. 浏览器同样运行 React 代码。

1、创建项目

mkdir react-ssr
cd react-ssr
npm init -y

2、项目目录结构分析

├── src
│   ├── client
│   │   ├── index.js // 客户端业务入口文件
│   ├── server
│   │   └── index.js // 服务端业务入口文件
│   ├── container    // React 组件
│   │   └── Home
│   │       └── Home.js
│   │
├── config // 配置文件夹
│   ├── webpack.client.js // 客户端配置文件
│   ├── webpack.server.js // 服务端配置文件
│   ├── webpack.common.js // 共有配置文件
├── .babelrc // babel 配置文件
├── package.json

首先我们编写一个简单的 React 组件, container/Home/Home.js

import React from "react";

const Home = ()=>{
   
  return (
    <div>
      hello world      <br/>
      <button onClick={
   ()=> alert("hello world")}>按钮</button>
    </div>
  )
}

export default Home;

安装客户端渲染的惯例,我们写一个客户端渲染的入口文件, client/index.js

import React from "react";
import ReactDom from "react-dom";
import Home from "../containers/Home";

ReactDom.hydrate(<Home/>,document.getElementById("root"));
// ReactDom.render(<Home/>,document.getElementById("root"));

以前看到的都是调用 render 方法,这里使用 hydrate 方法,它的作用是什么?

ReactDOM.hydrate

与 render() 相同,但它用于在 ReactDOMServer  渲染的容器中对 HTML 的内容进行 hydrate 操作。 React 会尝试在已有标记上绑定事件监听器。

我们都知道纯粹的 React 代码放在浏览器上是无法执行的,因此需要打包工具进行处理,这里我们使用 webpack ,下面我们来看看 webpack 客户端的配置:

webpack.common.js

module.exports = {
   
  module:{
   
    rules:[
      {
   
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      }
    ]
  }
}

.babelrc

{
   
  "presets":[
    ["@babel/preset-env"],
    ["@babel/preset-react"]
  ]
}

webpack.client.js

const path = require("path");
const {
   merge} = require("webpack-merge");
const commonConfig = require("./webpack.common");

const clientConfig = {
   
  mode: "development",
  entry:"./src/client/index.js",
  output:{
   
    filename:"index.js",
    path:path.resolve(__dirname,"../public")
  },
}

module.exports = merge(commonConfig,clientConfig);

代码解析:通过 entry 配置的入口文件,对 React 代码进行打包,最后输出到 public 目录下的 index.js 。

在以往,直接在 HTML 引入这个打包后的 JS 文件,界面就显示出来了,我们称之为纯客户端渲染。这里我们就不这样使用,因为我们还需要服务端渲染。

接下来,看看服务端渲染文件 server/index.js

import express from "express";
import {
    renderToString } from "react-dom/server";
import React from "react";
import Home from "../containers/Home";

const app = express(); // {1}
app.use(express.static('public')) // {2}
const content = renderToString(<Home />); //{3}

app.get('/',function (req,res) {
   
  // {4}
  res.send(`    <!doctype html>    <html lang="en">      <
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Router 是一个用于在 React 应用中实现路由功能的库。它提供了一种声明式的方式来定义和管理应用程序的路由,使得页面之间的导航和状态管理更加方便和灵活。React Router 的原理可以概括为以下几个关键概念和步骤: 1. **路由器(Router):** React Router 提供了多种类型的路由器组件,如 `BrowserRouter`、`HashRouter` 等。路由器组件负责监听 URL 的变化,并将相应的路由信息传递给应用程序。 2. **路由规则(Route):** 使用 `Route` 组件来定义路由规则。每个 `Route` 组件负责匹配 URL,并在匹配成功时渲染对应的组件。可以通过 `path` 属性来指定匹配的路径,通过 `component` 属性来指定要渲染的组件。 3. **导航(Navigation):** React Router 提供了多种导航组件来实现页面之间的跳转,如 `Link`、`NavLink` 等。这些导航组件会生成对应的 `<a>` 标签,并处理点击事件来触发路由的变化。 4. **路由参数(Route Parameters):** 可以通过在路由规则中使用冒号(`:`)来定义动态的路由参数,如 `/users/:id`。在匹配成功后,可以通过 `props.match.params` 来获取路由参数的值。 5. **嵌套路由(Nested Routes):** React Router 支持嵌套路由,即在一个组件内部定义子组件的路由规则。可以通过嵌套的 `Route` 组件来实现。 6. **路由守卫(Route Guards):** React Router 提供了一些钩子函数,如 `beforeEnter`、`beforeLeave` 等,用于实现路由守卫功能。可以在路由跳转前或跳转后执行一些逻辑操作,例如验证用户权限、处理登录状态等。 总的来React Router 的原理是通过路由器监听 URL 的变化,根据定义的路由规则匹配对应的组件进行渲染,同时提供导航组件来实现页面之间的跳转。这样可以实现单页面应用(SPA)的路由功能,使得页面的切换和状态管理更加灵活和可控。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值