KWeb — это библиотека для создания многофункциональных интерактивных веб-приложений на чистом Kotlin, которая делает практически незаметным для программиста различие между веб-браузером и сервером.
Но зачем нужен еще один веб-фреймворк?
Создание полнофункциональных веб- приложений обычно требует навигации по ужасной экосистеме Javascript, выбора между огромным множеством инструментов, транспиляторов, минификаторов, специалистов по сопровождению состояния и т.д., Большинство из которых устареют через 6 месяцев. Затем вы сталкиваетесь с взаимодействием между клиентом и сервером, что представляет собой совершенно другой мир боли.
KWeb исправляет это.
Современные веб-сайты состоят как минимум из двух тесно связанных компонентов, один из которых работает в браузере, а другой — на сервере. Они часто написаны на разных языках программирования и должны связываться друг с другом через HTTP-соединение.
Цель Kweb — устранить это разделение сервера и браузера, чтобы архитектура вашего веб-приложения определялась решаемой проблемой, а не ограничениями современных инструментов.
KWeb рассматривает веб-браузер как довольно тупого робота, сохраняя большую часть интеллекта на стороне сервера, передавая инструкции клиенту через веб-сокет и получая ответы от клиента. Инструкции для сервера представляют собой простые фрагменты кода JavaScript, которые браузер немедленно выполняет, возвращая результат на сервер, если он запрашивается.
В этом отношении он чем-то похож на Vaadin, хотя, в отличие от Vaadin, он не включает в себя большую библиотеку виджетов, и его намного проще интегрировать в проект.
В Apache Wicket также есть аналогичный механизм, хотя Wicket в основном ориентирован на прозрачное синхронизацию состояния сервера и клиента, что в значительной степени мотивировано желанием иметь изящный переход на HTML в случае, если браузер не поддерживает JavaScript. Сегодня это требование гораздо менее актуально, чем десять лет назад.
KWeb использует преимущества новых функциональных возможностей сопрограмм в грядущем Kotlin 1.1 для обработки асинхронного ожидания ответов от клиента способом, который почти прозрачен для пользователя KWeb, без привязки потоков.
Kweb — это автономная библиотека Kotlin, которую можно легко добавить в новые или существующие проекты. Когда Kweb получает HTTP-запрос, он отвечает начальной HTML-страницей и некоторым кодом JavaScript, который подключается обратно к веб-серверу через WebSocket. Затем страница ожидает и слушает инструкции от сервера, одновременно уведомляя сервер о соответствующих событиях браузера.
Распространенное беспокойство по поводу этого подхода заключается в том, что пользовательский интерфейс может казаться вялым, если он управляется сервером. Kweb решает эту проблему, предварительно загружая в браузер инструкции, которые должны выполняться немедленно при событиях браузера без обращения к серверу.
Kweb построен на превосходной платформе Ktor , которая обрабатывает транспорт HTTP, HTTPS и WebSocket. Вам не нужно знать Ktor, чтобы использовать Kweb, но если у вас уже есть приложение Ktor, вы можете встроить Kweb как функцию .
Kweb также включает простой DSL для управления DOM браузера и плагины, позволяющие использовать популярные JavaScript-фреймворки, такие как JQuery и Material Design Light. Также на удивление легко добавить собственный плагин для любимой библиотеки или инструмента.
Особенности Kweb
-
Делает барьер между веб-сервером и веб-браузером практически невидимым для программиста.
-
Минимизирует «болтовню» между сервером и браузером и накладные расходы на рендеринг браузера.
-
Поддерживает интеграцию с некоторыми мощными библиотеками JavaScript, такими как Semantic, которая представляет собой платформу пользовательского интерфейса, предназначенную для создания тем.
-
Позволяет напрямую связывать элементы DOM в браузере с состоянием на сервере и автоматически обновлять их с помощью шаблонов наблюдателя и сопоставителя данных.
-
Легко интегрируется с Shoebox, библиотекой Kotlin для постоянного хранения данных, которая поддерживает представления и шаблон наблюдателя.
-
Легко добавить в существующий проект.
-
Мгновенно обновляет веб-браузер в ответ на изменения кода.
-
Библиотека Kweb распространяется через JitPack, новый репозиторий пакетов для проектов JVM и Android.
-
Позволяет проблеме определять вашу архитектуру, а не разделение сервера / браузера
-
Сквозной Котлин ( Почему Котлин? )
-
Поддерживайте синхронизацию веб-страницы с вашими внутренними данными в реальном времени, Kweb сделает все за вас.
-
Рендеринг HTML на стороне сервера с регидратацией
-
Эффективная предварительная загрузка инструкций, позволяющая избежать ненужного взаимодействия с сервером
-
Очень легкий, Kweb содержит менее 5000 строк кода.
Что вам понадобится
Предполагается некоторое знакомство с Kotlin , а также знакомство с Gradle . Вы также должны иметь некоторое представление о HTML.
Вы можете найти простой шаблон проекта Kweb в kwebio/kweb-template.
Под линуксом запустите:
git clone https://github.com/kwebio/kweb-template
cd kweb-template
chmod +x gradlew
./gradlew run
Если нет ошибок, введите в браузере ссылку:
И теперь можно экспериментироват с kweb, меняя содержимое файла:
kweb-template/src/main/kotlin/server.kt
package kweb.template
import kweb.*
fun main(args: Array<String>) {
Kweb(port = 16097, buildPage = {
doc.body.new {
h1().text("Replace Me!")
}
})
}
Полезные ссылки:
Website: https://kweb.io/
Source code: https://github.com/kwebio/kweb-core
API documentation: https://dokka.kweb.io/index.html
Example projects: https://github.com/kwebio/kweb-demos
Live Demo: http://demo.kweb.io:7659/
Questions/Feedback/Bugs: https://github.com/kwebio/kweb-core/issues
Добавление Kweb в ваш проект
Добавьте их в свои репозитории и зависимости в файлы build.gradle или build.gradle.kt .
*Замените LATEST_VERSION последней версией Kweb, которую вы можете найти на https://jitpack.io/#kwebio/kweb-core .
Groovy DSL
repositories {
maven { url 'https://jitpack.io' }
jcenter()
}
dependencies {
implementation 'com.github.kwebio:kweb-core:LATEST_VERSION'
// This (or another SLF4J binding) is required for Kweb to log errors
implementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.30'
}
Gradle (Kotlin)
repositories {
maven("[https://jitpack.io](https://jitpack.io)")
jcenter()
}
dependencies {
implementation("com.github.kwebio:kweb-core:LATEST_VERSION")
// This (or another SLF4J binding) is required for Kweb to log errors
implementation("org.slf4j:slf4j-simple:1.7.30")
}
Hello World!
Создайте новый файл Kotlin и введите следующее:
import kweb.*
fun main() {
Kweb(port = 16097) {
doc.body {
h1().text("Hello World!")
}
}
}
Запустите его, а затем посетите http://localhost:16097/ в своем веб-браузере, чтобы увидеть традиционное приветствие, переводящееся в следующий HTML код:
<body>
<h1>Hello World!</h1>
</body>
Этот простой пример уже показывает некоторые важные особенности Kweb:
- Установить и запустить веб-сайт очень просто, не нужно возиться с сервлетами или сторонними веб-серверами.
- Ваш код Kweb будет примерно отражать структуру генерируемого HTML-кода.
Привет, мир²
Один из способов представить Kweb — это предметно-ориентированный язык (DSL) для создания и управления DOM в удаленном веб-браузере, а также прослушивания и обработки событий DOM.
Важно отметить, что этот DSL также может делать все, что может делать Kotlin, включая такие функции, как циклы, функции, сопрограммы и классы.
Вот простой пример использования обычного цикла for в Kotlin :
import kweb.*
fun main() {
Kweb(port = 16097) {
doc.body {
ul {
for (x in 1..5) {
li().text("Hello World $x!")
}
}
}
}
}
Выводит…
<body>
<ul>
<li>Hello World 1!</li>
<li>Hello World 2!</li>
<li>Hello World 3!</li>
<li>Hello World 4!</li>
<li>Hello World 5!</li>
</ul>
</body>
Вы можете использовать функции для разбития на модули и повторного использования кода:
fun main() {
Kweb(port = 16097) {
doc.body {
ul {
for (x in 1..5) {
createMessage(x)
}
}
}
}
}
private fun ElementCreator<ULElement>.createMessage(x: Int) {
li().text("Hello World $x!")
}
Создание элементов и фрагментов DOM
Давайте создадим <button> как дочерний элемент <body>:
import kweb.*
fun main() {
Kweb(port = 16097) {
doc.body {
button().text("Click Me!")
}
}
}
Атрибуты элемента
Если вы назначите элемент кнопки значению val, вы также можете изменить его атрибуты:
val button = button()
button.text("Click Me!")
button.classes("bigbutton")
button.setAttributeRaw("autofocus", JsonPrimitive(true))
Атрибуты также могут быть указаны на карте при создании элемента:
button(mapOf("class" to "bigbutton", "autofocus" to true)).text("Click Me!")
Или удалите его:
button.delete()
Добавление дочерних элементов к существующему элементу
Синтаксис DSL позволяет очень легко создавать элементы и их дочерние элементы вместе:
ul {
li().text("One")
li().text("Two")
}
В качестве альтернативы мы можем использовать новую функцию {} в элементах для добавления дочерних элементов к уже существующему элементу:
val unorderedList : ULElement = ul()
unorderedList.new {
li().text("One")
li().text("Two")
}
Чтение из DOM
Kweb также может читать из DOM, в данном случае значение элемента <input>:
import kweb.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch
fun main() {
Kweb(port = 2395) {
doc.body {
val input: InputElement = input()
input.on.submit {
GlobalScope.launch {
val value = input.getValue().await()
println("Value: $value")
}
}
}
}
}
Обратите внимание, что input.getValue () возвращает CompletableFuture <String>. Это связано с тем, что получение данных из браузера может занять до нескольких сотен миллисекунд, и мы не хотим, чтобы приложение блокировалось, если этого можно избежать. Здесь мы используем очень мощные функции корутины Kotlin, чтобы избежать ненужных блокировок.
Примечание
Мы обсуждаем еще лучший способ чтения значений <input> в разделе Observer Pattern & State .
Поддерживаемые HTML-теги
Kweb поддерживает значительный набор тегов HTML, таких как button () , p () , a () , table () и т. Д. Вы можете найти более полный список в prelude.kt (прокрутите вниз до раздела « Функции »). Это обеспечивает хороший статически типизированный HTML DSL, полностью интегрированный с языком Kotlin.
Если тег не имеет явной поддержки в Kweb, это не проблема. Например, вот как вы можете использовать печально известный и ныне устаревший тег <blink>:
doc.body {
val blink = element("blink").text("I am annoying!")
}
Дальнейшее чтение
Класс Element предоставляет множество других полезных способов взаимодействия с элементами DOM.
Обработка событий
Прослушивание событий
Вы можете прикрепить обработчики событий к элементам DOM:
doc.body {
val label = h1()
label.text("Click Me")
label.on.click {
label.text("Clicked!")
}
}
Поддерживаются большинство, если не все типы событий JavaScript, и вы можете читать данные события, например, какая клавиша была нажата:
doc.body {
val input = input(type = text)
input.on.keypress { keypressEvent ->
println("Key Pressed: ${keypressEvent.key}")
}
}
Немедленные события
Поскольку код, отвечающий на события, выполняется на сервере, может возникнуть небольшая задержка между действием, вызывающим событие, и любыми изменениями в DOM, вызванными обработчиком событий. Это была обычная жалоба на предыдущие серверные веб-фреймворки, такие как Vaadin, что препятствовало их внедрению.
К счастью, у Kweb есть решение:
doc.body {
val label = h1()
label.text("Click Me")
label.onImmediate.click {
label.text("Clicked!")
}
}
Это идентично первому примеру прослушивателя событий, за исключением того, что on было заменено на onImmediate .
Kweb выполняет этот обработчик событий при рендеринге страницы и записывает изменения, которые он вносит в DOM. Затем он «предварительно загружает» эти инструкции в браузер, чтобы они выполнялись немедленно, когда событие происходит, без какого-либо обратного обращения к серверу.
Внимание! Из-за этого механизма предварительной загрузки обработчик событий для onImmediate должен ограничиваться простыми модификациями DOM. Kweb включает некоторые средства защиты от этого во время выполнения, но они не могут выявить все проблемы, поэтому используйте их с осторожностью.
Комбинированные обработчики событий
Распространенным шаблоном является использование обоих типов обработчиков событий в элементе DOM. Непосредственный обработчик может отключить нажатую кнопку или временно отобразить некоторую форму счетчика . Затем обычный обработчик сделает то, что ему нужно на сервере, а затем, возможно, снова включит кнопку и удалит счетчик.
Шаблон и состояние наблюдателя
Обзор
Kweb использует шаблон наблюдателя для управления состоянием.
Приложение Kweb можно рассматривать как функцию сопоставления между состоянием на сервере и DOM в веб-браузере конечного пользователя. Как только это сопоставление определено, просто измените это состояние, и изменение автоматически распространится на браузер.
Строительные блоки
KWeb класс содержит один типизированный объект, который может меняться с течением времени. Например:
val counter = KVar(0)
Здесь мы создаем счетчик типа KVar <Int>, инициализированный значением 0.
Мы также можем прочитать и изменить значение KVar:
println("Counter value ${counter.value}")
counter.value = 1
println("Counter value ${counter.value}")
counter.value++
println("Counter value ${counter.value}")
Выведет:
Counter value 0
Counter value 1
Counter value 2
KVars поддерживают мощную семантику сопоставления для создания новых KVars:
val counterDoubled = counter.map { it * 2 }
counter.value = 5
println("counter: ${counter.value}, doubled: ${counterDoubled.value}")
counter.value = 6
println("counter: ${counter.value}, doubled: ${counterDoubled.value}")
Выведет:
counter: 5, doubled: 10
counter: 6, doubled: 12
Обратите внимание, что counterDoubled обновляется автоматически.
Примечание
KVars следует использовать только для хранения значений, которые сами по себе являются неизменяемыми, таких как Int, String или класс данных Kotlin с неизменяемыми параметрами.
KWeb и DOM
Вы можете использовать KVar (или KVal) для установки текста элемента DOM:
val name = KVar("John")
li().text(name)
Приятная часть состоит в том, что при изменении значения имени текст элемента DOM обновляется автоматически. Это может помочь думать об этом как о способе «развернуть» KVar.
Многие другие функции в Elements поддерживают KVars аналогичным образом, включая innerHtml () и setAttribute () .
Привязка KVar к значению входного элемента
Для элементов <input> вы можете установить значение KVar, которое будет соединять их в двух направлениях.
Любые изменения в KVar будут отражаться в браузере в реальном времени, и точно так же любые изменения в браузере, сделанные пользователем, будут немедленно отражены в KVar, например:
Kweb(port = 2395) {
doc.body {
p().text("What is your name?")
val clickMe = input(type = text)
val nameKVar = KVar("Peter Pan")
clickMe.value = nameKVar
p().text(nameKVar.map { n -> "Hi $n!" })
}
}
Это также будет работать для элементов <option> и <textarea>, которые также имеют значения.
См. Также: ValueElement.value
Состояние рендеринга во фрагмент DOM
Но что, если вы хотите сделать больше, чем просто изменить отдельный элемент на основе KVar, что, если вы хотите изменить все дерево элементов?
Вот где появляется функция рендеринга :
val list = KVar(listOf("one", "two", "three"))
Kweb(port = 16097) {
doc.body {
render(list) { rList ->
ul {
for (item in rList) {
li().text(item)
}
}
}
}
}
Вот, если бы мы изменили список:
list.value = listOf("four", "five", "six")
Тогда соответствующая часть DOM будет мгновенно перерисована.
Простота этого механизма может скрыть его мощь, поскольку блоки render {} могут быть вложенными, поэтому можно очень избирательно выбирать, какие части DOM должны быть изменены в ответ на изменения состояния.
Примечание
Kweb будет повторно визуализировать фрагмент DOM только в том случае, если значение KVar действительно изменится, поэтому вам следует избегать «разворачивания» KVar с помощью вызова render () или .text () до того, как вам это понадобится.
Функция KVal.map {} - мощный инструмент для управления значениями KVal и KVars без их разворачивания.
Извлечение свойств класса данных
Если ваш KVar содержит класс данных, вы можете использовать Kvar.property () для создания KVar из одного из его свойств, который обновит исходный KVar при изменении:
data class User(val name : String)
val user = KVar(User("Ian"))
val name = user.property(User::name)
name.value = "John"
println(user) // Will print: KVar(User(name = "John"))
Обратимое отображение
Если вы проверите тип counterDoubled , вы заметите, что это KVal, а не KVar . Значения KVal нельзя изменять напрямую, поэтому это запрещено:
val counter = KVar(0)
val counterDoubled = counter.map { it * 2 }
counterDoubled.value = 20 // <--- This won't compile
Класс KVar имеет вторую функцию map (), которая принимает реализацию ReversibleFunction . Эта версия карты будет производить KVar, который можно изменить следующим образом:
val counterDoubled = counter.map(object : ReversibleFunction<Int, Int>("doubledCounter") {
override fun invoke(from: Int) = from * 2
override fun reverse(original: Int, change: Int) = change / 2
})
counter.value = 5
println("counter: ${counter.value}, doubled: ${counterDoubled.value}")
// output: counter: 5, doubled: 10
counterDoubled.value = 12 // <-- Couldn't do this with a KVal
println("counter: ${counter.value}, doubled: ${counterDoubled.value}")
// output: counter: 6, doubled: 12
Примечание
Обратимые сопоставления — это расширенная функция, которая вам нужна только в том случае, если вы хотите, чтобы сопоставленное значение было изменяемым KVar. В большинстве случаев простая функция KVal.map {} будет тем, что вам нужно.
URL-маршрутизация
В веб-приложении маршрутизация — это процесс использования URL-адресов для управления пользовательским интерфейсом (UI). URL-адреса являются важной особенностью каждого веб-браузера и выполняют несколько основных функций:
- Создание закладок — пользователи могут добавлять URL-адреса в закладки в своем веб-браузере, чтобы сохранять контент, к которому они хотят вернуться позже.
- Совместное использование — пользователи могут делиться контентом с другими, отправив ссылку на определенную страницу.
- Навигация — URL-адреса используются для управления функциями браузера вперед / назад.
Традиционно посещение другого URL-адреса на том же веб-сайте приводит к загрузке новой страницы с сервера, но современные веб-сайты могут изменять страницу в ответ на изменения URL-адреса без полного обновления.
Благодаря механизму маршрутизации Kweb вы получаете это автоматически.
Простой пример
import kweb.Kweb
import kweb.dom.element.new
import kweb.dom.element.creation.tags.h1
import kweb.routing.route
fun main() {
Kweb(port = 16097) {
doc.body {
route {
path("/users/{userId}") { params ->
val userId = params.getValue("userId")
h1().text(userId.map { "User id: $it" })
}
path("/lists/{listId}") { params ->
val listId = params.getValue("listId")
h1().text(listId.map { "List id: $it" })
}
}
}
}
}
Теперь, если вы посетите http://localhost:16097/users/997 , вы увидите:
<h1>User id: 997</h1>
У вас может быть столько path (), сколько вам нужно, каждый со своим собственным определением пути. Определение может содержать параметры, завернутые в {braces}
.
Затем значение этих параметров можно получить из карты параметров , но обратите внимание, что значения заключены в KVar<String>
объект. Это означает, что вы можете использовать все функции управления состоянием Kweb для рендеринга частей DOM с использованием этого значения.
Ключевым преимуществом здесь является то, что при изменении URL-адреса страница может быть обновлена без полного обновления страницы, а, скорее, с изменением только тех частей DOM, которые необходимо изменить — это намного быстрее и эффективнее.
Обработка ошибки 404
Вы можете переопределить сообщение по умолчанию 404 Page Not Found в случае, если ни один из маршрутов не совпадает, что упрощает интеграцию страницы 404 со стилем вашего общего веб-сайта:
route {
path("/users/{userId}") { params ->
// ...
}
notFound {
h1().text("Page not found!")
}
}
Изменение URL-адреса
Вы можете получить и изменить URL-адрес текущей страницы с помощью WebBrowser.url .
Это возвращает , KVar<String>
который содержит URL относительно происхождения — так для страницы будет .http://foo/bar/z``url``/bar/z
Вот более реалистичный пример:
import kweb.Kweb
import kweb.dom.element.creation.tags.a
import kweb.dom.element.new
import kweb.routing.route
import kweb.state.*
fun main() {
Kweb(port = 16097) {
doc.body {
route {
path("/") {
url.value = "/number/1"
}
path("/number/{num}") { params ->
val num = params.getValue("num").toInt()
a().text(num.map {"Number $it"}).on.click {
num.value++
}
}
}
}
}
}
Если вы посетите, http://localhost:16097/
URL-адрес будет немедленно обновлен до http://localhost:16097/number/1
без обновления страницы, и вы увидите гиперссылку с текстом . Если вы нажмете на эту ссылку, вы увидите, что число увеличивается (как в URL-адресе, так и в тексте ссылки), также без обновления страницы.Number 1
Линия num.value++
заслуживает дополнительного внимания, поскольку здесь происходит больше, чем кажется на первый взгляд. num
является a KVar<Int>
, поэтому его можно увеличивать с помощью его value
свойства. Это приведет к обновлению URL-адреса страницы, что, в свою очередь, приведет к обновлению DOM для отражения нового URL-адреса. Все это Kweb делает за вас автоматически.
База данных
Обзор
Shoebox - это простое хранилище ключей и значений, которое поддерживает шаблон наблюдателя и является дочерним проектом Kweb. Он поддерживает ряд внутренних механизмов хранения, включая оперативную память (удобную для тестирования) и MapDB (лучшую для производственного использования).
Kweb не требует использования Shoebox. Вы можете использовать любую базу данных напрямую или через уровень абстракции базы данных, такой как Exposed . У Kotlin есть широкий выбор , как и у Java .
Основным преимуществом использования Shoebox является его интеграция с управлением состоянием Kweb .
Предположим, вы потратили минуту или две, чтобы просмотреть Shoebox и получить общее представление о том, как он используется.
Shoebox и состояние
В этом примере показано, как toVar можно использовать для преобразования значения в Shoebox в KVar :
fun main() {
data class User(val name : String, val email : String)
val users = Shoebox<User>()
users["aaa"] = User("Ian", "ian@ian.ian")
Kweb(port = 16097) {
doc.body {
val user = toVar(users, "aaa")
ul {
li().text(user.map {"Name: ${it.name}"})
li().text(user.map {"Email: ${it.email}"})
}
}
}
}
Будущее развитие
В будущем Shoebox будет поддерживать внутренние облачные сервисы, такие как AWS Pub / Sub Messaging и Dynamo DB , что обеспечит неограниченную масштабируемость. Новые серверные части хранилища можно легко добавить в Shoebox, реализовав интерфейс магазина .
(http://docs.kweb.io/en/latest/database.html#working-example "Постоянная ссылка на этот заголовок")
Более полный пример использования Shoebox для постоянного хранения см. В демонстрационной программе .
CSS и стиль
Kweb имеет встроенную поддержку отличного фреймворка Fantic UI , который помогает создавать красивые, отзывчивые макеты с использованием удобного для человека HTML.
Сначала скажите Kweb использовать плагин Fantic UI:
import kweb.plugins.fomanticUI.*
fun main() {
Kweb(port = 16097, plugins = listOf(fomanticUIPlugin)) {
// ...
}
}
Теперь плагин автоматически добавит на ваш сайт код Fantic CSS и JavaScript.
Давайте посмотрим на один из простых примеров из документации Fantic UI :
<div class="ui icon input">
<input type="text" placeholder="Search...">
<i class="search icon"></i>
</div>
Это переводится на Котлин:
import kweb.plugins.fomanticUI.*
import kweb.dom.element.creation.tags.InputType.*
fun main() {
Kweb(port = 16097, plugins = listOf(fomanticUIPlugin)4) {
div(fomantic.ui.icon.input).new {
input(type = text, placeholder = "Search...")
i(fomantic.search.icon)
}
}
}
Взгляните на документацию Fantic UI, чтобы увидеть все остальное, что он может делать.
Другие фреймворки пользовательского интерфейса
Легко создавать плагины Kweb для многих инструментов и фреймворков JavaScript, в полной мере используя возможности Kotlin DSL.
Реализация плагин Fomantic пользовательского интерфейса само по себе может служить в качестве примера.
Пример и демонстрация
Посмотрите простое приложение, созданное с использованием Fantic UI и Kweb (с исходным кодом): http://demo.kweb.io:7659/
Часто задаваемые вопросы
Не будет ли Kweb медленным по сравнению с клиентскими веб-фреймворками?
Нет, немедленные события Kweb позволяют избежать задержек связи с сервером, немедленно отвечая на события, изменяющие DOM.
Kweb по умолчанию спроектирован так, чтобы быть эффективным, сводя к минимуму использование ЦП / памяти как браузера, так и сервера.
Если вы столкнулись с ситуацией, когда Kweb работает медленно, сообщите об ошибке .
В чем разница между Kweb и Vaadin?
Из всех известных нам веб-фреймворков Vaadin наиболее близок по дизайну и философии к Kweb, но есть и важные отличия:
- Kweb намного легче, чем Vaadin. На момент написания kweb-core занимал около 4351 строки кода, а vaadin / framework в настоящее время — это 502 398 строк кода, то есть соотношение почти 100: 1!
- В Vaadin нет функции, эквивалентной непосредственным событиям Kweb , что привело к частым жалобам на медлительность со стороны пользователей Vaadin, поскольку для обновления DOM требуется обратная связь с сервером.
- Vaadin привнес в веб-браузер пользовательский интерфейс в стиле рабочего стола, но с тех пор мы поняли, что пользователи обычно предпочитают, чтобы их веб-сайты выглядели как веб-сайты.
- Вот почему философия Kweb заключается в том, чтобы быть тонким интерфейсом между логикой сервера и браузером пользователя, используя существующие инструменты из экосистемы JavaScript, когда это имеет смысл .
- Kweb был изначально создан для Kotlin и использует все его языковые функции, такие как сопрограммы и гибкий DSL-подобный синтаксис. Благодаря этому код Kweb может быть более лаконичным без ущерба для читабельности.
- В пользу Vaadin, это коммерческий продукт с 2006 года, он чрезвычайно зрелый и имеет обширную экосистему для разработчиков, в то время как Kweb все еще находится до версии 1.0.
Есть рабочий пример побольше?
Да, вот простая реализация списка дел, демонстрирующая многие функции Kweb.
Вы можете найти копию этой демонстрации здесь: http://demo.kweb.io:7659/
Он работает на инстансе EC2 за 50 долларов в месяц. Попробуйте посетить один и тот же URL-адрес списка в двух разных окнах браузера и обратите внимание, как они синхронизируются в реальном времени.
Вы можете увидеть ряд других примеров проектов Kweb здесь: kwebio / kweb-demos
Как включить HTTPS?
Очень просто, посмотрите этот пример .
Могу ли я встроить Kweb в приложение для Android?
Да! См. Пример в kweb-demos / tree / master / android .
Я хочу развернуть приложение Kweb за балансировщиком нагрузки, что мне нужно учесть?
Не забудьте включить привязку сеанса, чтобы повторяющиеся запросы от одного и того же клиента попадали в один и тот же экземпляр kweb. Kweb не делится своим внутренним состоянием между несколькими экземплярами, поэтому важно убедиться, что каждый запрос от одного пользователя всегда заканчивается одним и тем же экземпляром.
Если балансировщик нагрузки использует, например, стратегию циклического перебора для балансировки нагрузки, повторяющиеся запросы попадают в разные серверные экземпляры, и kweb может не работать должным образом.
Пример настройки HAProxy можно найти [здесь] ( https://www.haproxy.com/de/blog/load-balancing-affinity-persistence-sticky-sessions-what-you-need-to-know/ ).
А как насчет шаблонов?
Kweb заменяет шаблоны чем-то лучшим — типичным HTML DSL, встроенным в мощный язык программирования.
Если хотите, можете выделить код, который взаимодействует напрямую с DOM — это было бы архитектурно ближе к подходу на основе шаблонов, но мы рассматриваем это как особенность, которую эта парадигма не навязывается программисту.
Зачем рисковать своим проектом на платформе, о которой я только что слышал?
Выбор фреймворка — это стресс. Выберите не тот, и, возможно, компания, стоящая за ним, обанкротится, а это означает, что все ваше приложение теперь построено на чем-то устаревшем. Мы были там.
Разработкой Kweb движет сообщество добровольцев. Мы приветствуем пожертвования от кого угодно, но Kweb не зависит от какой-либо компании-спонсора.
Благодаря мощным абстракциям, на которых он построен, Kweb также имеет преимущество простоты (<5k loc). Это облегчает людям участие, а меньшее количество кода означает меньше ошибок.
Тем не менее, Kweb все еще является версией до 1.0, что означает, что мы можем и будем вносить критические изменения в API, а новые выпуски выходят довольно часто.
Можно ли встроить Kweb в существующее приложение Ktor?
Да! Пожалуйста, посмотрите этот пример .
Комментарии (16)
kom09
30.09.2021 09:09+1Мне почему-то покойный ASP.NET вспомнился (который еще без MVC). Главный девиз программистов всех времен и народов: зачем изучать чужие велосипеды, если можно изобрести свой собственный, с блекджеком и ... (сюда вписать любимую игрушку)?!!! :)
hardtop
30.09.2021 10:40ul {
li().text("One")
li().text("Two")
}Правильно ли я понимаю, при необходимости поправить вёрстку, надо залезть в код Котлина, поправить, и скомпилировать?
Nitvex
30.09.2021 12:04-2Создание полнофункциональных веб- приложений обычно требует навигации по ужасной экосистеме Javascript, выбора между огромным множеством инструментов, транспиляторов, минификаторов, специалистов по сопровождению состояния и т.д., Большинство из которых устареют через 6 месяцев.
Что в ней такого ужасного? Разве плохо, что появляются новые инструменты и платформа развивается? "устаревают" – что устаревает-то? Фреймворки React/Vue/Angular и популярные тулы по типу Redux/Vuex или сборщики (Webpack) живут себе годами. И ничто вам не мешает их использовать. Если выйдет новая версия, никто не заставит вас сразу же на нее обновляться.
Этот простой пример уже показывает некоторые важные особенности Kweb:
* Установить и запустить веб-сайт очень просто, не нужно возиться с сервлетами или сторонними веб-серверами.
* Ваш код Kweb будет примерно отражать структуру генерируемого HTML-кода.
Чем это лучше аналогичных примеров на других языках программирования? Например, на Go https://tproger.ru/translations/go-web-server/ или Node.js https://expressjs.com/ru/starter/hello-world.html ?
Затем вы сталкиваетесь с взаимодействием между клиентом и сервером, что представляет собой совершенно другой мир боли.
KWeb исправляет это.
Современные веб-сайты состоят как минимум из двух тесно связанных компонентов, один из которых работает в браузере, а другой — на сервере. Они часто написаны на разных языках программирования и должны связываться друг с другом через HTTP-соединение.
Цель Kweb — устранить это разделение сервера и браузера, чтобы архитектура вашего веб-приложения определялась решаемой проблемой, а не ограничениями современных инструментов.
Какая конкретно боль между взаимодействием клиента и сервера? Что плохого в разделении на клиента и сервера (один из принципов REST, между прочим).
Слабо себе представляю бекендера, который будет писать фронтовую логику, HTML и стили (если приложение больше, чем обычный TO DO list).
Фронту для этого придется учить котлин и делать исправления на бекенде?
Как вы потом искать людей будете под такой стек?prohfessor Автор
30.09.2021 12:17+3Это перевод. Автор фреймворка вас не услышит :-).
Чем это лучше аналогичных примеров на других языках программирования? Например, на Go https://tproger.ru/translations/go-web-server/ или Node.js https://expressjs.com/ru/starter/hello-world.html ?
Ничем. Фреймвор для тех, кто хочет писать на Котлине.
Какая конкретно боль между взаимодействием клиента и сервера? Что плохого в разделении на клиента и сервера (один из принципов REST, между прочим).
Тоже ничего плохого. Но в некоторых случаях я предпочел бы писать бакенд и фронтенд на одном языке, не заморачиваясь взаимодействием фронтенда и бакенда.
Reformat
02.10.2021 20:51+1Как вы потом искать людей будете под такой стек?
Подойдет любой Kotlin программист. Или Java программист с небольшим периодом адаптации. И такой стек в отличие от JS будет стабильно актуален еще с десяток лет, в отличие от "прогрессивного" мира JS, где фреймворки и инструменты сменяют друг друга каждые полгода.
Throwable
02.10.2021 13:43+1Интересная идея серверного рендеринга, однако к сожалению, годится только для небольших автономных веб страничек. Уже давно пишу на Vaadin бизнес приложения, и идея серверного state management-а на порядки упрощает разработку, позволяя ограничиться только одной платформой/языком и убирая рудиментарную прослойку ввиду API.
Но проблема в том, что в современном фронтэнде сложно ограничиться только библиотекой стилей и виджетов -- так или иначе потребуется интеграция с мощной экосистемой JS. Это было основным и правильным решением для Vaadin Flow: теперь там можно достаточно просто интегрировать JS-библиотеки, при этом не теряя возможности серверного state-management-а.
Reformat
02.10.2021 21:11+2Спасибо! А главное, это очень удобно для бекенд разработчиков. Написал логику и быстро в том-же сервисе прикрутил UI (как я понимаю библиотека стилей в комплекте даст возможность не тратиться на дизайн).
iskateli
Сейчас есть Vaadin Fusion, где фронт можно писать на TypeScript, причем можно комбинировать обычный Vaadin и Vaadin Fusion, соответственно критичные по производительности участки можно не на стороне сервера обрабатывать, а на клиенте. А можно вообще всё на Vaadin Fusion написать если не нужен SSR, короче очень удобно.
Reformat
Но Kotlin как язык банально лучше Java. И писать на нем по ощущениям большинству попробовавших программистов приятнее. Да и для DSL Kotlin лучше подходит, а тут у нас как раз HTML-подобный DSL.
iskateli
Самое главное что меня отталкивает в котлине это его неясное будущее, например есть такая проблема:
Ну и ещё конечно других проблем хватает в котлине, так что насчёт того что "котлин банально лучше джавы" я бы поспорил.
Reformat
Автор этой статьи путает причину со следствием)
Наоборот, это Java отчаянно пытается догнать Kotlin, но не догонит, так как несет груз обратной совместимости и новые возможности прибиваются страшными синтаксическими костылями.
iskateli
Пока большинство проектов пишутся на Java, то Kotlin - второстепенный язык, причём чем дальше, тем острее будет проблема описанная выше, неважно кто кого догоняет, важно что один из языков останется не у дел и это будет явно не Java.
Reformat
Уже давно большинство новых проектов под Android - сплошь Kotlin. С этого плацдарма он переползает на бекенд (например в Яндекс Банке все новые микросервисы сразу начинают на Kotlin). Подозреваю не у дел останется именно Java (как старый си после появления плюсов).