[译]使用Kobweb进行静态站点生成和部署

Kobweb是一个基于Compose HTML构建的框架,而Compose HTML本身是JetBrains提供的一个响应式Web UI框架,它允许你使用强大的API用Kotlin创建Web应用。

Note

你也可以从这篇早期文章中了解更多关于Kobweb的信息。

在这篇文章中,我们将讨论如何使用Kobweb将你的Compose HTML项目导出为可被各种静态网站托管提供商使用的格式。这意味着你可以为你的Kotlin网站获得快速且便宜(通常是免费!)的托管服务。

背景

这些背景部分是为那些刚接触前端开发世界和/或对Kobweb感到好奇的人提供的。

不过,如果你已经准备好动手实践,可以随时跳过这些内容▼

Compose HTML / SPAs

Compose HTML是构建单页应用程序(SPA)的绝佳工具。

它生成一个单一的、最小化的index.html文件,再加上一些JavaScript代码,可以在运行时重新排列你的应用。

一旦页面被浏览器加载并且其脚本开始运行,它会持续地修改页面的DOM,使用户产生一种错觉,仿佛他们在点击内容时实际上是在多个页面之间导航。


让我们讨论一个具体的例子。

假设你已经导航到了一个Compose HTML站点,比如https://mysite.com

接下来,你点击主页上的一个链接,该链接带你到站点上的另一个页面,如https://mysite.dev/blog/about-me。该站点实际上会拦截导航请求并阻止浏览器处理它。

此时,URL路径被解析。基于结果(在这种情况下,值为"/blog/about-me"),站点将动态选择开始渲染与该路径相关联的新页面(可能是mysite.pages.blog.AboutMePage())。

换句话说,你项目的核心本质上是一个作用于字符串值的巨大switch语句。你可以想象类似以下的伪代码:

// Inside your main `renderComposable`
val path = getPath() // Path updated when browser URL changes
when (path) {
    "/" -> mysite.pages.HomePage()
    "/blog/about-me" -> mysite.pages.blog.AboutMePage()
    "/blog/kobweb-tutorial" -> mysite.pages.blog.KobwebTutorialPage()
    // ... etc. ...
}

现在,假设我的用例是我想直接访问https://mysite.dev/blog/about-me。也许我的朋友在社交媒体帖子上发给我这个链接。

使用Compose HTML,我真正想做的是访问https://mysite.dev并允许它拦截URL值并用新内容重新渲染页面。

如果你希望你的站点由静态网站托管提供商提供服务,你应该知道它们非常简单。它们只是盲目地提供静态文件。

所以如果用户向静态网站托管提供商请求路径/blog/about-me,那么一个名为blog/about-me.html的文件最好存在,否则该用户将收到404错误。

Note

一些静态网站托管提供商实际上允许配置规则以允许重定向到回退页面,但有时这是一种黑客手段(比如创建一个假的404.html页面),而许多提供商根本不允许。在本文的其余部分,我们将回避这种细微差别,因为下面讨论的方法应该在所有静态托管提供商上都能通用。

Kobweb来救援

与Compose HTML不同,Kobweb可以处理这个问题,因为它位于更高的层次。它知道你网站上的所有页面(因为它是为你生成路由逻辑的工具)。

Kobweb体验的一个主要部分是其CLI二进制文件。在这种情况下,相关的命令是:

$ kobweb export --layout static

当你要求Kobweb导出你的站点时,它将启动一个本地Kobweb服务器,依次访问每个页面,并将其状态保存到html文件中。通过这种方式,Kobweb可以将你的动态Compose HTML页面转换为静态快照。

如何部署静态网站

在本文中,我们将讨论两个选项,针对两个非常流行(更重要地,免费!)的提供商:NetlifyGitHub Pages

注意: 我与Netlify或GitHub没有任何关联或赞助关系。它们在这里的包含是因为它们是我Discord服务器中的用户提到他们熟悉的服务。

我们将从两种方法共同的步骤开始。

共同步骤

要求

  • git
  • GitHub账户/熟悉GitHub
  • kobweb二进制文件(安装说明

创建项目

如果你已经有一个项目,可以跳过这一步。

否则,运行以下命令,这样你就有一些具体的东西可以在本文的其余部分使用:

$ kobweb create app
# Kobweb会问一堆问题,但默认值应该没问题
$ cd app

上述步骤应该已经提供了用git初始化你的项目的选项,但如果你告诉它不要这样做或者由于某种原因没有成功,你可以手动初始化:

$ git init -b main
$ git add . && git commit -m "Initial commit"

创建新的GitHub仓库

按照官方说明创建新的GitHub仓库。 你可以选择任何你想要的名称。我为Netlify使用了kobweb-netlify-demo,为GitHub Pages使用了kobweb-ghp-demo

当有机会用README.gitignore填充这个仓库时,不要这样做!因为Kobweb已经为你创建了它们。

完成后,将你的本地项目与GitHub仓库同步:

# REMOTE_URL看起来像
# https://github.com/<user>/<repo>.git
$ git remote add origin <REMOTE_URL>
$ git push -u origin main

Netlify

注意: 你应该先完成共同步骤▲。如果你想使用GitHub Pages,跳到该部分▼

Netlify正成为希望创建能够快速提供服务的静态网站的开发者的流行解决方案。它们检测你的GitHub仓库的变化并在几秒钟内发布你的站点。

注册Netlify账户

它是免费的!在这里注册

将Netlify与你的仓库集成

  • 转到你的Netlify页面上的仪表板
  • 点击Add new site按钮
  • 选择Import an Existing Project
  • 选择GitHub作为你的git提供商
  • 按照任何授权步骤告诉Netlify关于你的新Kobweb仓库的信息
  • 从列表中选择你的仓库

最终,你将到达一个页面,要求你提供构建设置。除了publish directory字段外,所有内容都留空,你可以将其设置为site/.kobweb/site

Netlify构建设置

在gitignore中允许.kobweb/site

默认情况下,Kobweb设置为不将导出的站点检入源代码控制。

然而,为了简化Netlify的工作流程,我们将直接将导出的站点提交到我们的仓库中。

打开项目的site/文件夹中的.gitignore文件,并在底部添加行!.kobweb/site

...

# Kobweb ignores
.kobweb/*
!.kobweb/conf.yaml
!.kobweb/site

导出你的站点

# 在kobweb-netlify-demo/site/...
$ kobweb export --layout static

这将运行一段时间。完成后,运行

$ git status

以验证新文件现在已准备好添加。

如果没有,请仔细检查上一步中的.gitignore更改,并确保文件实际上已写入你的.kobweb/site文件夹。

推送你的站点

$ git add . && git commit -m "Exported site"
$ git push

Netlify:完成!

如果一切顺利,你应该有一个已部署或正在部署的页面!一旦Netlify知道推送的更改,只需几秒钟。

转到你的Netlify仪表板。你应该看到一个类似于我这里的条目:

Netlify仪表板

点击它,你应该被带到一个包含你的URL的页面:

Netlify概览

如果你点击链接,你应该看到一个看起来像这样的站点。

如果是这样,恭喜!你已经完成了。🎉

如果你仍然遇到问题,可以随时将你的项目与我的项目进行比较。

GitHub Pages

注意: 你应该先完成共同步骤▲。如果你想使用Netlify,返回到该部分▲

配置GitHub Pages有几个选项,讨论所有这些选项超出了本文的范围。相反,我们将采用一种现代方法——使用GitHub Actions在代码签入后自动部署新站点。

GitHub仓库设置

  • 转到GitHub上的仓库项目并点击Settings选项卡
  • 在侧边栏的Code and automation部分,点击Pages
  • Source下拉菜单设置为GitHub Actions

GitHub Pages源

You can ignore the rest of the page, where it recommends using a suggested workflow. We'll be creating our own in the next section.

GitHub Actions工作流

GitHub Actions是GitHub的自动化工作方法,通常用于持续集成。工作流是一个脚本,它定义了一个或多个相关的作业,这些作业会响应某些事件一起运行。

我们将创建一个工作流,用于导出你的站点并将结果部署到GitHub Pages。

在你项目的.github/workflows文件夹中(如果不存在则创建它),创建这个YAML文件(我将其命名为export-and-deploy-site.yml,但名称并不重要):

# export-and-deploy-site.yml

name: Deploy Kobweb site to Pages

on:
  push:
    branches:
      - main

  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow one concurrent deployment
concurrency:
  group: "pages"
  cancel-in-progress: true

jobs:
  export:
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash

    env:
      KOBWEB_CLI_VERSION: 0.9.18

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Java
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 17

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4

      - name: Query Browser Cache ID
        id: browser-cache-id
        run: echo "value=$(./gradlew -q :site:kobwebBrowserCacheId)" >> $GITHUB_OUTPUT

      - name: Cache Browser Dependencies
        uses: actions/cache@v4
        id: playwright-cache
        with:
          path: ~/.cache/ms-playwright
          key: ${{ runner.os }}-playwright-${{ steps.browser-cache-id.outputs.value }}

      - name: Fetch kobweb
        uses: robinraju/[email protected]
        with:
          repository: "varabyte/kobweb-cli"
          tag: "v${{ env.KOBWEB_CLI_VERSION }}"
          fileName: "kobweb-${{ env.KOBWEB_CLI_VERSION }}.zip"
          tarBall: false
          zipBall: false

      - name: Unzip kobweb
        run: unzip kobweb-${{ env.KOBWEB_CLI_VERSION }}.zip

      - name: Run export
        run: |
          cd site
          ../kobweb-${{ env.KOBWEB_CLI_VERSION }}/bin/kobweb export --notty --layout static

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./site/.kobweb/site

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: export
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
Important

请确认上面on: push: branches:部分中的主分支名称。现在,main是标准选择,但你自己的项目可能使用master或者其他自定义名称。

上述工作流中有很多内容,但关键点是:

  • 它获取你的代码。
  • 它初始化Java和Gradle。
  • 它从其自己的仓库手动下载Kobweb CLI二进制文件。
  • 它导出你的站点(与你在本地运行kobweb export --layout static的效果相同)。
  • 它以GitHub Pages可以使用的方式上传导出的站点作为构件。
Note

上述脚本使用CLI版本0.9.18,这是在写这个说明时的最新版本。如果在你阅读时有更新的版本可用,你可以更新KOBWEB_CLI_VERSION环境变量为新版本。不过,旧版本也应该可以正常工作。

Base path

一个有趣的问题是,GitHub Pages会将你的站点部署到一个子文件夹中。这个URL看起来会像 https://<user>.github.io/<project>/(例如https://bitspittle.github.io/kobweb-ghp-demo/)。

这意味着如果你在代码中使用绝对路径(即带有前导斜杠的路径,如/images/example.png),浏览器会认为你是在根域名而不是GitHub pages子文件夹中搜索。

例如,/logo.png会在https://bitspittle.github.io/logo.png处被查找,而不是它实际所在的 https://bitspittle.github.io/kobweb-ghp-demo/logo.png

为了解决这个问题,Kobweb允许用户在.kobweb/conf.yaml中配置basePath属性。如果存在这个属性,那么当Kobweb在你的代码中遇到绝对路径时,这个前缀会被添加到路径前面。

basePath设置为你的仓库名称。

site:
  basePath: "<repo-project-name>"
  # 即你为仓库选择的名称。
  # 在我的例子中,值是:"kobweb-ghp-demo"
  # 但你的名称可能不同...

server:
  # ...

推送你的站点

$ git add . && git commit -m "Set up GitHub Pages workflow"
$ git push

确认工作流正在运行

推送更改后,转到你的GitHub项目并点击Actions标签。如果你做对了所有事情,你应该看到一个新的工作流正在运行:

GitHub工作流运行

这个工作流应该需要几分钟才能运行完成。请注意,后续运行可能会更快一些,因为某些步骤的输出会被缓存。

不久之后,工作流应该完成。如果你打开它的摘要页面,你应该在deploy作业的输出中看到一个URL:

GitHub工作流摘要

GitHub Pages:完成!

如果一切顺利,你应该有一个已部署的页面!

一旦准备就绪,你就可以访问你的GitHub Pages站点,它使用的URL格式类似于 https://<user>.github.io/<project>

例如,我的站点位于 https://bitspittle.github.io/kobweb-ghp-demo/。

你在你的链接上看到类似的内容了吗?如果是的话,恭喜!你已经完成了。🥳

如果你仍然遇到问题,可以随时将你的项目与我的项目进行比较。

结论

如你所见,静态网站托管既便宜又快速,而且易于设置。除了这里列出的两个选项外,还有很多其他选择,包括其他流行的功能,如 Firebase HostingAWS

Compose HTML是一个令人惊叹的API。而静态网站托管是一项令人惊叹的服务。如果你使用Kobweb,你就不必在两者之间做出选择,而是可以同时享受两者的好处!