1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/thoughtworks-Binding.scala

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
README.md 32 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 16.03.2025 00:12 a08cfa0

Библиотека привязки Binding.scala

Готово к использованию в продакшне Очень легковесная

Присоединиться к чату на https://gitter.im/ThoughtWorksInc/Binding.scala StackOverflow Статус сборки Scaladoc Последняя версия

Binding.scala — это библиотека привязки данных для Scala, работающая как на JVM, так и на Scala.js.Binding.scala может служить основой для фреймворков пользовательского интерфейса, однако последняя версия Binding.scala 12.x больше не содержит встроенные фреймворки пользовательского интерфейса. Для создания реактивных HTML-интерфейсов рекомендуется обратиться к html.scala, который является фреймворком пользовательского интерфейса, основанным на Binding.scala, а также преемником ранее встроенной dom библиотеки.Смотрите примеры использования Binding.scala в Binding.scala • TodoMVC или ScalaFiddle DEMOs для выполнения типичных задач при работе с Binding.scala.

Сравнение с другими реактивными веб-фреймворками

Binding.scala и html.scala имеют больше возможностей и меньше концепций, чем другие реактивные веб-фреймворки, такие как ReactJS.

Binding.scala ReactJS
Поддерживает ли литералы HTML? Да Частично поддерживается. Обычный HTML не компилируется, если разработчики не заменят атрибуты class и for на className и htmlFor, и не преобразуют встроенные style из синтаксиса CSS в синтаксис JSON вручную.
Алгоритм обновления DOM Точное привязывание данных, которое быстрее, чем виртуальный DOM Виртуальное дерево DOM, что требует управления атрибутами key вручную для сложных DOM.
Управление жизненным циклом выражений привязки данных Автоматически Недоступно
Статическая проверка типов Да, даже для HTML-тэгов и атрибутов Нет
Кривая обучения Всегда легко Легко начать. Требуется много усилий для понимания его специальных случаев.

Дополнительную информацию см. в разделе Проектирование.

Начало работы

Мы создадим веб-страницу с использованием Binding.scala во время следующих шагов.### Шаг 0: Настройка проекта Sbt Scala.js

См. http://www.scala-js.org/tutorial/basic/ для информации о том, как настроить такой проект.

Шаг 1: Добавьте зависимости html.scala в ваш build.sbt:

// Активируйте макросные аннотации, установив флаги компилятора Scala для версий 2.13 и выше
scalacOptions ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Seq("-Ymacro-annotations")
  } else {
    Nil
  }
}

// Активируйте макросные аннотации, добавив плагины компилятора для версий 2.12 и ниже
libraryDependencies ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Nil
  } else {
    Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full))
  }
}

библиотека依存关系 += "org.lrng.binding" %%% "html" % "latest.release"


### Шаг 2: Создайте поле `data`, которое будет содержать некоторые `Var` и `Vars` как источник данных для привязки

```scala
case class Contact(имя: Var[String], email: Var[String])

val данные = Vars.empty[Contact]

Один Var представляет собой привязываемую переменную, которая также реализует характеристику Binding, поэтому один Var можно рассматривать как привязывающее выражение. Если другое выражение зависит от одного Var, то значение этого выражения изменится всякий раз, когда значение Var изменится.

Один Vars представляет собой последовательность привязываемых переменных, которая также реализует характеристику BindingSeq, поэтому один Vars можно рассматривать как последовательность привязывающих выражений. Если другое выражение зависит от одного Vars, то значение этого выражения изменится всякий раз, когда значение Vars изменится.

Шаг 3: Создайте метод @html, который содержит привязывающие выражения

@html
def таблица: Binding[Table] = {
  <table border="1" cellPadding="5">
    <thead>
      <tr>
        <th>Имя</th>
        <th>Email</th>
      </tr>
    </thead>
    <tbody>
      {
        for (contact <- данные) yield {
          <tr>
            <td>{contact.имя.bind}</td>
            <td>{contact.email.bind}</td>
          </tr>
        }
      }
    </tbody>
  </table>
}
```Один `@html` метод представляет собой реактивный XHTML шаблон, поддерживающий HTML текст. В отличие от обычных XML текстов, используемых в стандартных Scala методах, тип HTML текста  это конкретный подкласс `org.scalajs.dom.raw.Node` или `com.thoughtworks.binding.BindingSeq[org.scalajs.dom.raw.Node]`, а не `scala.xml.Node` или `scala.xml.NodeSeq`.

Поэтому мы можем использовать следующий код внутри `@html` метода:

```scala
@html def нод: Binding[org.scalajs.dom.raw.HTMLBRElement] = <br/>

и

@html def нод: Binding[BindingSeq[org.scalajs.dom.raw.HTMLBRElement]] = <br/><br/>

Метод @html может быть объединён с другими данными-биндингами двумя способами:

  1. Вы можете использовать метод bind внутри @html метода для получения значений других Binding.
  2. Вы можете использовать выражения for / yield для отображения одного BindingSeq в другой.

Вы можете встраивать Node или BindingSeq[Node] в другие HTML элементы текста с помощью интерполяции {...}.

Шаг 4: Отображение данных с использованием выражений привязки в методе main

@JSExport
def main(): Unit = {
  html.render(document.body, table)
}

Шаг 5: Вызов метода main на HTML-странице

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="js-fastopt.js"></script>
  </head>
  <body>
    <script type="text/javascript">
      SampleMain().main()
    </script>
  </body>
</html>

Теперь вы увидите таблицу, содержащую только заголовок, поскольку data пустой в данный момент.

Шаг 6: Добавление кнопок для заполнения data данными для таблицы```scala

@html def table: BindingSeq[Node] = {

data.value += Contact(Var("Yang Bo"), Var("yang.bo@rea-group.com")) } > Add contact
{ for (contact } }
Name Email Action
{contact.name.bind} {contact.email.bind} contact.name.value = "Modified name" } > Modify name
} ```При нажатии на кнопку **Добавить контакт** новый контакт добавляется в `data`. Затем Binding.scala понимает связь между DOM и `data`, поэтому он добавляет новую строку ``, соответствующую новому контакту.

А при нажатии на кнопку Изменить имя поле name контакта меняется, и Binding.scala изменяет содержимое соответствующей строки <tr> на новое значение поля name.

Проектирование

Точная привязка данных

ReactJS требует от пользователей предоставления функции render для каждого компонента. Функция render должна преобразовать props и state в виртуальный DOM ReactJS, после чего фреймворк ReactJS создаёт DOM с такой же структурой, как и виртуальный DOM. Когда меняется state, фреймворк ReactJS вызывает функцию render, чтобы получить новый виртуальный DOM. К сожалению, фреймворк ReactJS точно не знает, что именно изменилось в state. Фреймворк ReactJS должен сравнить новый виртуальный DOM с исходным виртуальным DOM, предположить изменения между ними, а затем применить эти предполагаемые изменения к реальному DOM. Например, после того как вы добавили строку таблицы <tr> в существующий <tbody> внутри <table>, ReactJS может считать, что вы также изменили содержимое каждой существующей строки <tr> внутри <tbody>.Причина этого заключается в том, что функция render для ReactJS не описывает взаимоотношения между состоянием (state) и DOM. Вместо этого она описывает процесс создания виртуального DOM. Как следствие, функция render не предоставляет никакой информации о цели изменения состояния, хотя это было бы полезно для системы связывания данных.В отличие от ReactJS, метод @html в Binding.scala НЕ является обычной функцией. Это шаблон, который описывает взаимоотношения между источником данных и DOM. Когда часть источника данных изменяется, Binding.scala знает о точном соответствующем частичном DOM, затронутом этим изменением, поэтому он переопределяет только ту часть метода @html, которая отражает изменение в DOM.

С помощью возможности точного связывания данных, предоставляемой Binding.scala, вы можете избавиться от концепций, используемых для указания алгоритма догадок ReactJS, таких как атрибут key, метод shouldComponentUpdate, метод componentDidUpdate или метод componentWillUpdate.

Композиция

Наименьшая компонуемая единица в ReactJS — это компонент. Можно сказать, что React-компонент легче, чем контроллер AngularJS, в то время как Binding.scala превосходит даже это.

Наименьшая компонуемая единица в Binding.scala — это метод @html. Каждый метод @html способен компоноваться с другими методами @html через .bind.

case class Contact(name: Var[String], email: Var[String])

@html
def bindingButton(contact: Contact): Binding[Button] = {
  <button
    onclick={ event: Event =>
      contact.name.value = "Изменённое имя"
    }
  >
   Изменить имя
  </button>
}

@html
def bindingTr(contact: Contact): Binding[TableRow] = {
  <tr>
    <td>{ contact.name.bind }</td>
    <td>{ contact.email.bind }</td>
    <td>{ bindingButton(contact).bind }</td>
  </tr>
}

@html
def bindingTable(contacts: BindingSeq[Contact]): Binding[Table] = {
  <table>
    <tbody>
      {
        for (contact <- contacts) yield {
          bindingTr(contact).bind
        }
      }
    </tbody>
  </table>
}
``````javascript
@JSExport
function main() {
  var data = Vars(Contact(Var("Yang Bo"), Var("yang.bo@rea-group.com")));
  dom.render(document.body, bindingTable(data));
}

Вы можете заметить, что этот подход намного проще, чем ReactJS, поскольку:

  • Вместо передачи props в ReactJS вы просто предоставляете параметры для Binding.scala.
  • Вместо указания propTypes в ReactJS вы просто определяете типы параметров в Binding.scala.
  • Вместо возникновения ошибки выполнения при неправильном соответствии типов props в ReactJS, вы проверяете типы на этапе компиляции.

Управление жизненным циклом для выражений привязки данных

Способность точной привязки данных в Binding.scala требует регистрации прослушивателей на источнике данных. Другие реактивные фреймворки, имеющие такую возможность, заставляют пользователей управлять жизненным циклом выражений привязки данных.

Например, MetaRx предоставляет метод dispose для отмены регистрации прослушивателей, созданных при построении выражений привязки данных. Пользователи MetaRx должны вызывать метод dispose после каждого изменения выражения для каждого map и flatMap. Иначе MetaRx будет утечкой памяти. К сожалению, ручное управление всеми прослушивателями слишком сложно для сложных выражений привязки данных.

Еще один реактивный веб-фреймворк Widok не предоставил никаких механизмов управления жизненным циклом выражений привязки данных. Как следствие, он всегда утекает память.В Binding.scala, в отличие от MetaRx или Widok, все выражения привязки данных являются чистыми функциональными, без побочных эффектов. Binding.scala не регистрирует никаких прослушивателей при создании отдельных выражений, поэтому пользователям не требуется ручное отменение регистрации прослушивателей для одного выражения, как в случае с MetaRx.

Вместо этого Binding.scala создает все внутренние прослушиватели вместе, когда пользователь вызывает dom.render или Binding.watch для корневого выражения. Обратите внимание, что dom.render или Binding.watch управляют прослушивателями на всех входящих выражениях, а не только на прямых прослушивателях корневого выражения.

Кратко говоря, Binding.scala разделена на две категории функциональности:

  • Пользовательские методы @html, которые производят чистые функциональные выражения без побочных эффектов.
  • Вызовы dom.render или Binding.watch, которые автоматически управляют всеми побочными эффектами.

Литеральные значения HTML и статическая типизация

Как видно, вы можете встраивать литеральные значения HTML в методах @html в файлах исходного кода Scala. Вы также можете встраивать выражения Scala в фигурных скобках в содержании или атрибутах значений литерального значения HTML.

@html
def уведомление_ящик(сообщение: String): Binding[Div] = {
  <div class="uvedomlenie" title={ s"Подсказка: $сообщение" }>
    {
      сообщение
    }
  </div>
}
```Несмотря на схожий синтаксис литералов HTML между Binding.scala и ReactJS,
Binding.scala создаёт реальный DOM вместо виртуального DOM ReactJS.

В приведённом выше примере, `<div>...</div>` создаёт элемент DOM типа `org.scalajs.dom.html.Div`.
Затем, магия `@html` позволяет методу обёрнуть результат как `Binding`.

Вы можете даже назначить `Div` локальной переменной и вызывать нативные методы DOM на этой переменной:

```scala
@html
def уведомление_ящик(сообщение: String): Binding[Div] = {
  val result: Div = <div class="uvedomlenie" title={ s"Подсказка: $сообщение" }>
    {
      сообщение
    }
  </div>

  result.scrollIntoView()

  result
}

Метод scrollIntoView будет вызван при создании Div. Если вы вызываете другой метод, который не определён в Div, компилятор Scala сообщит об ошибке во время компиляции, а не приведёт к ошибке выполнения, поскольку Scala — это статически типизированный язык, и компилятор Scala понимает тип Div.

Вы также можете заметить class и title. Они являются свойствами DOM или атрибутами HTML на Div. Они тоже проверяются на соответствие типам компилятором Scala.

Например, дан следующий метод typo:

@html
def typo = {
  val myDiv = <div typoProperty="xx">content</div>
  myDiv.typoMethod()
  myDiv
}

Компилятор Scala сообщит об ошибках такого типа:

typo.scala:23: значение typoProperty не является членом org.scalajs.dom.html.Div
        val myDiv = <div typoProperty="xx">content</div>
                     ^
typo.scala:24: значение typoMethod не является членом org.scalajs.dom.html.Div
        myDiv.typoMethod()
              ^
```С помощью статической системы типов, методы `@html` могут быть гораздо более надёжными, чем компоненты ReactJS.

Вы можете найти полный список поддерживаемых свойств и методов на [scaladoc scalajs-dom](http://www.scala-js.org/api/scalajs-dom/0.8/org/scalajs/dom/raw/HTMLElement.html) или [MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement).

#### Пользовательские атрибуты

Если вы хотите отключить статическую проверку типов атрибутов, добавьте префикс `data:` к атрибуту:

```scala
@html def myCustomDiv = {
  val myDiv = <div data:customAttributeName="attributeValue"></div>
  assert(myDiv.getAttribute("customAttributeName") == "attributeValue")
  myDiv
}

Компилятор Scala теперь не выдаёт ошибок.## Примеры использования* TodoMVC: проект, который предлагает ту же Todo-приложение, реализованное с использованием концепций MV*, в большинстве популярных сегодня JavaScript MV*-фреймворков.

  • Granblue Raid Finder: сайт для поиска твиттов рейдов Granblue Fantasy.
  • Game of Life: реализация игры Конвея "Жизнь" с использованием Binding.scala.
  • playground-binding.scala: различные демонстрационные примеры с использованием Scala, Scala.js и Binding.scala.
  • CITE Application: одностраничное браузерное приложение для исследования цитируемых ресурсов.
  • hmt-reader: пакет приложений и данных для чтения текстовых данных Homer Multitext, доступных в текущей версии.
  • Full-Stack-Scala-Starter: начальный проект для Play 2.5, ScalaJS и Binding.scala.
  • scala-adapters: простой фреймворк для реализации ваших серверных задач — предоставляющий стандартное графическое окружение для мониторинга и тестирования этих задач. (Используется в продакшне)
  • Binding.scala-Google-Maps: пошаговое руководство по началу работы с Binding.scala.
  • scala-adapters.g8: шаблон Giter8 для полносайтного проекта на Scala, использующего scala-adapters.
  • play-akka-telegrambot4s-incidentscom/pme123/play-akka-telegrambot4s-incidents): приложение управления инцидентами — где вы можете отправлять инциденты через чат-бота.
  • scala-adapters-images: демонстрационный проект, использующий scala-adapters и play-akka-telegrambot4s.
  • jmh-view: встраиваемый просмотрщик отчетов JMH, используемый для создания отчета о benchmark'ах парсеров Scala.
  • RL-Playground: веб-интерактивное демонстрационное приложение обучения с подкреплением для игр.
  • Генератор словных облаков: расширение браузера для генерации визуализаций словных облаков для веб-страниц, текстовых файлов или других произвольных текстовых входных данных. (При желании вы можете добавить свой проект здесь)## Модули

Binding.scala имеет крайне небольшую базу кода. Исходные файлы разделены на несколько библиотек, один файл на каждую библиотеку.

Основные выражения привязки данных (Binding.scala)

Этот модуль доступен как для JVM, так и для Scala.js. Вы можете добавить его в ваш build.sbt.

// Для проектов на JVM
libraryDependencies += "com.thoughtworks.binding" %% "binding" % "latest.release"
// Для проектов Scala.js или кросс-проектов JS/JVM
libraryDependencies += "com.thoughtworks.binding" %%% "binding" % "latest.release"

Интеграция с HTML DOM (html.scala)

Это новая библиотека шаблонизации HTML, основанная на именной основе XML литералов, модуль доступен только для Scala.js, а версия Scala должна находиться между 2.12 и 2.13. Вы можете добавить его в ваш build.sbt.

// Включите макросные аннотации, установив флаги компилятора для Scala 2.13
scalacOptions ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Seq("-Ymacro-annotations")
  } else {
    Nil
  }
}

// Включите макросные аннотации, добавив плагины компилятора для Scala 2.12
libraryDependencies ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Nil
  } else {
    Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full))
  }
}

// Для проектов Scala.js (Scala 2.12 - 2.13)
libraryDependencies += "org.lrng.binding" %%% "html" % "latest.release"

Дополнительная информация доступна по адресу html.scala.

Привязка данных для scala.concurrent.Future (FutureBinding.scala)Этот модуль доступен как для JVM, так и для Scala.js. Вы можете добавить его в ваш build.sbt.

// Для проектов на JVM
libraryDependencies += "com.thoughtworks.binding" %% "futurebinding" % "latest.release"
// Для проектов Scala.js или кросс-проектов JS/JVM
libraryDependencies += "com.thoughtworks.binding" %%% "futurebinding" % "latest.release"

Дополнительная информация доступна по адресу FutureBinding.

Привязка данных для ECMAScript 2015's Promise (JsPromiseBinding.scala)

Этот модуль доступен только для проектов Scala.js. Вы можете добавить его в ваш build.sbt.

// Для проектов Scala.js
libraryDependencies += "com.thoughtworks.binding" %%% "jspromisebinding" % "latest.release"

Дополнительную информацию можно найти в JsPromiseBinding.

Требования

Из-за изменений в коллекционной API, версия Binding.scala 12.x работает только с Scala 2.13, ориентированной на JVM, Scala.js 0.6 и Scala.js 1.x.

Для Scala 2.10, 2.11 и 2.12 на JVM или Scala.js 0.6 используйте версию Binding.scala 11.x.

Ссылки

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/thoughtworks-Binding.scala.git
git@api.gitlife.ru:oschina-mirror/thoughtworks-Binding.scala.git
oschina-mirror
thoughtworks-Binding.scala
thoughtworks-Binding.scala
master