React同构起步
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同构起步的更多相关文章
- React同构直出原理浅析
通常,当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互.然后,如果加载组件的过程需要向服务端发出Ajax请求等,客户端和服务端又进行了一次交互,这 ...
- React 同构
React 同构 搬运 https://segmentfault.com/a/1190000004671209 究竟什么是同构呢? 同构就是希望前端 后端都使用同一套逻辑 同一套代码 Nodejs出现 ...
- 打造高可靠与高性能的React同构解决方案
前言 随着React的兴起, 结合Node直出的性能优势和React的组件化,React同构已然成为趋势之一.享受技术福利的同时,直面技术挑战,在复杂场景下,挑战10倍以上极致的性能优化. 什么是同构 ...
- React同构直出优化总结
收录待用,修改转载已取得腾讯云授权 作者:郭林烁 joeyguo 原文地址 React 的实践从去年在 PC QQ家校群开始,由于 PC 上的网络及环境都相当好,所以在使用时可谓一帆风顺,偶尔遇到点小 ...
- React 同构开发(二)
React 同构 所谓同构,简单的说就是客户端的代码可以在服务端运行,好处就是能极大的提升首屏时间,避免白屏,另外同构也给SEO提供了很多便利. React 同构得益于 React 的虚拟 DOM.虚 ...
- React 同构开发(一)
为什么要做同构 要回答这个问题,首先要问什么是同构.所谓同构,顾名思义就是同一套代码,既可以运行在客户端(浏览器),又可以运行在服务器端(node). 我们知道,在前端的开发过程中,我们一般都会有一个 ...
- 自制的React同构脚手架
代码地址如下:http://www.demodashi.com/demo/12575.html Web前端世界日新月异变化太快,为了让自己跟上节奏不掉队,总结出了自己的一套React脚手架,方便日后新 ...
- 腾讯新闻构建高性能的 react 同构直出方案
在腾讯新闻抢金达人活动 node 同构直出渲染方案的总结文章中我们整体了解了下同构直出渲染方案在我们项目中的使用.正如我在上篇文章结尾所说的: 应用型技术的难点不是在克服技术问题,而是在于能够不断的结 ...
- 七天接手react项目-起步
七天接手react项目-起步 背景 假如七天后必须接手一个 react 项目(spug - 一个开源运维平台),而笔者只会 vue,之前没有接触过 react,此刻能做的就是立刻展开一个"7 ...
随机推荐
- SQLSERVER 连接常见问题
1.从索引 0 处开始,初始化字符串的格式不符合规范 解决办法:检查数据库连接字符串 参考链接: https://www.cnblogs.com/guodongsky/archive/2013/04/ ...
- [大数据] hadoop全分布式安装
一.准备工作 在伪分布式的搭建基础上修改配置,搭建全分布式hadoop环境,伪分布式安装参照 hadoop伪分布式安装. 首先准备4台虚拟机,信息如下: 192.168.1.11 namenode1 ...
- JavaScript 运算符是什么?
㈠JavaScript 运算符 ⑴运算符 = 用于赋值. ⑵运算符 + 用于加值. ⑶示例: 向变量赋值,并把它们相加: ; // 向 x 赋值 5 ; // 向 y 赋值 2 var z = ...
- [Flask]celery异步任务队列的使用
Celery异步任务队列 目录结构树: 配置文件config.py: # 设置中间人地址 broker_url = 'redis://127.0.0.1:6379/1' 主main.py: impor ...
- 计算机网络(四),TCP三次握手
目录 1.三次握手详情 2.为什么需要三次握手才能建立连接 3.首次握手的隐患---SYN超时的问题 4.建立连接之后,Client出现故障 四.TCP三次握手 1.三次握手详情 (1)一开始,客户端 ...
- T级别视频上传解决方案
之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...
- [笔记]MongoDB 二(Linux下MongoDB API C++编程)
一.连接类 DBClientConnection,派生自DBClientBase.DBClientBase类是实现query, update, insert, remove等功能. 构造函数:DBCl ...
- java 手机号/身份证(*)加密隐藏中间某几位几位
//手机号 保留前3 后4 String phone = "18771632488"; System.out.println(phone.replaceAll("(\\d ...
- 【python】windows更改jupyter notebook(ipython)的默认打开工作路径
写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文! 本博客全网唯一合法URL:ht ...
- BuiltIn库
简介 作为一门表格语言,为了保持简单的结构,RF没有像别的高级语言那样提供类似ifelsewhile等内置关键字来实现各种逻辑功能(注1),而是提供给了用户BuiltIn库.如果用户想在测试用例中实现 ...