我们知道,React是一个用来开发单页面的Javascript库。如果想让它实现多页面那样的功能,就需要用到路由。然而,路由是一个虚拟目录,并不是真实目录,页面挂载在Apache下必须进行重定向设置,否则会找到不对应的文件。
有时,我们需要将打包好的页面挂载在Apache二级目录下,这时会报错找不到对应的资源。不管你搜索google还是baidu,都只能搜出Apache二级目录的重定向方法,离实际解决问题总会差那么一点点。不同工程,网页的路由实现是千差万别的,想要正确应用,必须自己摸索对应的方法,有时也需要对前端web代码作出一些修改。
本文应用于React的Material UI框架中的Admin & Dashboard 模板(我经常使用的一个模板,有免费和收费两种版本)为实例,主要介绍了将React打包好的包含路由功能的页面部署在Apache二级目录下的方法,同时也给出了直接挂载到根目录的方法。本文属于个人经验分享,希望能为大家提供一点点参考,欢迎广大开发者进一步改进完善。
Apache下的目录如果想实现重定向,必须进行相应的配置。这里有两种方法:
.htaccess
文件里放置指令。一般适用于虚拟主机等没有修改Apache配置权限的场景。/etc/apache2/httpd.conf
,记得修改前作好备份。其指令与.htaccess
文件中的指令是等价的,但是它更加高效。因为它只在Apache启动时读取一次,而不是每次请求时都读取。这里也有几点要注意的地方:
.htaccess
文件中的配置指令作用于当前目录和所有子目录。需要注意的是,其上级目录也可能存在.htaccess
文件,而指令是按从下到上的查找顺序生效的。因此,即子目录中的指令会覆盖父目录或者主配置文件中的指令。.htaccess
文件中的某些指令不起作用,可能有多种原因,但最常见的原因是Apache配置文件里AllowOverride
没有被正确设置。一般在虚拟主机上,配置文件中主目录的AllowOverride
属性都设置为All
。有一个简单的测试方法,就是在.htaccess
文件中随便写点什么,如果服务器没有报错,那么几乎可以断定设置了 AllowOverride None
。 本文采用常用的使用.htaccess
文件的方法来处理重定向请求。
下面是Material Dashboard React的截图。这个模板主要用作后台管理,它的左侧有一个导航栏,每个栏目对应一个路由,这正是我们经常要使用的。
模板下载地址 => https://themes.material-ui.com/
类似的模板也有Vue版本,使用Vue的小伙伴们不要错过哟,这里放出地址:
一些Vue模板地址 => https://www.creative-tim.com/templates/vuejs
下载模板源码后使用编辑器打开(Atom或者Visual Studio Code),src/routes.js
就是页面左边的栏目定义了,这里是其中一项的代码片断:
{
path: "/dashboard",
name: "Dashboard",
rtlName: "لوحة القيادة",
icon: Dashboard,
component: DashboardPage,
layout: "/admin"
}
src/layouts/Admin.jsx
是代码中具体的路由定义:
const switchRoutes = (
<Switch>
{routes.map((prop, key) => {
if (prop.layout === "/admin") {
return (
<Route
path={prop.layout + prop.path}
component={prop.component}
key={key}
/>
);
}
return null;
})}
<Redirect from="/admin" to="/admin/dashboard" />
</Switch>
);
最后是程序的入口,在src/index.js
中:
ReactDOM.render(
<Router history={hist}>
<Switch>
<Route path="/admin" component={Admin} />
<Route path="/rtl" component={RTL} />
<Redirect from="/" to="/admin/dashboard" />
</Switch>
</Router>,
document.getElementById("root")
);
有兴趣的同学可以直接npm install
,然后npm start
体验一下。
存在的问题
在开发环境中进行路由切换是没有任何问题的,但是如果npm run build
进行打包后将发布版的页面挂在Apache目录下(根目录或者二级目录),就会报错找不到相应的页面(资源)。
好了,代码中涉及到路由的地方都列出来了,下面我们来解决问题。
因为我们一个网站上要挂很多页面,所以一般功能性页面我们是放在二级目录下。这里就以打包后的页面放在二级目录下为例,进行源代码的修改与Apache的配置。
src/index.js
中的相关内容BrowserRouter as Router
basename={process.env.NODE_ENV === 'production' ? '/admin' : ''}
改为
并且移除
这一句。src/routes.js
中的相关内容layout: "/admin"
改为 layout: "/"
path
中的"/"
去掉,比如 path: "/dashboard"
改成 path: "dashboard"
src/layouts/Admin.jsx
switchRoutes
中的prop.layout === "/admin"
改为prop.layout === "/"
。switchRoutes
中的
改成
package.json
修改项目根目录下的package.json
文件,将homepage
属性的值改为"."
,意即可以挂在任何目录下。
.htaccess
文件 在Apache根目录下建立二级目录admin
,然后在admin
文件夹中创建.htaccess
文件,内容如下:
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . admin/index.html [L]
保存之后记住要 chmod 644 .htaccess
修改完毕,我们可以使用npm run build
来打包,将打包好的文件全部放在Apache二级目录admin下进行测试。
Router
为BrowserRouter
,增加了basename
属性,也就是把路由基准目录设定为该二级目录。.htaccess
中指令的作用是将二级目录下所有的访问转至该目录下的index.html
。index.html
后,Router
发挥作用,进行路由转发,将basename
目录(二级目录)下path为"/"
的内容(也就是所有内容)转至Admin
处理。Admin
中,再进行路由转发,将符合src/routes.js
中定义路由转到对应页面。Admin
中未匹配到对应路由,则执行
将内容重定向到dashboard
(也就是默认显示的第一个界面),记住这里是重定向,所以url会发生变化。 警告
本文中故意忽略了src/index.js
中第35行
,也没有对此路由进行处理,请大家在实际运用中注意。
可以看到,为了能挂载到二级目录下,我们手动修改了模板中所有和route相关的代码。我们也可以根据模板设计的初衷,不修改模板代码而直接将打包后的页面挂载在Apache根目录下。此时会报错:
admin/static
目录下的内容。.htaccess
后路由解析报错,原因仍然是找不到静态资源(对应的js文件).htaccess
中再增加一行(第二行)。RewriteEngine On
Redirect /admin/static /static
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . index.html [L]
增加的那一行的含义是将所有/admin/static
目录下的内容重新定向到/static
目录下。笔者测试时出现了一个小缺陷,会报manifest.json
出错,但是不影响页面,于是就略过了。
在使用了路由功能的React工程中,可以将打包后的页面挂载在Apache根目录或者二级目录下,此时容易出现资源找不到的情况。挂载到根目录下时通过创建.htaccess
指令文件进行资源的重定向来解决。因为根目录只能挂一个页面,所以有时会挂在二级目录下,此时需要使用BrowserRouterg
来定义路由的基准目录(为二级目录),并且手动修改route相关的代码,将多余的路径去掉。
欢迎大家留言指正改进或者一起讨论。