Библиотека Moon.js
Moon.js это минималистичная JavaScript-библиотека, предназначенная для разработки быстрых и функциональных интерфейсов. Она имеет сравнительно небольшие размеры, что позволяет создавать на её основе достаточно компактные приложения. Библиотека отличается очень высокой производительностью
В Moon.js используется подход к проектированию интерфейсов, основанный на компонентах. Для создания компонентов применяются шаблоны. Эта библиотека весьма похожа на Vue.js.
Сильные стороны Moon.js
- Moon.js отличается компактными размерами (в минифицированном и сжатом виде это около 2 Кб). Это меньше, чем размеры других библиотек и фреймворков вроде React и Angular.
- Эта библиотека отличается высокой скоростью рендеринга интерфейсов.
- Moon.js это библиотека, основанная на функциональных методах разработки. При работе с ней используется подход к проектированию интерфейсов, основанный на так называемых драйверах.
Начало работы
Библиотеку Moon.js можно включить в проект двумя способами. Первый заключается в установке её из NPM. Второй в её подключении непосредственно к странице, на которой её планируется использовать.
Если решено воспользоваться NPM-вариантом библиотеки, то сначала нужно будет установить пакет
moon-cli
, инструмент
командной строки:
$ npm i moon-cli -g
В данном примере этот инструмент устанавливается глобально, вызвать его можно из любой директории.
Для создания проекта, основанного на Moon.js, можно выполнить следующую команду:
$ moon create moon-prj
Эта команда создаёт новый проект в папке
moon-prj
.
После того, как будет завершено создание проекта, в вашем
распоряжении окажется основа будущего приложения.Второй вариант использования Moon.js предусматривает её подключение к странице, на которой её планируется использовать. У библиотеки есть модуль
moon-browser
, который позволяет
пользоваться её возможностями непосредственно на странице, к
которой она подключена.Итак, для подключения библиотеки к странице нам нужно включить в состав страницы два следующих тега:
<script src="http://personeltest.ru/aways/unpkg.com/moon"></script><script src="http://personeltest.ru/aways/unpkg.com/moon-browser"></script>
Как видите, соответствующие скрипты загружаются с CDN unpkg. В первом теге импортируется основная библиотека. Во втором библиотека
moon-browser
. Она отвечает за компиляцию шаблонов
Moon.js, за приведение их к виду, пригодному для вывода
браузером.Теперь, для того чтобы воспользоваться синтаксическими конструкциями Moon.js на странице, нужно будет включить их в тег
<script>
, не забыв задать его атрибут
type
как text/moon
.
<!-- Подключение к странице внешнего скрипта --><script src="./main-script.js" type="text/moon"></script><!-- Описание интерфейса в коде, встроенном в страницу --><script type="text/moon">...</script>
Подключение Moon.js-приложения к странице
Moon.js, как и другие библиотеки и фреймворки, используемые для создания одностраничных приложений, подключается к определённому элементу страницы. Обычно роль контейнера для Moon.js-приложения играет элемент
<div>
:
<div id="root"></div>
Подобный элемент, являющийся корневым элементом Moon.js-приложения, размещают в коде файла
index.html
, представляющего
собой точку входа в проект.Для подключения Moon.js-приложения к этому элементу используется драйвер
view
(ниже мы поговорим о драйверах
подробнее):
Moon.use({view: Moon.view.driver("#root")})
Данная конструкция сообщает библиотеке о том, что она должна подключить приложение к элементу с идентификатором
root
. При необходимости указать библиотеке подобный
элемент можно, воспользовавшись браузерным API, предназначенным для
работы с DOM:
Moon.use({view: Moon.view.driver(document.getElementById("root"))})
Теперь поговорим о том, как в Moon.js организована работа с данными, и о том, как с помощью этой библиотеки создавать элементы интерфейсов.
Синтаксис описания элементов интерфейса
Для описания Moon.js-интерфейсов используется язык программирования Moon View Language (MVL), который был разработан специально для решения данной задачи. Он напоминает JSX. Этот язык применяется для описания элементов и для настройки их взаимоотношений. Вот пример:
<script type="text/moon">function aView(data) {return (<div>Hi from Moon</div>)}</script>
Несложно заметить то, что в этом фрагменте Moon.js-кода, ответственного за формирование элемента
<div>
,
используются синтаксические структуры, напоминающие HTML. Но эти
структуры используются в JavaScript-коде. Такой JavaScript-код
браузер выполнить не сможет, но это от него и не требуется, так как
Moon.js компилирует подобные конструкции в обычный JavaScript.Работа с данными
В Moon.js для управления визуальными представлениями элементов и для работы с данными используется концепция драйверов. Здесь мы взглянем на драйвер, позволяющий работать с данными, а в следующем разделе поговорим о драйвере, предназначенном для работы с элементами интерфейса.
Драйвер, предназначенный для работы с данными, отвечает за хранение данных приложения и позволяет пользоваться данными там, где они нужны. Другими словами, этот драйвер хранит глобальное состояние приложения.
Задать начальные данные Moon.js-приложения можно с помощью API
Moon.use
:
Moon.use({data: Moon.data.driver})
Записывать новые данные в состояния можно, возвращая их из соответствующих функций:
Moon.run(({ data }) => {console.log(data) // undefinedreturn {data: "Nnamdi"}})
API
Moon.run
отвечает за запуск приложения. Коллбэк,
переданный этому API, получает ссылку на глобальные данные в
аргументе data
. Так как на момент вызова этой функции
в data
пока ничего нет, команда
console.log
из этого примера выведет
undefined
.Мы возвращаем из коллбэка объект, у которого имеется свойство
data
со значением Nnamdi
. Этот объект
будет представлять собой новое состояние приложение, данными
которого смогут воспользоваться любые другие функции, обращающиеся
к data
.Механизм работы с данными в Moon.js мы рассмотрели. Теперь подробнее поговорим о работе с элементами интерфейса.
Работа с элементами интерфейса
В Moon.js имеется драйвер
view
, который предназначен
для создания элементов и для монтирования их в DOM.Мы уже рассматривали фрагмент кода, повторённый ниже, в котором к элементу
<div>
подключается базовый элемент
Moon.js:
Moon.use({view: Moon.view.driver("#root")})
Именно тут производится монтирование элемента. Теперь функции могут возвращать элементы, которые способны заменять старые элементы. Их можно представлять в виде объектов, содержащих свойство
view
, в которое записаны соответствующие данные.
Библиотека берёт значение свойства view
из
возвращаемого функцией объекта и записывает его в элемент,
подключённый к элементу с идентификатором root
.В Moon.js используется концепция виртуальной DOM и мощный алгоритм сравнения старой и новой версий интерфейса. Это позволят библиотеке принимать решения о том, когда нужно обновлять DOM, и о том, какие именно части DOM нуждаются в обновлении.
function handleClick() {return {};}Moon.run(() => ({view: <button @click=handleClick>Click Me!</button>}));
Здесь коллбэк, передаваемый
Moon.run
, выводит в DOM
кнопку. Происходит это из-за того, что функция возвращает объект со
свойством view
. Значение, назначенное этому свойству,
попадает в DOM.У кнопки имеется обработчик события
click
,
представленный функцией handleClick
. Эта функция
возвращает пустой объект, её вызов не приводит к внесению изменений
в DOM.Создание элементов
Moon.js представляет разработчику большой набор вспомогательных функций, предназначенных для создания элементов интерфейсов. В результате оказывается, что элементы можно создавать, используя не язык описания интерфейсов Moon.js, а соответствующие функции:
const { div, text, node, p } = Moon.view.m
Moon.js экспортирует функции, имена которых соответствуют именам создаваемых с их помощью элементов. Так, функция
div
позволяет создавать элементы <div>
. Функция
text
создаёт текстовые узлы. Функция node
позволяет создавать пользовательские элементы. Функция
p
создаёт элементы <p>
. Как видите,
имена этих функций ясно указывают на их предназначение.Создадим элемент
<div>
:
const Div = div({});
Назначать элементам атрибуты можно, передавая соответствующей функции объект со свойствами:
const Div = div({class: "DivClass"});
Здесь мы описали элемент
<div>
, в атрибут
class
которого должно быть записано значение
DivClass
.Вот как создать текстовый элемент:
const Text = text({ data: "A text node" });
В свойстве
data
объекта, переданного функции
text
, имеется текст для элемента.Создадим пользовательский элемент:
const CustomEl = node("custom-el");
Для того чтобы задать этому элементу какой-нибудь атрибут, можно поступить так:
CustomEl({ "attr": "attr-value"})
События
Подключать к элементам обработчики событий можно, пользуясь конструкцией, в которой применяется символ
@
:
function handleClick() {return {};}Moon.run(() => ({view: <button @click=handleClick>Click Me!</button>}));
В результате на страницу будет выведена кнопка с текстом
Click Me
, по нажатию на которую будет вызвана функция
handleClick
.Компоненты
В Moon.js функции это компоненты. Это означает, что функции можно упоминать в описании элементов интерфейса. То, что возвращает функция, будет включено в состав элемента.
Предположим, у нас есть такая функция:
function aView({ data }) {return <div>A View</div>}
Эта функция,
aView
, возвращает элемент, который может
быть отрендерен:
Moon.run(() => {view: <div><aView /></div>})
Имя функции в этом примере используется в роли имени элемента. В результате выполнения этого кода окажется, что то, что возвращает функция, будет помещено в тег
<div>
. Когда всё
это попадёт в DOM, там окажется такая разметка:
<div><div>A View</div></div>
Разработка приложений, основанных на Moon.js
Для того чтобы собрать воедино всё то, о чём мы только что говорили, давайте создадим на Moon.js простое TODO-приложение. Здесь мы воспользуемся соответствующим примером, который подготовлен разработчиками Moon.js.
Хочу напомнить о том, что рекомендуется, осваивая новые библиотеки и фреймворки, создавать с их помощью небольшие приложения. Это позволяет ускорить обучение и помогает понять особенности работы изучаемых инструментов. Поначалу речь идёт об их основах, но со временем приходит и понимание более сложных механизмов.
Вот как выглядит страница этого приложения.
Страница приложения
На странице имеется заголовок, поле, кнопка, и список дел, который можно пополнять, вводя их описания в поле и нажимая на кнопку.
Начнём работу с создания файла
index.html
. Здесь мы
подключим Moon.js непосредственно к странице:
<html><body><div id="root"></div></body><script src="http://personeltest.ru/aways/unpkg.com/moon"></script><script src="http://personeltest.ru/aways/unpkg.com/moon-browser"></script><!-- Воспользуемся скриптом, встроенным в страницу --><script type="text/moon">function viewTodos({data, view}) {return (<div><input type="text" value=data.todo @input=updateTodo/><button @click=createTodo>Create</button><ul children=(data.todos.map(todo =><li>{todo}</li>))/></div>)}function updateTodo({ data, view }) {const dataNew = { ...data, todo: view.target.value };return {data: dataNew,view: <viewTodos data=dataNew/>}}function createTodo({ data }) {const dataNew = {todo: "",todos: [...data.todos, data.todo]};return {data: dataNew,view: <viewTodos data=dataNew/>}}<!-- Настройка драйверов data и view -->Moon.use({data: Moon.data.driver,view: Moon.view.driver("#root")})<!-- Запуск приложения -->Moon.run(() => {data: [],view: <viewTodos data=[]>})</script></html>
Функция
viewTodos
выводит элементы, необходимые для
ввода сведений о новых делах и для вывода их в виде списка. Её
аргументами являются data
и view
.Функция
createTodo
создаёт новое дело и возвращает его
в свойстве data
возвращаемого ей объекта.Функция
updateTodo
записывает новое дело в состояние
приложения.Обратите внимание на обработчики событий
@click
и
@input
, которые имеются в функции
viewTodos
. Событие @input
вызывается при
вводе текста, описывающего дело, в соответствующее поле. При
обработке этого события вызывается функция updateTodo
.
Аргумент view
в этой функции представляет произошедшее
событие. Пользуясь им, мы обращаемся к DOM и получаем данные,
введённые в поле. Затем эти данные попадают в состояние в виде
свойства todo
.Событие
@click
вызывается после нажатия на кнопку. Оно
выполняет запись нового дела в список дел. Для решения этой задачи
используется функция createTodo
. Эта функция
обращается к свойству состояния todo
и записывает
новые данные в свойство todos
, после чего, в свойстве
view
возвращаемого ей объекта, возвращает элемент
<viewTodos>
, представленный соответствующей
функцией, в атрибут data
которого записано значение
dataNew
.Это приведёт к выполнению повторного рендеринга
viewTodos
и к обновлению DOM. В список дел, выводимый
на странице, будет добавлено новое дело.Запустите это приложение в браузере и поэкспериментируйте с ним.
Итоги
Мы разобрали основы Moon.js. А именно, поговорили о том, что пользоваться библиотекой можно, устанавливая её из NPM и подключая к страницам напрямую. Далее, мы обсудили внутренние механизмы библиотеки: работу с данными, обработку событий, разработку компонентов.
Мне кажется, что Moon.js это приятная библиотека. И, если даже не говорить о других её достоинствах, она мне симпатична из-за её компактного размера.
Пользовались ли вы Moon.js?