Symfony 4 如何使用 React 设置 SPA(单页应用程序)?

Ber*_*rdA 2 php symfony reactjs react-router-dom

我无法让 Symfony 4 在 SPA 设置中正常工作。

具体来说,当我使用 React-router 链接导航时,一切正常。

但是,如果我尝试直接访问任何路由(除了 home ),Symfony 会拦截它,当然,会为找不到路由引发错误。

将 Symfony 放在子域中并将其仅用作 API 的替代方案是不可行的,因为我需要框架提供的所有用户和会话管理工具。

当然,我将需要 Symfony 的所有 API 调用路由到后端。

我正在使用 Symfony 4 的默认目录结构,只在顶层为所有 react/redux 代码添加一个目录 /client。

构建代码放在 /public/build 下。

我还尝试使用以下代码在 /public 上放置一个 .htaccess 文件,但这没有帮助。

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
Run Code Online (Sandbox Code Playgroud)

谢谢

Pat*_*ohn 11

解决方案可能如下。

带有路由注释的控制器,所有路由都将通过使用此注释来处理,无论路由可能有多少个参数:

namespace App\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends Controller
{
    /**
     * @Template("default/index.html.twig")
     * @Route("/{reactRouting}", name="index", requirements={"reactRouting"=".+"}, defaults={"reactRouting": null})
     */
    public function index()
    {
        return [];
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您有不应由 react 处理的路由,则您的注释可以将它们排除在外,如下所示 - 所有以 api 开头的路由都不会由 react 处理:

@Route("/{reactRouting}", name="index", requirements={"reactRouting"="^(?!api).+"}, defaults={"reactRouting": null})
Run Code Online (Sandbox Code Playgroud)

带有根元素的 Twig 模板:

{% extends 'base.html.twig' %}

{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('build/js/app.css') }}">
{% endblock %}

{% block body %}
    <div id="root"><div>
{% endblock %}

{% block javascripts %}
<script type="text/javascript" src="{{ asset('build/js/app.js') }}"></script>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

反应索引文件:

import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Route, Switch} from 'react-router-dom';

import Home from "./containers/Home";
import AboutUs from "./containers/AboutUs";


ReactDOM.render(
       <BrowserRouter>
            <Switch>
                <Route path="/" component={Home}/>
                <Route path="/about-us" component={AboutUs}/>
            </Switch>
        </BrowserRouter>,
    document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)

我的安可配置:

var Encore = require('@symfony/webpack-encore');

Encore
    // the project directory where compiled assets will be stored
    .setOutputPath('public/build/')
    // the public path used by the web server to access the previous directory
    .setPublicPath('/build')
    .cleanupOutputBeforeBuild()
    .enableSourceMaps(!Encore.isProduction())
    // uncomment to create hashed filenames (e.g. app.abc123.css)
    // .enableVersioning(Encore.isProduction())

    // uncomment to define the assets of the project
    // .addEntry('js/app', './assets/js/app.js')
    // .addStyleEntry('css/app', './assets/css/app.scss')

    // uncomment if you use Sass/SCSS files
    // .enableSassLoader()

    // uncomment for legacy applications that require $/jQuery as a global variable
    // .autoProvidejQuery()

    .enableReactPreset()
    .addEntry('js/app', './react/index.js')
    .configureBabel((config) => {
        config.presets.push('stage-1');
    })
;

module.exports = Encore.getWebpackConfig();
Run Code Online (Sandbox Code Playgroud)


Kar*_*lak 5

解决这个问题的正确方法是设置路由注释,如下所示:

/**
* @Route("/{reactRouting}", name="index", priority="-1", defaults={"reactRouting": null}, requirements={"reactRouting"=".+"})
*/
public function index(): Response
{
    return $this->render('index.html.twig');
}
Run Code Online (Sandbox Code Playgroud)

The magic here is to use "priority=-1" parameter. This allows symfony to use routing to find proper path for API methods, and only if no route was found, React router starts, looking for its route. If there is no React route as well you should handle it using some "NotFound" component in React app.

With this approach you can still use SPA, but also define all logic and use API in the same app.