使用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//Requiredforyarnworkspacetowork}

现在,已经创建了 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\”],/*ModulesResolution*/\”moduleResolution\”:\”node\”,\”esModuleInterop\”:true,/*PathsResolution*/\”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\”:\”yarnworkspace@my-app/app\”,\”common\”:\”yarnworkspace@my-app/common\”,\”server\”:\”yarnworkspace@my-app/server\”}}

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

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

准备 Git

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

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

#Logsyarn-debug.log*yarn-error.log*#Binariesnode_modules/#Buildsdist/**/public/script.js

文件夹结构应如下所示:

my-app/├─packages/├─.gitignore├─package.json 添加代码

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

Common

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

文件

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

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

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

src/index.ts

exportconstAPP_TITLE=\’my-app\’;

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

package.json

{\”name\”:\”@my-app/common\”,\”version\”:\”0.1.0\”,\”license\”:\”UNLICENSED\”,\”private\”:true,\”main\”:\”./src/index.ts\”//添加这一行来为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\”,//Noticethatwe\’veaddedthisimportmanually\”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

You need to enable JavaScript to run this app.

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

src/index.tsx

import*asReactfrom\’react\’;import*asReactDOMfrom\’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*asReactfrom\’react\’;exportfunctionApp():React.ReactElement{const[count,setCount]=React.useState(0);return(

Welcomeon{APP_TITLE}!

Thisisthemainpageofourapplicationwhereyoucanconfirmthatitisdynamicbyclickingthebuttonbelow.

Currentcount:{count}

);}

这个简单的 App 组件将呈现我们的应用标题和动态计数器。这将是我们的 React tree 的入口点。随意添加您想要的任何代码。

就是这样!我们已经完成了非常基本的 React 应用程序。目前它并没有太大的作用,但是我们总是可以稍后再使用它并添加更多功能。

结构提醒:

app/├─public/│├─index.html├─src/│├─App.tsx│├─index.tsx├─package.json Server

依赖项

server 软件包将需要以下依赖项:

cors express

从项目的根目录运行:

yarn server add cors express yarn server add -D @types/cors @types/express(为 TypeScript 添加类型typings)

package.json

{\”name\”:\”@my-app/server\”,\”version\”:\”0.1.0\”,\”license\”:\”UNLICENSED\”,\”private\”:true,\”dependencies\”:{\”@my-app/common\”:\”^0.1.0\”,//请注意,我们已手动添加了此导入\”cors\”:\”^2.8.5\”,\”express\”:\”^4.17.1\”},\”devDependencies\”:{\”@types/cors\”:\”^2.8.10\”,\”@types/express\”:\”^4.17.11\”}} 文件

现在我们的 React 应用程序已经准备就绪,我们需要的最后一部分是服务器来为其提供服务。首先为其创建以下文件夹:

一个 src/ 文件夹,包含我们服务器的代码。

接下来,添加 server 的主文件:

src/index.ts

import{APP_TITLE}from\’@flipcards/common\’;importcorsfrom\’cors\’;importexpressfrom\’express\’;import{join}from\’path\’;constPORT=3000;constapp=express();app.use(cors());//服务来自\”public\”文件夹的静态资源(例如:当有图像要显示时)app.use(express.static(join(__dirname,\’../../app/public\’)));//为HTML页面提供服务app.get(\’*\’,(req:any,res:any)=>{res.sendFile(join(__dirname,\’../../app/public\’,\’index.html\’));});app.listen(PORT,()=>{console.log(`${APP_TITLE}\’sserverlisteningathttp://localhost:${PORT}`);});

这是一个非常基本的 Express 应用程序,但如果除了单页应用程序之外我们没有任何其他服务,那么这就足够了。

结构提醒:

server/├─src/│├─index.ts├─package.json 构建应用

Bundlers(打包构建捆绑器)

为了将 TypeScript 代码转换为可解释的 JavaScript 代码,并将所有外部库打包到单个文件中,我们将使用打包工具。JS/TS 生态系统中有许多捆绑器,如 WebPack、Parcel 或 Rollup,但我们将选择 esbuild。与其他捆绑器相比,esbuild 自带了许多默认加载的特性(TypeScript, React),并有巨大的性能提升(快了 100 倍)。如果你有兴趣了解更多,请花时间阅读作者的常见问题解答。

这些脚本将需要以下依赖项:

esbuild 是我们的捆绑器 ts-node 是 TypeScript 的 REPL,我们将使用它来执行脚本

从项目的根目录运行:yarn add -D -W esbuild ts-node。

package.json

{\”name\”:\”my-app\”,\”version\”:\”1.0\”,\”license\”:\”UNLICENSED\”,\”private\”:true,\”workspaces\”:[\”packages/*\”],\”devDependencies\”:{\”esbuild\”:\”^0.9.6\”,\”ts-node\”:\”^9.1.1\”,\”typescript\”:\”^4.2.3\”},\”scripts\”:{\”app\”:\”yarnworkspace@my-app/app\”,\”common\”:\”yarnworkspace@my-app/common\”,\”server\”:\”yarnworkspace@my-app/server\”}} Build(编译构建)

现在,我们拥有构建应用程序所需的所有工具,因此让我们创建第一个脚本。

首先在项目的根目录下创建一个名为 scripts/ 的新文件夹。

我们的脚本将用 TypeScript 编写,并从命令行使用 ts-node 执行。尽管存在用于 esbuild 的 CLI,但是如果您要传递更复杂的参数或将多个工作流组合在一起,则可以通过 JS 或 TS 使用该库,这更加方便。

在 scripts/ 文件夹中创建一个 build.ts 文件,并在下面添加代码(我将通过注释解释代码的作用):

scripts/build.ts

import{build}from\’esbuild\’;/***在构建期间传递的通用选项。*/interfaceBuildOptions{env:\’production\’|\’development\’;}/***app包的一个构建器函数。*/exportasyncfunctionbuildApp(options:BuildOptions){const{env}=options;awaitbuild({entryPoints:[\’packages/app/src/index.tsx\’],//我们从这个入口点读React应用程序outfile:\’packages/app/public/script.js\’,//我们在public/文件夹中输出一个文件(请记住,在HTML页面中使用了\”script.js\”)define:{\’process.env.NODE_ENV\’:`\”${env}\”`,//我们需要定义构建应用程序的Node.js环境},bundle:true,minify:env===\’production\’,sourcemap:env===\’development\’,});}/***server软件包的构建器功能。*/exportasyncfunctionbuildServer(options:BuildOptions){const{env}=options;awaitbuild({entryPoints:[\’packages/server/src/index.ts\’],outfile:\’packages/server/dist/index.js\’,define:{\’process.env.NODE_ENV\’:`\”${env}\”`,},external:[\’express\’],//有些库必须标记为外部库platform:\’node\’,//为Node构建时,我们需要为其设置环境target:\’node14.15.5\’,bundle:true,minify:env===\’production\’,sourcemap:env===\’development\’,});}/***所有软件包的构建器功能。*/asyncfunctionbuildAll(){awaitPromise.all([buildApp({env:\’production\’,}),buildServer({env:\’production\’,}),]);}//当我们从终端使用ts-node运行脚本时,将执行此方法buildAll();

该代码很容易解释,但是如果您觉得遗漏了部分,可以查看 esbuild 的 API文档 以获取完整的关键字列表。

我们的构建脚本现已完成!我们需要做的最后一件事是在我们的 package.json 中添加一个新命令,以方便地运行构建操作。

{\”name\”:\”my-app\”,\”version\”:\”1.0\”,\”license\”:\”UNLICENSED\”,\”private\”:true,\”workspaces\”:[\”packages/*\”],\”devDependencies\”:{\”esbuild\”:\”^0.9.6\”,\”ts-node\”:\”^9.1.1\”,\”typescript\”:\”^4.2.3\”},\”scripts\”:{\”app\”:\”yarnworkspace@my-app/app\”,\”common\”:\”yarnworkspace@my-app/common\”,\”server\”:\”yarnworkspace@my-app/server\”,\”build\”:\”ts-node./scripts/build.ts\”//Addthislinehere}}

现在,您可以在每次对项目进行更改时从项目的根文件夹运行 yarn build 来启动构建过程(如何添加hot-reloading,稍后讨论)。

结构提醒:

my-app/├─packages/├─scripts/│├─build.ts├─package.json├─tsconfig.json Serve(提供服务)

我们的应用程序已经构建好并可以提供给全世界使用,我们只需要向 package.json 添加最后一个命令即可:

{\”name\”:\”my-app\”,\”version\”:\”1.0\”,\”license\”:\”UNLICENSED\”,\”private\”:true,\”workspaces\”:[\”packages/*\”],\”devDependencies\”:{\”esbuild\”:\”^0.9.6\”,\”ts-node\”:\”^9.1.1\”,\”typescript\”:\”^4.2.3\”},\”scripts\”:{\”app\”:\”yarnworkspace@my-app/app\”,\”common\”:\”yarnworkspace@my-app/common\”,\”server\”:\”yarnworkspace@my-app/server\”,\”build\”:\”ts-node./scripts/build.ts\”,\”serve\”:\”node./packages/server/dist/index.js\”//Addthislinehere}}

由于我们现在正在处理纯 JavaScript,因此可以使用 node 二进制文件启动服务器。因此,继续运行 yarn serve。

如果您查看控制台,您将看到服务器正在成功侦听。你也可以打开一个浏览器,导航到 http://localhost:3000 来显示你的 React 应用

给TA打赏
共{{data.count}}人
人已打赏
云计算

Twitter面向“超级用户”推出订阅服务 实现收入来源多样化

2021-6-4 9:27:03

云计算

Twitter发布首款订阅服务Twitter Blue 可撤回推文

2021-6-4 9:27:05

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索