aws s3 客户端
作为最近的Bootcamp毕业生,我想将我的新React技能发挥作用,并与AWS结合使用我的旧技能。 我使用React构建了新的投资组合站点,然后我心想:“我可以在S3存储桶中托管它吗?” 第一项任务是部署到S3,这是非常简单的,有很多很好的资源,你可以用一点谷歌搜索发现,像这样的一个 ,步进通过该怎么做。
我正在用新的改进的React网站替换现有网站,因此我已经设置了存储桶并配置了Route 53等。我启动了项目并在浏览器中将其拉出,一切似乎都运行良好。 成功!
但是有一个大问题,如果您在浏览器上单击刷新按钮,则会收到404错误。 怎么会这样 我早些时候在部署到Heroku的一个学校项目中注意到了这一点,但是当时没有时间进行调查,现在我想深入了解它。
通过一些搜索,我找到了一些解决方法的解释和一些方法,但没有找到完整的答案。 关于堆栈溢出的问题, 这里有一个很好的解释,我将尝试自己做一个快速解释。
基本上,由于当您发送刷新命令时,React-router是“客户端”导航,因此浏览器正在进入,它在地址路径的末尾查找索引文件,但没有一个。 因此,在我的投资组合示例中,如果您正在查看“ / About”页面,则在传统的静态网站中,“ / About”文件夹中将有一个索引文件,浏览器将读取该索引文件并将其显示在页面上。
但是在React中,“关于”是React在页面上实例化的组件,但是该路径的末尾没有资源可供浏览器查看。 浏览器实际上总是在站点根目录下查看索引文件,而react-router正在读取地址栏并在组件之间进行切换。
经过更多的谷歌搜索之后,我在Mike Biek的这篇文章中得到了很大一部分解决方案。 此处的重要见解是,您可以使用重定向规则和条件重定向在AWS S3中进行一些“服务器端”路由。 条件重定向使您可以对错误情况做出React,因此,当单击刷新并且页面无法加载时,它将返回404错误。
使用重定向规则,我们可以在前面加上“#!” 在地址前面,浏览器不会在'#'之后读取任何内容,因此将转到您的首页'/'路线页面,但我们仍将在地址栏中提供该地址以供使用。
这是文件,直接复制自Mike Biek的帖子…
< RoutingRules >
< RoutingRule >
< Condition >
< HttpErrorCodeReturnedEquals > 404 </ HttpErrorCodeReturnedEquals >
</ Condition >
< Redirect >
< HostName > myhostname.com </ HostName >
< ReplaceKeyPrefixWith > #!/ </ ReplaceKeyPrefixWith >
</ Redirect >
</ RoutingRule >
< RoutingRule >
< Condition >
< HttpErrorCodeReturnedEquals > 403 </ HttpErrorCodeReturnedEquals >
</ Condition >
< Redirect >
< HostName > myhostname.com </ HostName >
< ReplaceKeyPrefixWith > #!/ </ ReplaceKeyPrefixWith >
</ Redirect >
</ RoutingRule >
</ RoutingRules >
要找到这些规则的去向,请在您的AWS账户上打开S3服务。 单击您用于托管项目的存储桶,然后单击属性,然后单击显示“静态网站托管。 您会看到一个标有“重定向规则(可选)”的文本框。
将代码复制并粘贴到此处,并确保您使用域的实际名称来编辑“ myhostname.com”。
添加此规则后,刷新时将不再收到404错误。 取而代之的是,您将被路由回到本地路由,无论是从“ /”路由链接到的哪个组件。 这是一个改进,但情况可能会更好。 理想情况下,我们希望路由回到相同的组件,而我的Nav不能反映我正确在哪个页面上,这是一个不同的问题。
回到Mike Biek的帖子 ,他说使用'createBrowserHistory'直接使用路径。 我试图使它起作用,但我无法这样做,经过进一步的谷歌搜索和阅读后发现,createBrowserHistory已被放弃,开发人员应直接使用location道具。 我找不到任何人为此目的使用它的示例,但我有自己的想法。
我必须对定位对象进行一些实验才能弄清楚。 基本上,只要您遵循路线,就可以创建位置对象并将其传递到道具中,实例化该位置对象后,该位置对象将在组件中可用。 在定位对象内部有一个“路径名”键和一个“哈希”键,来自地址栏的路径将是“路径名”值,但是如果地址中有一个“#”,则从“#”开始的所有内容将是“哈希”值。
我的投资组合网站的布局很简单,导航中只有两个项目,一个是“项目”,默认情况下显示,另一个是“关于”页面。
解决方案的第一个迭代是将一个函数放入我的默认组件中,该组件将读取位置对象,并且如果存在哈希变量并且其中包含“#!/ About”,则使用从react-router重定向到About的重定向组件,否则它可能会停留在Projects上。
看起来像这样…
const path = props.history.location
const checkPath = () => {
if (path.hash) {
if (path.hash.includes( '#!/About' )) {
return < Redirect push to = '/About' > </ Redirect >
}
}
}
现在,单击刷新按钮不会使事情崩溃。 如果您使用的是About,那么您将回到About,如果您使用的是Projects,那么您最终将回到Projects。 过渡将发生得足够快,以至于用户基本上看不见它。 但是我的Nav没有反映出哪个组件正常工作。
因此,在此阶段我需要做的另一件事是提升控制,以显示哪个Nav元素对顶层“ App”组件“处于活动状态”。 我创建了一个名为“ active”的状态,该状态可以保存哪个Nav元素将处于活动状态,然后创建一个函数来设置活动元素,然后将该函数传递给Nav和Project组件。
像这样
class App extends Component {
constructor (props) {
super (props)
this .state = {
active : 'Projects'
}
}
setActive = ( str ) => {
this .setState({ active : str })
}
这还不能完全正常工作,单击刷新按钮后事情不会再坏了,很好,但是前进和后退按钮仍然会破坏活动的Nav项目,并且它不是一个非常灵活的系统。 重定向仅在两个页面上运行良好,但是,如果我添加了更多页面,则需要一系列的ifs或switch语句,这感觉很笨拙。
我决定再做一次。 首先,我制作了一个helper.js文件,并将我的checkPath函数放在那里。 这样,我可以从每个组件链接到它。 我进行了设置,以便可以在任何页面上使用。
如果该位置存在“哈希”值,则该函数将哈希路径作为变量获取,并去除多余的字符,然后重定向到该项目。 同样,相同的功能将以类似的方式设置活动的导航项目。 看起来像这样…
import React from 'react'
import { Redirect } from 'react-router-dom'
export function checkPath ( props ) {
const path = props.state.history.location
if (path.hash) {
let active = path.hash.replace( /\/|!|#/g , '' )
active = '/' + active
return < Redirect push to = {active} > </ Redirect >
}
if (path.pathname) {
let active = path.pathname.replace( /\//g , '' )
props.setActive(active)
}
}
接下来,我将setActive函数传递给其Route声明中的每个组件,并在该组件内部导入setPath helper函数,然后在该组件的return语句内调用它。 您必须确保将道具传递到checkPath函数中,以便它可以使用setActive函数。
另外,我的setActive函数还需要做更多的工作,以确保正确的Nav项目在'/'路径上设置为活动状态,并防止setState调用启动无限循环。 您将在下面的代码中看到。
现在,我没有要显示服务器端的“ 404”错误页面,因此需要解决的另一个问题是,我需要设置一个默认路由或全部路由,以在错误链接或手动键入的路径上显示一条消息,而不显示错误消息。比赛。 这只需要添加一个最终的Route即可显示一个组件,如果其他Routes都不匹配,则我将其称为“ NoMatch”。
关于我的代码的另一个说明,我通过“状态”沿路线发送道具,您可以随意调用它们,但如果不隐式传递它们,则它们将无法在组件中访问。 在另一个项目上,我感到非常沮丧,这很艰难。
将所有这些放在一起看起来像…
在App.js中...
class App extends Component {
constructor (props) {
super (props)
this .state = {
active : 'Projects'
}
}
setActive = ( str ) => {
if (!str) {
str = 'Projects'
}
if ( this .state.active !== str) {
this .setState({ active : str })
}
}
render() {
return (
< div className = "App flex-container-column" >
<div className="container flex-container-column">
<Header></Header>
<Nav active={this.state.active}></Nav>
<Switch>
<Route path="/" exact render={props => <Projects setActive={this.setActive} state={props} />} />
<Route path="/About/" render={props => <Home setActive={this.setActive} state={props} />} />
<Route path="/Projects/" render={props => <Projects setActive={this.setActive} state={props} />} />
<Route path="/" render={props => <NoMatch setActive={this.setActive} state={props} />} />
</Switch>
</div>
<Footer></Footer>
</div>
);
}
}
在您的组件中,它看起来像这样...
import { checkPath } from '../helpers'
function NoMatch ( props ) {
return (
< div >
<div className="flex-container-column centered">
{checkPath(props)}
<div className='short'>
<p>Hmm, There doesn't seem to be anything here... Go back <Link to="/">Home?</Link></p>
</div>
</div>
</ div >
)
}
该解决方案效果很好,但并不完美。 如果用户单击刷新,则后退按钮将无法越过该点,因为它将无休止地转到哈希路由,然后重定向到非哈希路由。 另外,除非您设置了某种服务器端路由,否则SEO在第一页之后将找不到任何内容。
但是,对于很少的单页应用程序,项目组合和其他简单项目,这是一个很好的解决方案。
James C Rodgers是AWS认证的云实践者,并且是General Assemb.ly全栈软件工程沉浸式远程程序的最新毕业生
照片由 尤利娅Kosolapova 上 Unsplash ,由JCR编辑
aws s3 客户端