Moon.js library
Moon.js is a minimalist JavaScript library for developing fast and functional interfaces. It has a relatively small size, which makes it possible to create fairly compact applications on its basis. The library has very high performance
Moon.js takes a component-based approach to interface design. Templates are used to create components. This library is quite similar to Vue.js.
Moon.js strengths
- Moon.js is compact in size (minified and compressed it is about 2 KB). This is smaller than the size of other libraries and frameworks like React and Angular.
- This library is distinguished by high speed of rendering interfaces.
- Moon.js is a library based on functional development techniques. When working with it, an approach to interface design is used, based on the so-called "drivers".
Beginning of work
The Moon.js library can be included in a project in two ways. The first is to install it from NPM. The second is connecting it directly to the page on which it is planned to be used.
If you decide to use the NPM version of the library, then you first need to install the package
moon-cli
, the command line tool:
$ npm i moon-cli -g
In this example, this tool is installed globally, you can call it from any directory.
To create a project based on Moon.js, you can run the following command:
$ moon create moon-prj
This command creates a new project in a folder
moon-prj
. After the creation of the project is completed, you will have at your disposal the foundation of the future application.
The second option for using Moon.js involves connecting it to the page on which you plan to use it. The library has a module
moon-browser
that allows you to use its capabilities directly on the page to which it is connected.
So, to connect the library to the page, we need to include the following two tags in the page:
<script src="https://unpkg.com/moon"></script>
<script src="https://unpkg.com/moon-browser"></script>
As you can see, the corresponding scripts are loaded from the unpkg CDN. The first tag imports the main library. The second is the library
moon-browser
. She is responsible for compiling the Moon.js templates, for making them look suitable for display by the browser.
Now, in order to use the Moon.js syntax on the page, you will need to include them in the tag
<script>
, not forgetting to set its attribute type
as text/moon
.
<!-- -->
<script src="./main-script.js" type="text/moon"></script>
<!-- , -->
<script type="text/moon">
...
</script>
Connecting Moon.js application to the page
Moon.js, like other libraries and frameworks used to create single page applications, connects to a specific page element. Usually an element plays the role of a container for a Moon.js application
<div>
:
<div id="root"></div>
A similar element, which is the root element of the Moon.js application, is placed in the code of the file
index.html
that is the entry point to the project.
A driver is used to connect a Moon.js application to this element
view
(below we will talk about drivers in more detail):
Moon.use({
view: Moon.view.driver("#root")
})
This construct tells the library that it should connect the application to the element with the identifier
root
. If necessary, you can specify a similar element to the library using the browser API designed for working with the DOM:
Moon.use({
view: Moon.view.driver(document.getElementById("root"))
})
Now let's talk about how data manipulation is organized in Moon.js and how to create interface elements using this library.
Syntax for describing interface elements
To describe Moon.js interfaces, the Moon View Language (MVL) programming language is used, which was developed specifically to solve this problem. It resembles JSX. This language is used to describe elements and to customize their relationships. Here's an example:
<script type="text/moon">
function aView(data) {
return (
<div>Hi from Moon</div>
)
}
</script>
It's easy to see that this piece of Moon.js, which is responsible for the element formation
<div>
, uses syntax structures that resemble HTML. But these structures are used in JavaScript code. The browser will not be able to execute such JavaScript code, but this is not required of it, since Moon.js compiles such constructs into regular JavaScript.
Working with data
Moon.js uses the concept of drivers to control the visual presentation of elements and to work with data. Here we will take a look at the driver that allows us to work with data, and in the next section we will talk about the driver for working with interface elements.
A data driver is responsible for storing application data and allowing data to be used where it is needed. In other words, this driver stores the global state of the application.
You can set the initial data for the Moon.js application using the API
Moon.use
:
Moon.use({
data: Moon.data.driver
})
You can write new data to states by returning them from the corresponding functions:
Moon.run(({ data }) => {
console.log(data) // undefined
return {
data: "Nnamdi"
}
})
The API
Moon.run
is responsible for launching the application. The callback passed to this API receives a reference to the global data as an argument data
. Since at the moment of calling this function there data
is nothing in it yet, the command console.log
from this example will output undefined
.
We return from the callback an object that has a property
data
with a value Nnamdi
. This object will represent a new state of the application, the data of which can be used by any other functions that call data
.
We have examined the mechanism for working with data in Moon.js. Now let's talk in more detail about working with interface elements.
Working with interface elements
Moon.js has a driver
view
for creating elements and mounting them in the DOM.
We have already examined the code snippet repeated below, in which the
<div>
base Moon.js element is connected to the element:
Moon.use({
view: Moon.view.driver("#root")
})
This is where the element is mounted. Functions can now return elements that are capable of replacing old elements. They can be represented in the form of objects containing a property
view
in which the corresponding data is written. The library takes the property value view
from the object returned by the function and writes it to the element connected to the element with the identifier root
.
Moon.js uses the concept of virtual DOM and a powerful algorithm for comparing old and new interface versions. This will allow the library to make decisions about when to update the DOM and which parts of the DOM need to be updated.
function handleClick() {
return {};
}
Moon.run(() => ({
view: <button @click=handleClick>Click Me!</button>
}));
Here the callback passed
Moon.run
in is outputting the button to the DOM. This happens due to the fact that the function returns an object with a property view
. The value assigned to this property goes into the DOM.
The button has an event handler
click
, represented by a function handleClick
. This function returns an empty object, calling it does not change the DOM.
Creating elements
Moon.js provides the developer with a large set of helper functions for creating interface elements. As a result, it turns out that elements can be created using not the Moon.js interface description language, but the corresponding functions:
const { div, text, node, p } = Moon.view.m
Moon.js exports functions whose names match the names of the elements they create. So, the function
div
allows you to create items <div>
. The function text
creates text nodes. The function node
allows you to create custom elements. The function p
creates elements <p>
. As you can see, the names of these functions clearly indicate their purpose.
Let's create an element
<div>
:
const Div = div({});
You can assign attributes to elements by passing an object with properties to the corresponding function:
const Div = div({
class: "DivClass"
});
Here we have described the element
<div>
, in the attribute of class
which the value should be written DivClass
.
Here's how to create a text element:
const Text = text({ data: "A text node" });
The property of the
data
object passed to the function text
contains the text for the element.
Let's create a custom element:
const CustomEl = node("custom-el");
In order to assign any attribute to this element, you can do the following:
CustomEl({ "attr": "attr-value"})
Developments
You can attach event handlers to elements using a construct that uses a symbol
@
:
function handleClick() {
return {};
}
Moon.run(() => ({
view: <button @click=handleClick>Click Me!</button>
}));
As a result, a button with text will be displayed on the page
Click Me
, by clicking on which the function will be called handleClick
.
Components
In Moon.js, functions are components. This means that functions can be mentioned in the description of interface elements. What the function returns will be included in the element.
Suppose we have a function like this:
function aView({ data }) {
return <div>A View</div>
}
This function,,
aView
returns an element that can be rendered:
Moon.run(() => {
view: <div><aView /></div>
})
The function name is used in this example as the element name. As a result of executing this code, it turns out that what the function returns will be placed in the tag
<div>
. When all of this is in the DOM, there will be markup like this:
<div>
<div>A View</div>
</div>
Development of applications based on Moon.js
In order to put together everything that we just talked about, let's create a simple TODO application in Moon.js. Here we will use the corresponding example , which was prepared by the developers of Moon.js.
I want to remind you that it is recommended, while mastering new libraries and frameworks, to create small applications with their help. This allows you to speed up learning and helps you understand the features of the studied tools. At first, we are talking about their foundations, but over time, an understanding of more complex mechanisms comes.
This is what the page of this application looks like.
Application
page The page has a title, a field, a button, and a to-do list, which can be replenished by entering their descriptions in the field and clicking on the button.
Let's start by creating a file
index.html
. Here we connect Moon.js directly to the page:
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/moon"></script>
<script src="https://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>
The function
viewTodos
displays the items required to enter information about new cases and to display them in a list. Its arguments are data
and view
.
The function
createTodo
creates a new case and returns it in the property of the data
object it returns.
The function
updateTodo
writes a new case to the application state.
Pay attention to the event handlers
@click
and @input
that are in the function viewTodos
. The event @input
is called when you enter text describing the case in the appropriate field. When this event is processed, a function is called updateTodo
. Argumentview
in this function represents the event that occurred. Using it, we access the DOM and get the data entered in the field. This data then enters the state as a property todo
.
The event
@click
is called after clicking the button. It records a new to-do list. The function is used to solve this problem createTodo
. This function accesses the state property todo
and writes new data to the property todos
, after which, in the property of the view
object returned to it, it returns the element <viewTodos>
represented by the corresponding function, in the attribute of data
which the value is written dataNew
.
This will re-render
viewTodos
and update the DOM. A new case is added to the to-do list displayed on the page.
Run this app in your browser and experiment with it.
Outcome
We've covered the basics of Moon.js. Namely, we talked about the fact that you can use the library by installing it from NPM and connecting it to the pages directly. Next, we discussed the internal mechanisms of the library: working with data, handling events, developing components.
Moon.js seems to be a nice library. And, even if not to talk about its other advantages, I like it because of its compact size.
Have you used Moon.js?