Kobweb介绍:基本用法、使用场景、学习资源

前言

“生命不息,折腾不止”,这句话放在个人博客上算是尤为明显😂。

这些年我都会用一些感兴趣的方式来“折腾”自己的博客。

从毕业至今在个人的博客上用过如下的平台/框架:

  • 平台
    • CSDN (2016年弃用,主要因为当时跟51CTO比体验差,现在也是)
    • 51CTO(2016年弃用,不适合写博客)
    • 博客园(没咋用,听说快倒闭了?)
    • 简书(商业化味太冲,内容审核太傻,2020年弃用)
    • 少数派(偶尔看看,不怎么写东西)
  • 框架
    • Jekyll - 基于Ruby,Jekyll 是一个基于 Ruby 的博客感知型静态网站生成器(比较古老了)
    • Hexo - 基于Node.js,快速、简洁且高效的博客框架
    • Hugo - 基于Golang,号称全球最快的网站构建框架
    • Zola - 基于Rust,单点静态站点引擎

虽然拥有十余年的开发经验,但基本上都是偏Android平台的,依托于Jetbrains 开源的 Kotlin Multiplatform 以及 Compose Multiplatform 可以将相关的经验迁移到其他平台。

废话不多说,下面开始介绍 Kobweb -- 一个Kotlin Web Framework开发框架。

Note

以下内容翻译自Kobweb作者的原文: Kobweb: A Framework Built on Compose HTML

😄没有任何添加剂,请放心食用~

Kobweb

Kobweb是基于 Compose HTML,这是由JetBrains创建的官方响应式Web UI框架(与Google密切合作,并基于Android的Jetpack Compose中引入的核心技术构建)。

而这整个网站,包括你现在正在浏览的这个页面,就是使用Kobweb构建的。

Kotlin在前端开发领域仍处于早期阶段,所以现在是探索这个领域的激动人心的时刻。在这篇文章中,我将介绍一些Kobweb的基础知识,并讨论为什么你可能(或可能不!)想要使用它。

Compose HTML是一个丰富的API,它以令人印象深刻的方式将底层的html/css概念包装成响应式API。然而,它最终只是一个基础层,让开发者在如何处理最终设计上有很多选择。

如果你想立即开始编写Kotlin代码来创建网页,那么Kobweb就是为你准备的。

创建页面

假设你最近获得了域名https://example.com。你可能想做的第一件事就是创建页面https://example.com/hello

使用Kobweb,这再简单不过了 -- 只需用@Page注解标记一个可组合方法,就完成了:

// src/jsMain/kotlin/com/example/pages/Hello.kt
package com.example.pages

@Page
@Composable
fun HelloPage() {
    Text("Hello, World!")
}

就是这样!真的!

要测试它,启动Kobweb服务器(使用 kobweb run ),在浏览器中访问 http://localhost:8080/hello ,然后享受你的Kobweb网站。

链接页面

一旦你有了至少两个页面,你可以使用 Link 在它们之间导航。页面转换将立即发生,无需从服务器获取额外信息。

换句话说,如果我们添加这个"goodbye"页面:

// src/jsMain/kotlin/com/example/pages/Goodbye.kt
package com.example.pages

@Page
@Composable
fun GoodbyePage() {
    Text("Goodbye, Cruel World!")
}

然后我们可以修改我们的"hello"页面示例,添加一个链接:

// src/jsMain/kotlin/com/example/pages/Hello.kt
package com.example.pages

@Page
@Composable
fun HelloPage() {
    Text("Hello, World!")
    Link("/goodbye", "Say goodbye...")
}

通过这种设置, https://example.com/hello 现在将显示一个链接,如果点击,将立即切换到 https://example.com/goodbye 页面。

如果你传递一个外部地址给 Link ,例如 Link("https://google.com") ,那么它将像普通的html链接一样,按预期导航到该页面。

Silk

Kobweb可以单独用于其路由功能,但它还提供了一个名为Silk的UI库,这是一个支持颜色模式(即亮色和暗色)的小部件集合,以及通过CSS样式块提供的通用主题和CSS样式支持。

我相信CSS样式块是那种一旦开始使用就不想回头的功能之一。我将在 CSS样式块▼ 中进一步演示它。

颜色模式

你是否注意到网站右上角的颜色切换按钮?不需要移动光标 -- 我将在这里创建另一个副本:${.components.widgets.button.ColorModeButton}

这个按钮封装了更改网站活动颜色模式的逻辑。试着点击它!

查询网站的颜色模式非常简单。Silk暴露了一个 ColorMode.current 属性:

@Composable
fun SomeWidget() {
    val colorMode = ColorMode.current
    val widgetColor =
        if (colorMode.isDark) Colors.Pink else Colors.Red
    /* ... code that uses widgetColor ... */
}

You can also use var instead of val with ColorMode.currentState in your code, if you want to change the color mode, not just read it:

如果你想更改颜色模式而不仅仅是读取它,你也可以在代码中使用 var 而不是 valColorMode.currentState

@Composable
fun ToggleColorButton() {
    var colorMode by ColorMode.currentState
    Button(onClick = { colorMode = colorMode.opposite })
}

画布

到目前为止,这篇文章的大部分内容都是文本。但老实说 -- 文本是静态的。多么乏味。

Web3时代的用户需要更多。这是未来!

使用Kotlin,你可以通过在画布上渲染来创建动态元素。下面的时钟改编自 这个Mozilla画布示例 ,原本是用JavaScript编写的。

它还会根据网站的颜色模式以不同方式渲染。你可以点击这个颜色模式按钮${.components.widgets.button.ColorModeButton}自己观察结果。

这是该组件的 Kotlin源代码

除其他外,Silk提供了一个有用的 Canvas2d 小部件,它使注册一些代码变得容易,这些代码将自动为你每帧调用一次。

@Composable
private fun Clock() {
  Canvas2d(300, 300, minDeltaMs = ONE_FRAME_MS_60_FPS) {
    /* This callback handles one frame of canvas rendering. */
  }
}

尽管使用简单,但画布小部件非常强大,你可以用它来创建动态效果、全屏背景,甚至游戏。

修饰符

任何接触过Jetpack Compose的人可能都熟悉 Modifier 类。它可能看起来与 @Composable 注解一样是Compose的基础。

然而,事实并非如此!Compose HTML实际上没有 Modifier 类。

相反,它使用一种方法,将所有HTML标签转换为接收所谓 AttrsScope@Composable 函数调用。

作为一个具体的例子,这个HTML文档标签:

<div
   id="example"
   style="width:50px;height:25px;background-color:black"
>

将用以下Compose HTML代码编写:

Div(attrs = {
    assert(this is AttrsScope)
    id("example")
    style {
        width(50.px)
        height(25.px)
        backgroundColor("black")
    }
})

我认为这种方法相当巧妙,但由于 AttrsScope 是一个可变类,这使得将其存储在共享变量中变得危险。此外,其API不支持链式调用。

为了解决这个问题,Silk提供了自己的 Modifier 类,它 受到 Jetpack Compose版本的启发,但并不完全相同。不过,对于编写Jetpack Compose代码的人来说,它应该看起来足够熟悉。

上面的Compose HTML AttrsScope 将由以下 Modifier 表示:

private val EXAMPLE_MODIFIER = Modifier
    .id("example")
    .width(50.px).height(25.px)
    .backgroundColor(Colors.Black)

Silk小部件直接接受修饰符:

Button(
    onClick = { /*...*/ },
    modifier = EXAMPLE_MODIFIER
)

但为了与Compose HTML元素互操作,使用 toAttrs 方法可以轻松地即时将 Modifier 转换为 AttrsScope

Div(attrs = EXAMPLE_MODIFIER.toAttrs())

使用 Modifier ,通过 then 方法可以轻松实现链式调用:

private val SIZE_MODIFIER = Modifier.size(50.px)
private val SPACING_MODIFIER = Modifier.margin(10.px).padding(20.px)

private val COMBINED_MODIFIER = SIZE_MODIFIER.then(SPACING_MODIFIER)

修饰符在整个Silk中被大量使用,这应该有助于为刚开始接触前端开发的Android和桌面Kotlin开发者提供更轻松的体验。

组织样式

样式表的不足

大多数前端项目都有一个单一的、巨大的、令人恐惧的样式表(或者更糟,几个巨大的、令人恐惧的样式表)来驱动其网站的外观和感觉。

旁注: 如果你不知道什么是样式表,它是一个CSS规则的集合,这些规则针对页面上的各种元素,使用声明式格式指定它们的样式。

例如,在开发Kobweb的某个时刻,我使用了一个待办事项应用来学习,而我花费的至少一半时间是在查看 他们的样式表 以理解他们方法的细微差别。

Compose HTML允许你在代码中定义这个样式表 ,但你仍然很容易最终得到一个单体。

CSS样式块

Kobweb引入了CSS样式块,这是一种花哨的说法,意思是你可以在使用它们的代码旁边定义更小的样式片段。

很简单 -- 只需实例化一个 CssStyle 并将结果存储到一个 val 中:

val HoverContainerStyle = CssStyle {
    base { Modifier.fontSize(32.px).padding(10.px) }
    hover {
        val highlightColor =
            if (colorMode.isDark) Colors.Pink else Colors.Red
        Modifier.backgroundColor(highlightColor)
    }
}

如果定义了 base 样式,它是特殊的,因为它总是首先被应用。如果满足条件,任何额外的声明都会叠加在基础之上。

CSS样式可以使用 toModifier 方法转换为 Modifier ,使用 toAttrs 方法转换为 AttrsScope 。这样,你可以将它们传递给Silk小部件 Compose HTML元素:

val HoverContainerStyle = CssStyle { /*...*/ }

// Then later...

// Silk widget:
Box(HoverContainerStyle.toModifier()) { /*...*/ }
    
// Compose HTML element:
Div(attrs = HoverContainerStyle.toAttrs()) { /*...*/ }

当你的元素样式位于使用它们的代码附近时,阅读代码会更容易,因为你不需要在代码和不同文件中的单体样式表之间来回跳转。

Markdown

在这篇文章的开头,我说这个网站完全是用Kotlin编写的。这可能实际上是一个技术细节。

事实上,这个网站的大部分内容都是使用markdown编写的。相关的markdown文件在编译发生之前会被转译成Kotlin。

Kobweb扩展了Markdown,增加了一些自定义支持,可以在其中嵌套代码,这就是我如何嵌入上面的颜色按钮和时钟小部件的方式。你可以使用类Kotlin的 ${...} 语法内联代码,或者使用三重大括号语法在单独的行上放置更大的小部件:

# An intro to pathfinding

Here is a demonstration of A-star pathfinding

{{{ .components.widgets.astar.Demo }}}

Play: ${.components.widgets.astar.PlayButton}
Step: ${.components.widgets.astar.StepButton}

. 开头的代码引用将自动由你项目的基础包前缀,例如上面的所有代码引用都会生成以 com.example 为前缀的最终代码(但具体使用什么取决于你的项目)。

你可以自己查看 这篇博客文章的markdown源码

最终,开箱即用的Markdown支持意味着如果你喜欢Kotlin并且正在考虑开始写博客,Kobweb可能是一个很好的解决方案。

其他方法

我们通过讨论其他方法来结束,以比较和对比Kobweb。

如果你已经被Kobweb说服了,可以随意跳过这一节,直接跳到 结论▼

Compose Multiplatform

Kotlin社区中的许多用户对多平台的前景感到兴奋,他们希望编写一次应用程序就能在所有地方运行(Android、iOS、Desktop和Web)。

Kobweb绝对不是那种解决方案。它是为那些想要创建传统网站但使用Kotlin而不是TypeScript的开发者设计的。

Kobweb(和Compose HTML)与DOM交互并让浏览器处理渲染。相比之下,Web版的Compose Multiplatform通过创建HTML画布然后不透明地将你的应用渲染到其中来工作。如果你真正想要做的是编写一个恰好也能在浏览器中工作的跨平台应用,那么Compose Multiplatform可能是适合你的解决方案。

然而,没有一种万能的解决方案,如果你正在创建一个网站,Kobweb可能仍然是正确的选择。我在 Kobweb的README 中对此做了更多说明,以防你想了解更多关于不同方法的信息,以及为什么在多平台世界中你可能仍然选择传统的DOM API。

原生Compose HTML

也许你之前被框架坑过。"是的伙计,Kobweb不错,但我还是要坚持使用 经典 的Compose HTML。"

这对我来说没问题!只是要注意,这篇文章仅仅触及了Kobweb能为你做什么的表面。这里是我们提供的更完整的功能列表,因为如果你单打独斗,你可能需要自己实现其中的一些:

  • 设置Gradle构建文件和index.html样板
  • 站点路由
  • 运行和配置服务器
  • 定义和与服务器API路由通信
  • 站点导出,用于SEO和/或静态提供你的站点页面
  • 组织你的样式表
  • 明暗色模式支持和主题
  • 一个(不断增长的)支持颜色模式的小部件集合
  • 引入 Modifier 概念,用于链式样式
  • 在html/css之上实现 BoxColumnRow
  • 大量Compose HTML中没有的CSS属性
  • 许多用于处理DOM的实用方法和类
  • Markdown支持
  • 通过API流的WebSocket支持
  • 通过Kobweb Workers的Web worker支持
  • 形状裁剪
  • 所有免费Font Awesome Material Design图标的可组合组件
  • 解析和处理查询参数(例如 /posts?userId=...&postId=...
  • 解析和处理动态路由(例如 /users/{userId}/posts/{postId}
  • 处理响应式布局(移动端vs桌面端)
  • 从头开始构建的围绕实时重载的体验

我提到这些不(仅仅)是为了炫耀,而是因为我自己对创建Kobweb的MVP所需要的东西感到惊讶。我大大低估了范围。

所以,当然,我是有偏见的,但我的观点是,如果你打算使用Compose HTML来制作网站,你可能至少要尝试一下Kobweb。

JavaScript / TypeScript

Kotlin/JS可能不适合所有人。大多数webdev社区都聚集在JavaScript/TypeScript和React等库周围。

在这种情况下,跟随大众有很多优势。而且不仅仅是因为他们有巨大的先发优势。编译时间往往要快得多,你可以通过在浏览器中直接输入命令来试验JavaScript,你将受益于大量的社区支持和资源,而且有大量有趣的项目可以学习。

我已经和许多TypeScript程序员交谈过,他们为它担保并说他们喜欢用这种语言编写代码。微软真的在为JavaScript添加安全带、头盔和全身缓冲方面做得很好(JavaScript本身仍在不断发展和改进)。

虽然我个人想鼓励更多的Kotlin开发者探索前端世界并发展社区,但如果今天有一个新程序员来找我说他们想从头开始编写一个网站,特别是希望培养能够转化为前端职业的技能,那么我现在会建议他们去学习JavaScript/TypeScript教程。

如果你喜欢我用Kobweb所做的事情,但认为此时使用JavaScript/TypeScript更适合你的项目,可以看看 Next.js 搭配 Chakra UI ,因为这两个解决方案都给了我很大的启发。

结论

自从Compose HTML宣布以来,我一直对Kotlin 在WebDev领域感到非常兴奋,我希望这篇文章至少能推动他人跨过这个门槛。

Trying Kobweb

如果Kobweb看起来是你想要尝试的东西,最简单的开始方式是在你的电脑中 安装Kobweb二进制文件

安装后,你可以运行:

$ kobweb create app
# answer a bunch of questions about your project
$ cd app/site
$ kobweb run

或者如果这篇文章只是让你对Compose HTML感到好奇,你可以从 官方教程 开始,但让Kobweb在几秒钟内为你设置好:

$ kobweb create examples/jb/counter
$ cd counter/site
$ kobweb run

最后,如果读完这篇文章后你正在考虑使用Kobweb,可以考虑加入我们的 Discord服务器 ,当我在线时我很乐意回答关于Kobweb的问题。

未来

我无法预测Kotlin 在Web应用的开发上是否会起飞,更不用说Kobweb本身了。但我真诚地希望未来会有更多的Kotlin开发者拥有跨越全栈的代码库。如果我能帮助抛出一些代码来帮助改善体验,那么我很高兴尝试过。

至少,Kobweb将永远有一个用户 -- 这个网站!

最后

Kobweb 能不能起飞我不知道,但是Kobweb能给我带来在Web端应用开发的效率提升是实打实的,希望你通过这篇文章的介绍,如果能让你对这个框架感兴趣从而动手去实践,那就太棒了~

另外关于Kobweb的相关资料,我进行了一些搜集,也在此分享给对新的技术感兴趣的朋友~

案例展示

你可以构建什么?让我们查看一些示例。

基于Kobweb的网站

有这些基于Kobweb的开源项目~

Tip

如果你感兴趣,可以去Github上看看:Kobweb Projects

SEO及多语言

本站用到了一些多语言的配置,这些经验参考自社区的分享及搜索结果

学习资源

看官方的文档和官方推荐的视频也许是一个不错的入门方式

官方社群

Kobweb的官方社群比较活跃(至少目前是),Kobweb的作者也是一个热心肠的老哥,喜欢夸人~

Kobweb版本兼容性

Note

如果有更多的资源,我将通过更新博客的方式在这里更新~