zoukankan      html  css  js  c++  java
  • 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等,这里不一一贴出代码,感谢的同学可以下载项目源码进行研究。

    更完整版本请参考完整例子仓库地址。
    (以后有时间再补全详细说明,现在感兴趣的同学可参考代码进行理解。)

  • 相关阅读:
    [HNOI2002]营业额统计
    HDU 1374
    HDU 3345
    HDU 2089
    Graham扫描法
    Codeforces 1144D Deduction Queries 并查集
    Codeforces 916E Jamie and Tree 线段树
    Codeforces 1167F Scalar Queries 树状数组
    Codeforces 1167E Range Deleting
    Codeforces 749E Inversions After Shuffle 树状数组 + 数学期望
  • 原文地址:https://www.cnblogs.com/idiv/p/10013464.html
Copyright © 2011-2022 走看看