olla / react
Requires
- php: >=5.5.0
- nacmartin/phpexecjs: ^2.0
- twig/twig: ^1.20|^2.0
Requires (Dev)
README
ReactRenderer 允许你在PHP项目中实现React.js的客户端和服务器端渲染,从而开发通用(同构)应用程序。
它之前是 ReactBundle 的一部分,但现在可以作为独立组件使用。
如果你希望与Silex一起使用它,请查看 @teameh 的 Silex React Renderer Service Provider。
功能包括
- 预渲染服务器端React组件,用于SEO、加快页面加载速度、为禁用JavaScript的用户或渐进式Web应用程序。
- 集成Twig。
- 客户端渲染将获取服务器端渲染的DOM,识别并控制它,直到需要再次渲染组件。
- 服务器和客户端代码的错误和调试管理。
- 简单集成Webpack。
完整示例
为了查看一个完整的实时示例,包括合理的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_side
、server_side
或 both
。
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组件。