Javascript 反应路由器URL don';刷新或手动写入时无法工作

Javascript 反应路由器URL don';刷新或手动写入时无法工作,javascript,reactjs,url,react-router,Javascript,Reactjs,Url,React Router,我正在使用React路由器,当我点击链接按钮时,它工作正常,但当我刷新网页时,它不会加载我想要的内容 例如,我在localhost/joblist中,一切都很好,因为我按了一个链接到达这里。但如果我刷新网页,我会得到: 无法获取/作业列表 默认情况下,它不是这样工作的。最初我的URL是localhost/#/和localhost/#/joblist,它们工作得非常好。但我不喜欢这种URL,所以试图删除它,我写道: Router.run(routes, Router.HistoryLocatio

我正在使用React路由器,当我点击链接按钮时,它工作正常,但当我刷新网页时,它不会加载我想要的内容

例如,我在
localhost/joblist
中,一切都很好,因为我按了一个链接到达这里。但如果我刷新网页,我会得到:

无法获取/作业列表
默认情况下,它不是这样工作的。最初我的URL是
localhost/#/
localhost/#/joblist
,它们工作得非常好。但我不喜欢这种URL,所以试图删除它,我写道:

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);
});

路由器可以用两种不同的方式调用,这取决于导航是在客户机上进行还是在服务器上进行。您已将其配置为客户端操作。关键参数是位置的第二个参数

当您使用React Router链接组件时,它会阻止浏览器导航并调用Transitiono来执行客户端导航。您使用的是HistoryLocation,因此它使用HTML5历史API通过模拟地址栏中的新URL来完成导航的假象。如果您使用的是较旧的浏览器,这将不起作用。您需要使用HashLocation组件

当您点击刷新时,您将绕过所有React和React路由器代码。服务器获取对
/joblist
的请求,它必须返回一些内容。在服务器上,您需要将请求的路径传递给
run
方法,以便它呈现正确的视图。您可以使用相同的路线图,但可能需要对
路由器进行不同的调用。运行
。正如Charles指出的,您可以使用URL重写来处理这个问题。另一个选项是使用node.js服务器处理所有请求,并将path值作为location参数传递

例如,在express中,它可能如下所示:

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });
    });
});
请注意,请求路径正在传递到
run
。要做到这一点,您需要有一个服务器端视图引擎,您可以将呈现的HTML传递给该引擎。使用
renderToString
和在服务器上运行React时还有许多其他注意事项。在服务器上呈现页面后,当您的应用程序加载到客户端时,它将再次呈现,并根据需要更新服务器端呈现的HTML。

查看对已接受答案的评论以及此问题的一般性质(“不起作用”),我认为这可能是一个对这里涉及的问题进行一般性解释的好地方。因此,这个答案旨在作为OP特定用例的背景信息/详细说明。请接受我的回答

服务器端与客户端 关于这一点,首先要了解的是,现在有2个地方可以解释URL,而在“旧时代”中只有1个。在过去,当生活很简单的时候,一些用户发送了一个
http://example.com/about
发送到服务器,服务器检查URL的路径部分,确定用户正在请求关于页面,然后将该页面发回

使用React路由器提供的客户端路由,事情就不那么简单了。首先,客户端还没有加载任何JS代码。因此,第一个请求总是发送到服务器。然后,它将返回一个页面,其中包含加载React和React Router等所需的脚本标记。只有当这些脚本已加载时,阶段2才会启动。在第2阶段,例如,当用户单击“关于我们”导航链接时,URL仅在本地更改为
http://example.com/about
(由提供),但未向服务器发出请求。相反,React Router在客户端执行其操作,确定要渲染的React视图,并对其进行渲染。假设您的about页面不需要进行任何REST调用,那么它已经完成了。您已从家中转到About Us,但未触发任何服务器请求

因此,基本上,当您单击一个链接时,会运行一些Javascript来操纵地址栏中的URL,而不会导致页面刷新,从而导致React Router在客户端执行页面转换

现在考虑如果你复制粘贴地址栏中的URL并把它发给朋友,会发生什么。您的朋友尚未加载您的网站。换句话说,她仍处于第一阶段。还没有路由器在她的机器上运行。因此,她的浏览器将向
发出服务器请求http://example.com/about

这就是你的麻烦开始的地方。到目前为止,您只需在服务器的webroot上放置一个静态HTML即可。但是当从服务器请求时,所有其他URL都会出现
404
错误。这些URL在客户端可以正常工作,因为路由器正在为您执行路由,但在服务器端它们会失败,除非您让服务器理解它们

结合服务器端和客户端路由 如果您想要
http://example.com/about
URL要在服务器端和客户端上工作,您需要在服务器端和客户端上为其设置路由。有道理吧

这就是你选择的起点。解决方案包括完全绕过问题,通过返回引导HTML的“一网打尽”路径,到完全同构的方法,即服务器和客户端运行相同的JS代码

完全绕过这个问题:哈希历史记录 使用“而不是”,您的“关于”页面的URL将如下所示:
http://example.com/#/about
哈希(
#
)符号后的部分不会发送到服务器。因此服务器只能看到
http://example.com/
并按预期发送索引页。React路由器将拾取
#/about
部分并显示正确的页面

缺点

  • “丑陋”URL
  • 服务器端渲染
    var app = express();
    
    app.get('*', function (req, res) { // This wildcard method handles all requests
    
        Router.run(routes, req.path, function (Handler, state) {
            var element = React.createElement(Handler);
            var html = React.renderToString(element);
            res.render('main', { content: html });
        });
    });
    
    <script>
      System.config({ baseURL: '/' });
    </script>
    
    devServer: {
       historyApiFallback: true,
       contentBase: './',
       hot: true
    },
    
    <Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />
    
    <IfModule mod_rewrite.c>
      RewriteEngine On
      RewriteBase /
      RewriteRule ^index\.html$ - [L]
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteCond %{REQUEST_FILENAME} !-l
      RewriteRule . /index.html [L]
    </IfModule>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
    
        <!-- WELCOME FILE LIST -->
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
    
        <!-- ERROR PAGES DEFINITION -->
        <error-page>
            <error-code>404</error-code>
            <location>/index.jsp</location>
        </error-page>
    
    </web-app>
    
    <Router history={hashHistory} >
       <Route path="/home" component={Home} />
       <Route path="/aboutus" component={AboutUs} />
    </Router>
    
    sudo npm run build
    
    <VirtualHost *:80>
        ServerAdmin admin@0.0.0.0
        ServerName 0.0.0.0
        ServerAlias 0.0.0.0
        DocumentRoot /var/www/html/
    
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
        <Directory "/var/www/html/">
                Options Indexes FollowSymLinks
                AllowOverride all
                Require all granted
        </Directory>
    </VirtualHost>
    
    cd /etc/apache2/sites-available
    sudo a2ensite sample.conf
    
       RewriteEngine On
       RewriteBase /
       RewriteCond %{REQUEST_FILENAME} !-f
       RewriteCond %{REQUEST_FILENAME} !-d
       RewriteCond %{REQUEST_FILENAME} !-l
       RewriteRule ^.*$ / [L,QSA]
    
    <Router history={hashHistory} >
    
    import { HashRouter } from 'react-router-dom'
    
    <HashRouter>
      <App/>
    </HashRouter>
    
    <httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
        <remove statusCode="500" subStatusCode="100" />
        <remove statusCode="500" subStatusCode="-1" />
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />
        <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
        <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
    </httpErrors>
    
    /*  /index.html  200
    
    // app.js
    
    import { BrowserRouter as Router } from 'react-router-dom'
    
    const App = () {
      render() {
        return (
            <Router>
               // your routes here
            </Router>
        )
      }
    }
    
    // server.js
    
    app.get('/*', function(req, res) {   
      res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
        if (err) {
          res.status(500).send(err)
        }
      })
    })
    
    location / {
        try_files $uri /index.html;
    }
    
    location / {
      if (!-e $request_filename){
        rewrite ^(.*)$ /index.html break;
      }
    }
    
    Options -MultiViews
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.html [QSA,L]
    
    devServer: {
        historyApiFallback: true
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.webServer>
        <httpErrors errorMode="Custom" existingResponse="Replace">
            <remove statusCode="404" subStatusCode="-1" />
            <error statusCode="404" path="/" responseMode="ExecuteURL" />
        </httpErrors>
      </system.webServer>
    </configuration>
    
    <script src="{{ URL::to('js/user/spa.js') }}"></script>
    
    Route::get('/setting-alerts', function () {
       return view('user.set-alerts');
    });
    
    Route::get('/setting-alerts/{spa?}', function () {
      return view('user.set-alerts');
    });
    
    expressApp.get('/*', (request, response) => {
        response.sendFile(path.join(__dirname, '../public/index.html'));
    });
    
    <base href="/">
    <!-- This must come before the css and javascripts -->
    
    webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback
    
    class App extends Component {
        render() {
            return (
                <Router history={browserHistory}>
                    <div>
                        <Root>
                            <Switch>
                                <Route exact path={"/"} component={Home} />    
                                <Route path={"/home"} component={Home} />
                                <Route path={"/createnewproject"} component={CreateNewProject} />
                                <Route path={"/projects"} component={Projects} />
                                <Route path="*" component={NotFoundRoute} />
                            </Switch>
                        </Root>
                    </div>
                </Router>
            )
        }
    }
    render (<App />, window.document.getElementById("app"));
    
    RewriteEngine On
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
    RewriteRule ^ - [L]
    
    RewriteRule ^ /index.html [L]  
    
    { 
      "hosting": {
        "rewrites": [{
          "source":"**",
          "destination": "/index.html"
        }]    
      }
    }
    
    module.exports = {
      entry: './app/index.js',
      output: {
           path: path.join(__dirname, '/bundle'),
           filename: 'index_bundle.js',
           publicPath: '/'
      },
    
    <IfModule mod_rewrite.c>
    
      RewriteEngine On
      RewriteBase /
      RewriteRule ^index\.html$ - [L]
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteCond %{REQUEST_FILENAME} !-l
      RewriteRule . /index.html [L]
    
    </IfModule>
    
        public class HomeController : Controller
        {
            public IActionResult Index()
            {
                var url = Request.Path + Request.QueryString;
                return App(url);
            }
    
            [Route("App")]
            public IActionResult App(string url)
            {
                return View("/wwwroot/app/build/index.html");
            }
       }
    
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
    
                routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Index" });
            });
    
    Options -MultiViews
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.html [QSA,L]
    
    "start": "webpack-dev-server --inline --content-base . --history-api-fallback"
    
    @Controller
    public class ForwardingController {
        @RequestMapping("/<any end point name>/{path:[^\\.]+}/**")
        public String forward(HttpServletRequest httpServletRequest) {
            return "forward:/";
        }
    }
    
    import {
      Router //replace Router
    } from "react-router-dom";
    
    ReactDOM.render(
        <LocaleProvider locale={enUS}>
        <Provider store={Store}>
            <Router history={history}> //replace here saying Router
                <Layout/>
            </Router>
        </Provider>
    </LocaleProvider>, document.getElementById("app"));
    registerServiceWorker();
    
    import {
      HashRouter //replaced with HashRouter
    } from "react-router-dom";
    
    ReactDOM.render(
        <LocaleProvider locale={enUS}>
        <Provider store={Store}>
            <HashRouter history={history}> //replaced with HashRouter
                <Layout/>
            </HashRouter>
        </Provider>
    </LocaleProvider>, document.getElementById("app"));
    registerServiceWorker();
    
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.webServer>
            <defaultDocument>
                <files>
                    <remove value="default.aspx" />
                    <remove value="iisstart.htm" />
                    <remove value="index.htm" />
                    <remove value="Default.asp" />
                    <remove value="Default.htm" />
                </files>
            </defaultDocument>
            <rewrite>
                <rules>
                    <rule name="React Routes" stopProcessing="true">
                        <match url=".*" />
                        <conditions logicalGrouping="MatchAll">
                            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                            <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                        </conditions>
                        <action type="Rewrite" url="/YOURVIRTUALDIRECTORYNAME/" />
                    </rule>
                </rules>
            </rewrite>
            <directoryBrowse enabled="false" />
            <httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
                <remove statusCode="500" subStatusCode="100" />
                <remove statusCode="500" subStatusCode="-1" />
                <remove statusCode="404" subStatusCode="-1" />
                <remove statusCode="403" subStatusCode="18" />
                <error statusCode="403" subStatusCode="18" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
                <error statusCode="404" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
                <error statusCode="500" prefixLanguageFilePath="" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
                <error statusCode="500" subStatusCode="100" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
            </httpErrors>
        </system.webServer>
    </configuration>
    
    {
      "name": "sicon.react.crm",
      "version": "0.1.0",
      "private": true,
      "homepage": "/YOURVIRTUALDIRECTORYNAME/",
      "dependencies": {
    ...
    
    import  {createBrowserHistory } from 'history';
    
    export default createBrowserHistory({
        //Pass the public URL as the base name for the router basename: process.env.PUBLIC_URL
    });
    
      <Router history={history} basename={process.env.PUBLIC_URL}>
    
      <base href="%PUBLIC_URL%/">