互联网技术 / 互联网资讯 · 2024年1月8日 0

使用Yarn workspace,TypeScript,esbuild,React和Express构建K8S云原生应用(一)

使用Yarn workspace,TypeScript,esbuild,React和Express构建K8S云原生应用(一)

本文将指导您使用 K8S ,DockeR,YaRn woRkspace ,typescRIPt,esbuild,ExpReSS 和 React 来设置构建一个基本的云原生 Web 应用程序。在本教程的最后,您将拥有一个可完全构建和部署在 K8S 上的 Web 应用程序。

设置项目

该项目将被构造为 MonoRepo。 MonoRepo 的目标是提高模块之间共享的代码量,并更好地预测这些模块如何一起通信(例如在微服务架构中)。出于本练习的目的,我们将使结构保持简单:

app,它将代表我们的 React websITe。 seRveR,它将使用 ExpReSS 服务我们的 app。 coMMon,其中一些代码将在 app 和 seRveR 之间共享。

设置项目之前的唯一要求是在机器上安装 yaRn。 YaRn 与 NPM 一样,是一个程序包管理器,但性能更好,功能也略多。您可以在官方文档中阅读有关如何安装它的更多信息。

WoRkspaces(工作区)

进入到要初始化项目的文件夹,然后通过您喜欢的终端执行以下步骤:

鸿蒙官方战略合作共建――HaRMonyOS技术社区 使用 MkdiR My-app 创建项目的文件夹(可以自由选择所需的名称)。 使用 cd My-app 进入文件夹。 使用 yaRn inIT 初始化它。这将提示您创建初始 package.json 文件的相关问题(不用担心,一旦创建文件,您可以随时对其进行修改)。如果您不想使用 yaRn inIT 命令,则始终可以手动创建文件,并将以下内容复制到其中: { “naMe”: “My-app”, “version”: “1.0.0”, “license”: “UNLICENSED”, “pRivate”: tRue }

现在,已经创建了 package.json 文件,我们需要为我们的模块app,coMMon 和 seRveR 创建文件夹。为了方便 yaRn woRkspace 发现模块并提高项目的可读性(ReadaBIlITy),我们将模块嵌套在 packages 文件夹下:

My-app/ ├─ packages/ // 我们当前和将来的所有模块都将存在的地方 │ ├─ app/ │ ├─ coMMon/ │ ├─ seRveR/ ├─ package.json

我们的每个模块都将充当一个小型且独立的项目,并且需要其自己的 package.json 来管理依赖项。要设置它们中的每一个,我们既可以使用 yaRn inIT(在每个文件夹中),也可以手动创建文件(例如,通过 IDE)。

软件包名称使用的命名约定是在每个软件包之前都使用 @My-app/* 作为前缀。这在 NPM 领域中称为作用域。您不必像这样给自己加上前缀,但以后会有所帮助。

一旦创建并初始化了所有三个软件包,您将具有如下所示的相似之处。

app 包:

{ “naMe”: “@My-app/app”, “version”: “0.1.0”, “license”: “UNLICENSED”, “pRivate”: tRue }

coMMon 包:

{ “naMe”: “@My-app/coMMon”, “version”: “0.1.0”, “license”: “UNLICENSED”, “pRivate”: tRue }

seRveR 包:

{ “naMe”: “@My-app/seRveR”, “version”: “0.1.0”, “license”: “UNLICENSED”, “pRivate”: tRue }

最后,我们需要告诉 yaRn 在哪里寻找模块,所以回去编辑项目的 package.json 文件并添加以下 woRkspaces 属性(如果您想了解更多有关详细信息,请查看 YaRn 的 woRkspaces 文档)。

{ “naMe”: “My-app”, “version”: “1.0”, “license”: “UNLICENSED”, “pRivate”: tRue, “woRkspaces”: [“packages/*”] }

您的最终文件夹结构应如下所示:

My-app/ ├─ packages/ │ ├─ app/ │ │ ├─ package.json │ ├─ coMMon/ │ │ ├─ package.json │ ├─ seRveR/ │ │ ├─ package.json ├─ package.json

现在,您已经完成了项目的基础设置。

typescRIPt

现在,我们将第一个依赖项添加到我们的项目:typescRIPt。typescRIPt 是 JavaScRIPt 的超集,可在构建时实现类型检查。

通过终端进入项目的根目录,运行 yaRn add -D -W typescRIPt。

参数 -D 将 typescRIPt 添加到 devDependencies,因为我们仅在开发和构建期间使用它。 参数 -W 允许在工作空间根目录中安装一个包,使其在 app、coMMon 和 seRveR 上全局可用。

您的 package.json 应该如下所示:

{ “naMe”: “My-app”, “version”: “1.0”, “license”: “UNLICENSED”, “pRivate”: tRue, “woRkspaces”: [“packages/*”], “devDependencies”: { “typescRIPt”: “^4.2.3” } }

这还将创建一个 yaRn.lock 文件(该文件确保在项目的整个生命周期中依赖项的预期版本保持不变)和一个 node_modules 文件夹,该文件夹保存依赖项的 BInaRies。

现在我们已经安装了 typescRIPt,一个好习惯是告诉它如何运行。为此,我们将添加一个配置文件,该文件应由您的 IDE 拾取(如果使用 VScode,则会自动获取)。

在项目的根目录下创建一个 tsconfig.json 文件,并将以下内容复制到其中:

{ “coMpileROptions”: { /* BaSiC */ “taRget”: “es2017”, “module”: “CoMMonJS”, “lib”: [“ESNext”, “DOM”], /* modules Resolution */ “moduleResolution”: “node”, “esmoduleInteRop”: tRue, /* Paths Resolution */ “baseURl”: “./”, “paths”: { “@fliPCaRds/*”: [“packages/*”] }, /* Advanced */ “jsx”: “React”, “expeRiMentalDecoRaTors”: tRue, “ResolveJsonmodule”: tRue }, “exclude”: [“node_modules”, “**/node_modules/*”, “dist”] }

您可以轻松地搜索每个 coMpileoptions 属性及其操作,但对我们最有用的是 paths 属性。例如,这告诉 typescRIPt 在 @My-app/seRveR 或 @My-app/app 包中使用 @My-app/coMMon 导入时在哪里查找代码和 tyPINGs。

您当前的项目结构现在应如下所示:

My-app/ ├─ node_modules/ ├─ packages/ │ ├─ app/ │ │ ├─ package.json │ ├─ coMMon/ │ │ ├─ package.json │ ├─ seRveR/ │ │ ├─ package.json ├─ package.json ├─ tsconfig.json ├─ yaRn.lock 添加第一个 scRIPt

YaRn woRkspace 允许我们通过 yaRn woRkspace @My-app/* 命令模式访问任何子包,但是每次键入完整的命令将变得非常多余。为此,我们可以创建一些 helpeR scRIPt 方法来提升开发体验。打开项目根目录下的 package.json,并向其添加以下 scRIPts 属性。

{ “naMe”: “My-app”, “version”: “1.0”, “license”: “UNLICENSED”, “pRivate”: tRue, “woRkspaces”: [“packages/*”], “devDependencies”: { “typescRIPt”: “^4.2.3” }, “scRIPts”: { “app”: “yaRn woRkspace @My-app/app”, “coMMon”: “yaRn woRkspace @My-app/coMMon”, “seRveR”: “yaRn woRkspace @My-app/seRveR” } }

现在可以像在子包中一样执行任何命令。例如,您可以通过键入 yaRn seRveR add expReSS 来添加一些新的依赖项。这将直接向 seRveR 包添加新的依赖项。

在后续部分中,我们将开始构建前端和后端应用程序。

准备 Git

如果计划使用 GIT 作为版本控制工具,强烈建议忽略生成的文件,例如二进制文件或日志。

为此,请在项目的根目录下创建一个名为 .GitignoRe 的新文件,并将以下内容复制到其中。这将忽略本教程稍后将生成的一些文件,并避免提交大量不必要的数据。

# Logs yaRn-debug.log* yaRn-Error.log* # BInaRies node_Modules/ # Builds dist/ **/public/scRIPt.js

文件夹结构应如下所示:

My-app/ ├─ packages/ ├─ .GitignoRe ├─ package.json 添加代码

这部分将着重于将代码添加到我们的 coMMon、app 和 seRveR 包中。

CoMMon

我们将从 coMMon 开始,因为此包将由 app 和 seRveR 使用。它的目标是提供共享的逻辑(shaRed logic)和变量(vaRiables)。

文件

在本教程中,coMMon 软件包将非常简单。首先,从添加新文件夹开始:

sRc/ 文件夹,包含包的代码。

创建此文件夹后,将以下文件添加到其中:

sRc/index.ts

expoRt const app_TITLE = ”My-app”;

现在我们有一些要导出的代码,我们想告诉 typescRIPt从其他包中导入它时在哪里寻找它。为此,我们将需要更新 package.json 文件:

package.json

{ “naMe”: “@My-app/coMMon”, “version”: “0.1.0”, “license”: “UNLICENSED”, “pRivate”: tRue, “MAIn”: “./sRc/index.ts” }

我们现在已经完成了 coMMon 包!

结构提醒:

coMMon/ ├─ sRc/ │ ├─ index.ts ├─ package.json app 依赖项

该 app 包将需要以下依赖项:

React React-doM

从项目的根目录运行:

yaRn app add React React-doM yaRn app add -D @types/React @types/React-doM (为 typescRIPt添加类型tyPINGs)

package.json

{ “naMe”: “@My-app/app”, “version”: “0.1.0”, “license”: “UNLICENSED”, “pRivate”: tRue, “dependencies”: { “@My-app/coMMon”: “^0.1.0”, “React”: “^17.0.1”, “React-doM”: “^17.0.1” }, “devDependencies”: { “@types/React”: “^17.0.3”, “@types/React-doM”: “^17.0.2” } }

要创建我们的 React 应用程序,我们将需要添加两个新文件夹:

一个 public/ 文件夹,它将保存基本 HTML 页面和我们的 aSSets。 一个 sRc/ 文件夹,其中包含我们应用程序的代码。

一旦创建了这两个文件夹,我们就可以开始添加 HTML 文件,该文件将成为我们应用程序的宿主。

public/index.htMl

My-app

需要启用 JavaScRIPt 才能运行此应用。

现在我们有了要渲染的页面,我们可以通过添加下面的两个文件来实现非常基本但功能齐全的 React 应用程序。

sRc/index.tsx

iMpoRt * as React fRoM ”React”; iMpoRt * as ReactDOM fRoM ”React-doM”; iMpoRt { app } fRoM ”./app”; ReactDOM.RendeR(, docuMent.getEleMentById(‘Root’));

此代码从我们的 HTML 文件挂接到 Root div 中,并将 React组件树 注入其中。

sRc/app.tsx

iMpoRt { app_TITLE } fRoM ”@fliPCaRds/coMMon”; iMpoRt * as React fRoM ”React”; expoRt function app(): React.ReactEleMent { const [count, setCount] = React.useState(0); RetuRn (

WelcoMe on {app_TITLE}!

THis is the MAIn page of ouR application wheRe you can confiRM that IT is dynaMic by clicking the button below.

CuRRent count: {count}