olla/react

客户端和服务器端React渲染

维护者

详细信息

github.com/ollatech/react

源代码

v2.1.1 2018-01-29 22:23 UTC

README

ReactRenderer 允许你在PHP项目中实现React.js的客户端和服务器端渲染,从而开发通用(同构)应用程序。

它之前是 ReactBundle 的一部分,但现在可以作为独立组件使用。

如果你希望与Silex一起使用它,请查看 @teameh 的 Silex React Renderer Service Provider

功能包括

  • 预渲染服务器端React组件,用于SEO、加快页面加载速度、为禁用JavaScript的用户或渐进式Web应用程序。
  • 集成Twig。
  • 客户端渲染将获取服务器端渲染的DOM,识别并控制它,直到需要再次渲染组件。
  • 服务器和客户端代码的错误和调试管理。
  • 简单集成Webpack。

Build Status Latest Stable Version Latest Unstable Version License

完整示例

为了查看一个完整的实时示例,包括合理的Webpack设置、一个示例应用程序以及如何在Symfony项目中集成,请查看 Symfony React Sandbox

安装

ReactRenderer 使用Composer,如果有疑问,请访问 composer网站

此命令将在你的项目中安装 ReactRenderer

$ composer require limenius/react-renderer

ReactRenderer 依据PSR-4约定命名其类,因此你可以将其与自动加载器集成。

用法

JavaScript和Webpack设置

为了使用React组件,你需要将其注册到JavaScript中。此组件使用React On Rails npm包来渲染React组件(不要担心,你不需要编写任何Ruby代码! ;) )。

暴露React组件的代码可能看起来像这样

import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';

ReactOnRails.register({ RecipesApp });

其中RecipesApp是我们想要在本例中注册的组件。

请注意,你很可能需要为服务器端和客户端组件使用分开的入口点,以处理诸如路由等问题。这是任何通用(同构)应用程序的常见问题。再次,请参阅沙盒以了解如何处理此类问题。

如果你使用服务器端渲染,你也应该有一个Webpack包,其中包含React、React on Rails以及将用于评估你的组件的JavaScript代码。

有关更多信息,请参阅 symfony-react-sandbox中的Webpack配置

启用Twig扩展

首先,你需要配置和启用Twig扩展。

use Limenius\ReactRenderer\Renderer\PhpExecJsReactRenderer;
use Limenius\ReactRenderer\Twig\ReactRenderExtension;
use Limenius\ReactRenderer\Context\SymfonyContextProvider;

// SymfonyContextProvider provides information about the current request, such as hostname and path
// We need an instance of Symfony\Component\HttpFoundation\RequestStack to use it
$contextProvider = new SymfonyContextProvider($requestStack);
$renderer = new PhpExecJsReactRenderer(__DIR__.'/client/build/server-bundle.js', false, $contextProvider);
$ext = new ReactRenderExtension($renderer, $contextProvider, 'both');

$twig->addExtension(new Twig_Extension_StringLoader());
$twig->addExtension($ext);

ReactRenderExtension 需要一个 renderer 参数和一个字符串,该字符串定义了我们将渲染React组件的方式 client_sideserver_sideboth

renderer 是继承自 AbstractReactRenderer 的一个渲染器。

此库目前提供两个渲染器

  • PhpExecJsReactRenderer:内部使用 phpexecjs 自动检测可用的最佳javascript运行时。
  • ExternalServerReactRenderer:依赖于外部nodeJs服务器。

现在你可以使用以下方式在Twig模板中插入React组件

{{ react_component('RecipesApp', {'props': props}) }}

其中,RecipesApp 是我们组件的名称,而 props 是您组件的属性。属性可以是 JSON 编码的字符串或数组。

例如,一个将生成有效属性的控制台操作可能是

/**
 * @Route("/recipes", name="recipes")
 */
public function homeAction(Request $request)
{
    $serializer = $this->get('serializer');
    return $this->render('recipe/home.html.twig', [
        'props' => $serializer->serialize(
            ['recipes' => $this->get('recipe.manager')->findAll()->recipes], 'json')
    ]);
}

服务器端、客户端或两者?

您可以选择是否仅在客户端、仅在服务器端或同时渲染您的 React 组件,无论是在上述配置中,还是在 Twig 标签的基础上。

如果您设置了 Twig 调用的 rendering 选项,您可以覆盖您的配置(默认是在服务器端和客户端都渲染)。

{{ react_component('RecipesApp', {'props': props, 'rendering': 'client_side'}) }}

将仅在客户端渲染组件,而下面的代码

{{ react_component('RecipesApp', {'props': props, 'rendering': 'server_side'}) }}

... 将仅在服务器端渲染组件(因此动态组件将无法工作)。

或者两者都渲染(默认)

{{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}) }}

您可以通过查看生成的 HTML 代码来探索这些选项。

调试

在从 PHP 运行服务器端 JavaScript 代码时,一个重要点是管理由 console.log 抛出的调试消息。ReactRenderer 受 React on Rails 的启发,有方法可以将 console.log 消息回放至浏览器中的 JavaScript 控制台。

要启用跟踪,您可以设置一个配置参数,如上述所述,或者您可以在模板中这样设置

{{ react_component('RecipesApp', {'props': props, 'trace': true}) }}

请注意,在这种情况下,您可能会看到类似以下这样的 React 警告

"警告:render():目标节点由 React 渲染的标记,但也存在无关节点。这种情况通常是由在服务器端渲染的标记周围插入的空白引起的。"

此警告是无害的,在生产中禁用跟踪后将会消失。这意味着当在客户端渲染组件并与服务器端等效组件进行比较时,React 找到了额外的字符。这些字符是您的调试消息,所以请不用担心。

上下文

这个库将为 React 组件提供有关当前请求的上下文信息。您的组件在实例化时将接收两个参数

const App = (initialProps, context) => {
}

Symfony 上下文提供者有这种实现

    public function getContext($serverSide)
    {
        $request = $this->requestStack->getCurrentRequest();

        return [
            'serverSide' => $serverSide,
            'href' => $request->getSchemeAndHttpHost().$request->getRequestUri(),
            'location' => $request->getRequestUri(),
            'scheme' => $request->getScheme(),
            'host' => $request->getHost(),
            'port' => $request->getPort(),
            'base' => $request->getBaseUrl(),
            'pathname' => $request->getPathInfo(),
            'search' => $request->getQueryString(),
        ];
    }

因此,您可以在 React 组件中访问这些属性,以获取有关请求的信息,以及它是否已服务器端或客户端渲染。

服务器端模式

此库支持两种使用服务器端渲染的模式

  • 使用 PhpExecJs 自动检测 JavaScript 环境(通过终端命令调用 node.js 或使用 V8Js PHP)并通过它运行 JavaScript 代码。

  • 使用外部 node.js 服务器(示例)。它将使用一个虚拟服务器,该服务器对您的逻辑一无所知,为您渲染 React。引入了更多的操作复杂性(您需要保持 node 服务器运行)。

目前,最佳选择是使用 V8rjs 并在生产中启用缓存,如我们将在下一节中看到的。

缓存

如果您在 config.prod.yaml 或 config/packages/prod/limenius_react.yaml 中添加以下配置,并且您已安装 V8js,则此包将更快

limenius_react: serverside_rendering: cache: enabled: true # 您的应用程序名称,它是存储快照的缓存键。key: "recipes_app"

首次页面渲染后,这将存储 V8js JS 虚拟机的快照到缓存中,因此在下一次访问时,您的整个 JavaScript 应用程序不需要再次处理,只需渲染您想要的特定组件。

启用缓存后,如果您更改了您的 JS 应用程序的代码,您将需要清除缓存。

Redux

如果您正在使用 Redux,则可以使用此库来激活您的存储。

在渲染依赖于存储的组件之前,在您的Twig文件中使用 redux_store

{{ redux_store('MySharedReduxStore', initialState ) }}
{{ react_component('RecipesApp') }}

在这里的 MySharedReduxStore 是您在javascript中使用以获取存储的标识符。`initialState` 可以是一个JSON编码的字符串或一个数组。

然后,就像您公开组件一样,在您的包中公开您的存储。

import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';
import configureStore from './store/configureStore';

ReactOnRails.registerStore({ configureStore });
ReactOnRails.register({ RecipesApp });

最后,在您本应使用传递给 registerStore 的对象的地方使用 ReactOnRails.getStore

// Get hydrated store
const store = ReactOnRails.getStore('MySharedReduxStore');

return (
  <Provider store={store}>
    <Scorecard />
  </Provider>
);

请确保在这里使用相同的标识符(MySharedReduxStore),就像您在Twig文件中设置存储时使用的那样。

沙箱 中有一个示例。

生成器函数

您可以选择从JavaScript代码中返回一个对象,而不是返回一个组件。

这种情况的一个用例是在服务器端渲染中使用 React Helmet 来渲染标题或其他元标签。您可能希望返回与标题一起生成的组件的HTML。

export default (initialProps, context) => {
    const renderedHtml = {
      componentHtml: renderToString(
        <MyApp/>
      ),
      title: Helmet.renderStatic().title.toString()
    };
    return { renderedHtml };
}

在这些情况下,将要渲染的主要HTML代码必须位于 componentHtml 键中。您可以在Twig中访问结果数组。

{% set recipes = react_component_array('RecipesApp', {'props': props}) %}
{% block title %}
  {{ recipes.title is defined ? recipes.title | raw : '' }}
{% endblock title %}

{% block body %}
  {{ recipes.componentHtml | raw }}
{% endblock %}

沙箱中有一个这样的示例。

许可证

此库受MIT许可证的约束。请参阅包中的完整许可证。

LICENSE.md

致谢

ReactRenderer深受伟大的 React On Rails 启发,并使用其npm包来渲染React组件。