Hands-on with the Marko JavaScript framework

Innovation in full-stack server-side rendering JavaScript frameworks continues apace. Marko It is developed under the auspices of eBay, which uses it on its e-commerce site. Marko is intended to be an easy-to-learn, high-performance framework.

Ryan Carniato, creator of SolidJS, has been involved in the development of Marko. He describes as “specifically built to meet the high-performance needs of the eBay platform.” Considering eBay giveaways 307 million monthly users, Marko almost certainly can handle your use case.

Marko Components

Let’s begin our exploration of Marko with its component system. Marko has one of the simplest component definition and discovery systems ever devised. You can see a simple component definition here, a color picker. Notice that in the main index.marko file, there is an HTML element called <color-picker>, along with a property that contains an array of hexadecimal colors. How does Marko find the color-picker component?

The answer is that Marko starts at the directory where the component usage is located, then, starting at the sibling directories, he works his way by looking for a directory / component that contains the required component definition. If no such component is found in the application code, Marko will fall back to the installed dependencies and scan them as well.

Note that Marko seeks upwards, so that independent branch directories do not know each other. This provides a kind of scope for the components.

In our case, Marko doesn’t have to look very far, because there is a /components/color-picker/index.marko file. (Marko will also load components from a file with the component name in the component directory, or a file within the component directory with the component name as folder and file.)

If you look at the /components/color-picker/index.marko file, you will see the definition of the color picker component shown in Listing 1.

Listing 1. color-picker.marko

import getDefaultColors from '../../util/getDefaultColors.js'

class {
  onInput(input) {
    var colors = input.colors || getDefaultColors();

    this.state = {
      selectedColor: colors[0],
      colors
    };
  }

  handleColorSelected(color) {
    this.state.selectedColor = color;
  }
}

<div>
  <color-picker-header color=state.selectedColor/>
  <color-picker-footer colors=state.colors on-color-selected("handleColorSelected")/>
</div>

Listing 1 contains the main elements of a component. You start by importing another JS file with an import statement (a simple JavaScript function that you will use if no colors are passed). Next, define the JavaScript structures you will need; in this case, a class and a function. Lastly, there is the markup of the actual template, which is mainly incorporating two other components, the header and the footer.

Let’s take a closer look at that class definition. Define two methods.

Property entry

The first method is onInput(). This is a life cycle method that takes an input argument and allows modifying the component’s state (more on the state below).

Look at the input variable. That is a reserved identifier in Marko that resolves to the properties passed from the parent above. Remember that the main component contained a property on the element. colors that pointed to a color coded list of hex. Now the child component accesses them through the input.colors property. The properties are fully reactive, which means that the system will make sure that everything that depends on the accessories is updated.

Event handling

The second method on the class is handleColorSelected, which is a custom event handler. You can see that controller in use in Listing 1 where the footer is placed:

<color-picker-footer colors=state.colors on-color-selected("handleColorSelected")/>

Translation: When the on-color-selected the event is triggered, call handleColorSelected method, passing the present arguments.

Been to Marko

State in Marko is similar to React in that it is expected to be immutable, which means that one must assign a new state to update a single property. Marko provides a way to forcibly trigger a status update:

this.setStateDirty(property);

As in other reactive frameworks, set in Marko models the internal state of the component. The reactive system takes care of updating the UI and other settings that depend on that state.

Iterating and raising events in Marko

Now let’s take a look at how the footer component does two things: it iterates over color props and triggers their on-color-selected event.

The code for color-picker-footer / index.marko shown in Listing 2.

Listing 2. color picker-footer

  <div.color-picker-footer>
  <div.color-picker-selection-container>
    <for|color| of=input.colors>
      <div>
        <color-picker-selection
          color=color
          on-color-selected("handleColorSelected", color)/>
      </div>
    </for>
    <input key="hexInput" placeholder="Hex value" on-input("handleHexInput")/>
  </div>
</div>

You can see that the iteration work is done with the <for> label. The <for> The label can specify your iterator variable with the name inside the | symbols. In this case, the iterator is named color. The collection to be iterated is identified with the of property, in this case referring to input.colors parents’ past.

Each member of the input.colors The variable will be generated as a div, with access to the color variable. This is similar in syntax to other frameworks like React.

Broadcasting events in Marko

Most of the click-through color selection work is done by the color-picker-selection component, which is emitted within the for iterator, along with the color property and controller for on-color-selected.

Listing 3 shows color-picker-selection component.

Listing 3. color picker selection component

class {
  handleColorSelected() {
    this.emit("color-selected");
  }
}

style {
  .color-picker-selection {
    width: 25px;
    height: 25px;
    border-radius: 5px 5px 5px 5px;
    display: flex;
    flex-direction: column;
    margin: 5px 0px 0px 5px;
    float: left;
  }
}

<div.color-picker-selection
  on-click("handleColorSelected")
  on-touchstart("handleColorSelected")
  style={
    backgroundColor: input.color
  }/>

most of color-picker-selection is dedicated to defining the layout (that is, the little colored squares that allow you to click on a color). Here you can see CSS as part of the structure of a component, in the style block, which defines the little rounded square. Note that you can also define CSS in a separate .css file named style.css. You can see this latter approach in the / color-picker-selection directory.

In the template markup, notice the inline style that is used to set the background color to the hex color set to input.color.

Also notice how the on-click Y on-touchstart Events are used to capture user interaction with the colored square. The event is passed to handleColorSelected, which is defined at the beginning of the file. Uses this.emit("color-selected") shoot a custom color-selected event.

Remember that color-picker-footer custom event clocks with on-color-selected("handleColorSelected", color). Notice that the controller is calling handleColorSelected and passing the color variable. But where is this function defined?

Component definition flexibility

The answer is that it is defined in the separate component.js file in the same directory, similar to the separate style.css file you saw earlier. The ability to put the discrete parts of the component into one file or into separate files (or a combination of both) allows great flexibility in how you define components as they grow from simple to complex.

Wait tag on Marko

Marko also includes a <await> label to handle asynchronous rendering. The <await> The tag allows you to pass a promise, and the framework will take care of waiting for your result and will only display it when it is available. (This is analogous to the similarly named component in Svelte.)

The <await> The tag simplifies the handling of asynchronous output.

A simple frame and no surprises

Marko lives up to its promise of being easy to learn. For one thing, you only have a small number of essential basic tags to learn. On the other hand, these tags are quite straightforward and work in harmony with the principle of least surprise. And Marko is a full-stack framework, so you also get server-side rendering out of the box, with integrations for servers like Fast.

Combined with the intuitive component definition system, packages for Webpack and Rollup, and top-notch performance, Marko makes a strong case for your next JavaScript framework.

Copyright © 2021 IDG Communications, Inc.

Leave a Comment