React同构从0到1

前言

如果你想快速做react同构的新项目建议你去了解next.js等成熟框架,本教程仅限于想了解如何从0开始实现一个同构环境过程的同学,对于想改造现有spa项目的同学也很有帮助,具有一定参考价值。

需要实现的目标

  • 服务端渲染
  • 带路由的服务端渲染
  • 同构
  • 热更新
  • 前后端按需加载
  • 服务端预加载数据
  • css模块化(选修)
  • seo(选修)

以上任务中热更新和代码按需加载比较难实现,实现按需加载时会带来同构不一致的问题,随着你可能想做的越来越完美,就要添加功能,我们最起码的需求是要保证同构,否则这一切除了浪费服务器资源之外将毫无意义。

可能用到的技术

我们拥抱最新技术,所用框架全是最新版本,可能你看到这个教程时已经有更新的版本产生了。

  • webpack
  • express
  • react
  • react-dom
  • react-router-dom
  • react-router-config
  • react-loadable
  • redux
  • babel

看到这些框架是不是很凌乱,我做这个教程的时候仅仅对react和redux了解的还算熟悉,react-router-dom了解一般,仅仅会使用路由,其他的基本上是小白,用到啥就去github上看api即可,所以大家不必担心,而且这些不是必须的,比如express完全可以用koa取代,除了react技术栈其他的并不是核心,对于webpack建议大家多接触些,非常有用,它的生态已经很全面了,我是在做这个同构时对webpack有不少需求然后边做边学,现在想想它确实是个好帮手。

服务端渲染

该目标简单到爆,寥寥几行代码即可实现。

import React from 'react';
import { renderToString } from 'react-dom/server';
import express from 'express'; var app = express();
var ReactApp = (props) => <h1>Hello SSR from {props.path}</h1>; app.get('*', (req, res, next) => {
var ssrDomStr = renderToString(
<ReactApp path={req.url} />
); res.send(ssrDomStr);
return;
}); app.listen(3000);

注意,我用的是ES6语法,需要自行配置babel,用babel-node替代node运行程序即可。

带路由的服务端渲染

该实现目标也不难,原理很简单,就是客户端怎么搞服务端也怎么搞就是了,代码如下

import React from 'react';
import { StaticRouter, Switch, Route } from 'react-router-dom';
import { renderToString } from 'react-dom/server';
import express from 'express'; var app = express();
var Home = ({match}) => <h1>Hello Home, {match.url}</h1>;
var Todo = ({match}) => <h1>Hello Todo, {match.url}</h1>;
var ReactApp = (props) => (
<div>
<div>这里可以放些公共头之类的</div>
<Switch>
<Route path="/" exact component={Home}></Route>
<Route path="/todo" exact component={Todo}></Route>
</Switch>
</div>
); app.get('*', function (req, res, next) {
var context = {};
var ssrDomStr = renderToString(
<StaticRouter
location={req.url}
context={context}
>
<ReactApp />
</StaticRouter>
); res.send(ssrDomStr);
return;
}); app.listen(3000);

做到这里我们已经能让服务器根据输入的地址不同用react框架渲染出不同的内容了,为自己鼓个掌吧,简单分析下代码,上半部分就是很普通的react路由配置,下面处理渲染时使用了StaticRouter,这是专门在没有浏览器对象环境中使用的,比如服务端或者无头浏览器,我们传入当前路径给让StaticRouter知道匹配哪个路由,context属性能携带一些信息出来,比如有没有匹配到等,现在用不到这些信息,所以没做什么处理,后面随着深入可以慢慢了解。

同构

我们目前说的都是服务端渲染,既然同构至少要两端吧,不然谁和谁同,这里要说的就是客户端渲染和服务端渲染相同,你可能发现我们上面的代码返回的只是一些html片段,你里面如果绑定事件肯定不会在浏览器里实现,因为查看源码你会发现里面并没有脚本文件,因为客户端没有参与渲染(这里指客户端脚本没有参与渲染),而做这些需要同构。简单的说就是服务端和客户端协同渲染,准确来讲客户端接着服务渲染的结果继续渲染,所以这里一定要保证前后端对相同组件渲染出一致的结果,顾名思义叫同构,这只是我个人理解,也就是服务端分担了一部分客户端的工作,服务端分担要有条件的,就是它的渲染要和客户端一致,打个比方,你生产宝马汽车,从头到尾都是你生产,后来有个合作伙伴,他能生产宝马轮子,你可以让他生产轮子你接着轮子继续制造汽车,他能分担你工作唯一的条件就是他生产的轮子和你的轮子一模一样,否则他的轮子不管是大还是小都无法一次性组装一辆车,这种情况下你可能要对他的轮子修改,而此时正是浏览器会都抖动的原因,它在修改与服务端不一致的结果,我们在做的时候会踩到这些坑,庆幸的是react框架给做好了警告,我们很容易发现问题。说到这里,同构带的优势就很明显了,首屏无白屏现象,展示更快,至于为什么,想必大家明白了吧。废话少说,我们在服务端渲染的基础上加客户端渲染,所谓客户端渲染就是最熟悉不过的spa项目。这里会有一些坑或者叫一些难点(大神直接无视)。

// 核心代码同上, 在服务端返回给浏览器时需要带上webpack前端打包后的js文件,这里有整个前端的代码逻辑,包括事件绑定,样式加载(未抽离css文件时)等
res.send(html.repalce('<div id="root"></div>', `<div id="root">${ssrDomStr}</div>`));

上述中的html指的是html-webpack-plugin生成后的html,里面已经有客户端所需的入口代码,在它会接替服务渲染之后的事情,比如事件绑定,路由跳转等。整个代码篇幅较大,需要有相应的webpack配置,server等,这里不一一贴出代码,感谢的同学可以下载项目源码进行研究。

更完整版本请参考完整例子仓库地址。

(以后有时间再补全详细说明,现在感兴趣的同学可参考代码进行理解。)

React同构起步的更多相关文章

  1. React同构直出原理浅析

    通常,当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互.然后,如果加载组件的过程需要向服务端发出Ajax请求等,客户端和服务端又进行了一次交互,这 ...

  2. React 同构

    React 同构 搬运 https://segmentfault.com/a/1190000004671209 究竟什么是同构呢? 同构就是希望前端 后端都使用同一套逻辑 同一套代码 Nodejs出现 ...

  3. 打造高可靠与高性能的React同构解决方案

    前言 随着React的兴起, 结合Node直出的性能优势和React的组件化,React同构已然成为趋势之一.享受技术福利的同时,直面技术挑战,在复杂场景下,挑战10倍以上极致的性能优化. 什么是同构 ...

  4. React同构直出优化总结

    收录待用,修改转载已取得腾讯云授权 作者:郭林烁 joeyguo 原文地址 React 的实践从去年在 PC QQ家校群开始,由于 PC 上的网络及环境都相当好,所以在使用时可谓一帆风顺,偶尔遇到点小 ...

  5. React 同构开发(二)

    React 同构 所谓同构,简单的说就是客户端的代码可以在服务端运行,好处就是能极大的提升首屏时间,避免白屏,另外同构也给SEO提供了很多便利. React 同构得益于 React 的虚拟 DOM.虚 ...

  6. React 同构开发(一)

    为什么要做同构 要回答这个问题,首先要问什么是同构.所谓同构,顾名思义就是同一套代码,既可以运行在客户端(浏览器),又可以运行在服务器端(node). 我们知道,在前端的开发过程中,我们一般都会有一个 ...

  7. 自制的React同构脚手架

    代码地址如下:http://www.demodashi.com/demo/12575.html Web前端世界日新月异变化太快,为了让自己跟上节奏不掉队,总结出了自己的一套React脚手架,方便日后新 ...

  8. 腾讯新闻构建高性能的 react 同构直出方案

    在腾讯新闻抢金达人活动 node 同构直出渲染方案的总结文章中我们整体了解了下同构直出渲染方案在我们项目中的使用.正如我在上篇文章结尾所说的: 应用型技术的难点不是在克服技术问题,而是在于能够不断的结 ...

  9. 七天接手react项目-起步

    七天接手react项目-起步 背景 假如七天后必须接手一个 react 项目(spug - 一个开源运维平台),而笔者只会 vue,之前没有接触过 react,此刻能做的就是立刻展开一个"7 ...

随机推荐

  1. user-select 用户禁止选中

    我们在页面输入的文本按理来说应该都是可以选中的,但是如何才能让显示出来的文本或是图片不被选中呢,这时候就需要用到 user-select 属性. user-select user-select (CS ...

  2. maven项目创建4

    运行maven项目,首先要不最根项目添加到maven本地仓库,执行  项目-->右键-->Run as-->Maven install 注:创建war包项目,本地测试,创建index ...

  3. BZOJ 3940 Censoring ( Trie 图 )

    题目链接 题意 : 中文题.点链接 分析 : 直接建 Trie 图.在每一个串的末尾节点记录其整串长度.方便删串操作 然后对于问询串.由于可能有删串操作 所以在跑 Trie 图的过程当中需要拿个栈记录 ...

  4. RabbitMq运行原理浅析

    转载:https://blog.csdn.net/Evankaka/article/details/80977027 1.RabbitMq简介     AMQP,即Advanced Message Q ...

  5. Error:Connection activation failed: No suitable device found for this connection

    原文链接: https://blog.csdn.net/baiboya/article/details/80452822 ens33这个网卡一直无法激活,在网上找了半天,找到这个博主的文章,才解决,虽 ...

  6. springboot学习问题一:启动springboot报错端口被占用解决办法

    一:问题 二:分析原因 springboot启动默认端口为8080,现在提示被占用,那我们可以修改springboot的启动端口,换一个未被占用的端口即可 三:解决方法 打开application.p ...

  7. Spark学习(一)——Spark运行架构

    基本概念 在具体讲解Spark运行架构之前,需要先了解几个重要的概念: RDD:是弹性分布式数据集(Resilient Distributed Dataset)的简称,是分布式内存的一个抽象概念,提供 ...

  8. kolla-ansible-----cinder存储配置

    对接lvm后端存储 1.创建vg 可以使用裸盘或者可以采用Das.iscsi-san.fc-san等方式经存储映射到宿主机本地,然后在进行如下操作 (a)创建LVM 物理卷 /dev/sdb pvcr ...

  9. HTML基础汇总

    一.HTML的概述(了解) a.html是什么 : hypertext markup language 超文本标记语言           超文本:音频,视频,图片称为超文本..           ...

  10. JAVA初级面试题,附个人理解答案

    一,面向对象的特征:1.抽象 包括数据抽象跟行为抽象,将对象共同的特征取出形成一个类2.继承 被继承类为基类/超类,继承类为子类/派生类3.封装 多次使用道德数据或方法,封装成类,方便多次重复调用4. ...