Getting started with Moon.js

Today we are going to talk about the next JavaScript library for developing interfaces. There is a feeling that such libraries appear more and more often. In this article, we will look at the Moon.js library and reveal its features that you need to know about in order to start working with it. In particular, we will talk about how to create new Moon.js projects, how to create interface elements, how to handle events. With this tutorial, you should be able to use Moon.js to develop your own applications.







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-browserthat 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 typeas 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.htmlthat 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.runis 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 datais nothing in it yet, the command console.logfrom this example will output undefined.



We return from the callback an object that has a property datawith 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 viewfor 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 viewin which the corresponding data is written. The library takes the property value viewfrom 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.runin 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 divallows you to create items <div>. The function textcreates text nodes. The function nodeallows you to create custom elements. The function pcreates 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 classwhich 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 dataobject passed to the function textcontains 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,, aViewreturns 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 fileindex.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 viewTodosdisplays the items required to enter information about new cases and to display them in a list. Its arguments are dataand view.



The function createTodocreates a new case and returns it in the property of the dataobject it returns.



The function updateTodowrites a new case to the application state.



Pay attention to the event handlers @clickand @inputthat are in the function viewTodos. The event @inputis called when you enter text describing the case in the appropriate field. When this event is processed, a function is called updateTodo. Argumentviewin 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 @clickis 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 todoand writes new data to the property todos, after which, in the property of the viewobject returned to it, it returns the element <viewTodos>represented by the corresponding function, in the attribute of datawhich the value is written dataNew.



This will re-render viewTodosand 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?










All Articles