将React引入PHP:ReactRenderer的全面解析与应用指南
ReactRenderer是一款强大的PHP库,它允许你在PHP项目中实现React.js的客户端和服务器端渲染,从而打造无处不在(isomorphic)的应用程序。原本是ReactBundle的一部分,现在已独立成为可单独使用的组件。
如果你在Silex框架下工作,可以查看@teameh提供的Silex React 渲染服务提供者。
特性包括:
- 预先渲染服务器端React组件,优化SEO,加速页面加载,支持JavaScript被禁用的用户,以及渐进式Web应用程序。
- 与Twig模板引擎集成。
- 客户端渲染会识别并接管服务器端渲染的DOM,直到需要时再重新渲染组件。
- 错误和调试管理,覆盖服务器和客户端代码。
- 简单的Webpack集成。
全面示例应用
要查看一个完整的活生生的例子,包含合理的Webpack配置,一个起始样本应用以及在Symfony项目中的整合,请访问 Symfony React Sandbox。
安装
ReactRenderer依赖Composer,如果你对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
是我们要在此示例中注册的组件。
注意,由于任何通用(isomorphic)应用都会遇到这个问题,所以很可能会需要为服务器端和客户端组件设置不同的入口点,用于处理如路由等事项。再次提醒,参考symfony-react-sandbox以了解如何应对这种情况。
如果你使用服务器端渲染,还需要有一个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 提供关于当前请求的信息,如主机名和路径
// 我们需要一个Symfony\Component\HttpFoundation\RequestStack实例来使用它
$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
需要一个渲染器和一个字符串作为参数,该字符串定义我们是在client_side
,render_side
还是both
上渲染React组件。
库目前提供了两个渲染器:
PhpExecJsReactRenderer
:内部使用phpexecjs自动检测最佳javascript运行时环境。ExternalServerReactRenderer
:依赖外部nodeJs服务器。
现在可以在你的Twig模板中插入React组件:
{{ react_component('RecipesApp', {'props': props}) }}
在这里,RecipesApp
是我们的组件名称,props
是组件的属性。属性可以是JSON编码的字符串或数组。
例如,一个产生有效props的控制器操作可能如下所示:
/**
* @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标签的基础上指定。
通过设置rendering
选项,你可以覆盖配置(默认值为both
)。
{{ 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警告:“Warning: render(): Target node has markup rendered by React, but there are unrelated nodes as well. This is most commonly caused by white-space inserted around server-rendered markup.”
这个警告是无害的,而且在生产环境中关闭追踪后就会消失。这意味着当在客户端重新渲染组件并与服务器端对应的组件进行比较时,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服务器运行,但这其实并不是大问题)。
目前,生产环境的最佳选项是使用外部服务器,因为V8js很难编译。然而,如果你能够编译它,或者你的发行版/操作系统有很好的包,那么在开启缓存的情况下,这是一个非常不错的选择。
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 });