我的第一个包含 React 和 AWS 的作品集

2025-05-27

我的第一个包含 React 和 AWS 的作品集

嗨,dev.to!我创建了我的第一个作品集,并考虑记录这个过程。不过在开始之前,先声明一下:

  • 我相信要为工作选择正确的工具,使用 React 进行作品集制作可能看起来有点过度,但我决定使用它主要是因为我想做得更好。
  • 出于同样的原因,我选择使用 AWS 来部署它,而不是 Github 或 Netlifly。AWS 简直是庞然大物,我想尽可能多地学习。

呼!好吧,那我们开始吧。哦,这是作品集https://georginagrey.com

有趣的部分

在编写应用程序代码时,我学到了一些我认为值得分享的新技巧。

React 的 Context API

我的作品集是多语言的,为了实现这一点,我使用了React 的 Context,其目的是提供一种“全局”状态,让其他深度嵌套的组件可以访问它,从而避免在层层嵌套中传递props。它就是这样帮助我实现语言切换器的:

提供者

LanguageContext.js存放着文本翻译,并且创建和导出了上下文。

//LanguageContext.js
export const languages = {
    en: {...
    },
    es: {...
    }
}

export const LanguageContext = React.createContext({
    langText: languages.en,
    toggleLanguage: () => { }
});

Enter fullscreen mode Exit fullscreen mode

App组件是最外层的组件,toggleLanguage函数实际上就是在这里实现的。LanguageContext.Provider组件包装了所有需要使用“全局”状态的组件

共享访问状态的函数时要小心,这些函数需要明确绑定状态,方法是使用super(props)关键字或bind(this)方法,否则深层嵌套的组件执行此函数将引发错误。

// App.js
...
import { LanguageContext, languages } from './LanguageContext';
...

constructor(props) {
    super(props);

    this.state = {
        language: 'en',
        langText: languages.en,
        toggleLanguage: this.toggleLanguage
    }
}

toggleLanguage = () => {...
}

render() {
    return (
        <div id="app" className={app}>
            <LanguageContext.Provider value={this.state}>
                <Menu />
                <Main />
                <Footer />
            </LanguageContext.Provider>
        </div>
    )
}

Enter fullscreen mode Exit fullscreen mode

消费者

由于LanguageContext.Consumer组件的存在,LanguagePicker组件嵌套深度大约为 3 层,这就是可以访问状态的方式。

// LanguagePicker.js
const LanguagePicker = () => (
  <LanguageContext.Consumer>
    {({ toggleLanguage, language }) => (
      <div className={main} onClick={() => toggleLanguage()}>
        ...
        <span>{language}</span>
      </div>
    )}
  </LanguageContext.Consumer>
)
Enter fullscreen mode Exit fullscreen mode

这也可以用 Redux 来实现,但我没用它做其他事情。不过, Context API不应该轻易使用,所以请记住这一点。



交叉口观察器 API

如果某个元素在视口内可见时需要触发某个行为,它就非常有用。我用它来触发一些动画,但最有意义的用例是提升网站的加载时间首次内容绘制以及降低带宽占用

<img>即使组件尚未安装,该标签也会立即渲染其源中的任何内容,因此用户可能会下载可能永远都看不到的图像。首次内容绘制的速度也会有所降低,这也是意料之中的。

这里的技巧是使用占位符,获取原始图像并将其缩小到大约 10x10 像素的比例。只有当IntersectionObserver启动时,我们才会获取原始图像。以下是实现代码片段:

// Proyects.js
componentDidMount() {
    this.observe();
}

observe() {
    var options = {
        threshold: [0.1]
    }

    const observer = new IntersectionObserver(entries => {
        entries.forEach(entry => {
            if (entry.intersectionRatio > 0) {
                const image = entry.target;
                const src = image.dataset.src;

                this.fetchImage(src).then(() => {
                    image.src = src;
                });
            }
        });
    }, options);

    const images = document.querySelectorAll('img');
    images.forEach(i => observer.observe(i));
}
Enter fullscreen mode Exit fullscreen mode

专业提示:我使用Cloudinary来代替自己缩小图像,当url: 中提供c_scale时,您可以动态转换图像
https://res.cloudinary.com/georginagrey/image/upload/c_scale,h_12,w_12/v1532709273/portfolio/portfolio.jpg,如果您去掉那部分,您将获得原始图像。

注意: IntersectionObserver 并非在所有浏览器中都完全受支持,因此您可能需要使用 pollyfill 或 fallback。



用户界面

这是我最弱的一点,直到最近我才稍微了解了一下 CSS3,或者说我之前一直以为是这样,直到我开始用纯 CSS 给组件添加样式时,各种“陷阱”都让我措手不及。我不得不重写了好几遍,直到我决定使用emotion。虽然css-in-js引起了一些争议,但我还是决定尝试一下,而且我很喜欢它,我再也不用担心在处理不同的组件时会覆盖规则了。

布局非常简单,我采用了移动优先的方法,并且只使用了flexbox



堆栈

简而言之,这是一个 React 静态网站,托管在由 CloudFront 和 Route53 提供服务的 S3 存储桶上。

我怎么会变成这样?!

在编写了主要的 React 组件并设计了其中大部分的样式之后,我偶然发现了 Google 的Lighthouse审计工具,我下载了 Chrome 扩展程序并生成了一份报告(本地),几秒钟内我就得到了结果和一系列优化机会,例如,通过在服务器中启用“文本压缩”,在我的情况下,应用程序的加载速度应该会提高大约 3 秒。

我不知道那是什么意思,所以在谷歌搜索了一会儿之后,我偶然发现了Cloudfront,最重要的是你可以免费申请 SSL 证书

设置一切其实并不像听起来那么难,这里有一个非常实用的指南。您将获得什么?托管、更强大的性能、更快的交付速度和安全的 HTTPS。

它是免费的吗?

S3 和 CloudFront本身并不是免费的,而是按需付费服务,因此对于低流量网站,在 1 年免费套餐到期后,我们讨论的是每月支付几美分的费用(如果有的话)。

Route53 是 DNS 提供商,每个托管区域每月固定收费 0.51 美元,所以每年只需 6 美元。在本例中,我已经在 GoDaddy 注册了一个域名,为了使用 Route53,我直接获取了 Route53 提供的 DNS 名称,并将其保存在GoDaddy 的“管理名称服务器”表单中。

缓存和使 CloudFront 失效

正如预期的那样,每次请求进入 CloudFront 时,它都会提供缓存的内容,而不是每次都去 S3 存储桶查找文件,内容保持缓存的时间取决于配置的默认 TTL时间范围,请在此处阅读有关它的更多信息

由于我仍在维护网站,我将默认 TTL 设置为
3600 秒(1 小时),并cache-control:max-age=0在源 S3 存储桶的元数据中添加了一个标头 。但我很快会恢复该设置,并使用Invalidation来代替,它会强制刷新缓存,而无需等待缓存过期。这样做实际上也更省钱。

编辑:
我收到月结单了!以下是 AWS Princing 的设置示例:
AWS 账单

CloudFront为美洲/欧洲套餐处理了超过 2300 个请求。此外,DNS路由和存储费用总计0.62 美元。由于预计流量不会激增,因此价格不会更高。



就是这样!...我想🤔

这不是我第一次处理 AWS,但这是我第一次编写网站前端代码,因此非常感谢任何评论。

感谢您的光临👋

编辑 07/30:添加了有关 IntersectionObserver 的警告。
编辑 08/03:添加了 AWS 账单声明。
文章来源:https://dev.to/georginagrey/my-first-portfolio-with-react-and-aws-2g73
PREV
适合初学者的 JavaScript 项目
NEXT
使用 SVG 创建生成式社交图像!最终成果!但是……它到底是用来做什么的?起飞吧🚀