All U Need Is Pizza πŸ•

Cover image

Published on dev.to and medium.com

Some time ago (mid-September), I challenged myself to code for 100 consecutive days. Unlike the #100DaysOfCode challenge, I didn't post my progress on Twitter. During this period, I tried several frameworks (frontend / backend) under a common theme: creating, reading, updating and deleting... pizzas!

NB: My passion for Italian gastronomy got the better of me this time...

Now halfway from my goal, I explain in this article my approach, as well as the things I discovered by testing the following frontend frameworks (and libraries): Angular, React, Svelte and Vue. My point of view will be component-oriented because of the technologies used.

The final result is available here. See this article as a feedback.

Introduction

Before going any further, and especially before opening my favorite IDE (VSCode), I had to come up with an idea. I already had the objective to test several frameworks and / or languages (after all, I'm a passionate, I eat code morning, noon and evening), so I only needed the theme...

At first, I thought I would make a series of counters, but the idea being too classic, I chose to abandon it. A few days later, during a visit into a pizzeria, when I was making my choice, I said to myself: "Hey! It would be great to create your own pizzas!"

This is my idea: a CRUD with pizza as its main topic. The idea of the CRUD is not very original... However, it allowed me to develop several boilerplates (some consisting of routing, others implementing a reactive programming paradigm) for my future developments. Another important point is that I decided to code a series of isomorphic applications in terms of graphic rendering, that is to say that the visual must be the same, regardless of the framework (or the library) used.

Design

First step: design! Style is a key element when creating a web application. Not being UI/UX, I opted for a simple design with a hint of Material Design (for colors). I modeled the different screens using the super tool Figma. I'm not using all features of the application, however, the tool has nothing to envy to Sketch. Small parenthesis: Figma can be enhanced with features (in the form of plugins) including a must-have: SVG To JSX; extremely convenient for a React developer!

AUNIP

Anyway, once the models were done, I bought myself a reminder shot on the CSS. Especially on the concepts of flexbox and grid layout that I didn't know fully (CSS3 being modular, we learn it almost every day). I actually started coding from that moment: opening VSCode, creating a .scss file, hands on the keyboard and go!

Seduced by Brad Frost's Atomic Model, I wrote the different parts of my application following this principle of decomposition: atoms, molecules, organisms, etc... So, you'll easily find the colors as atoms, or even the "bricks" that will compose my application (such as the list element) as organisms. The Atomic Model is a really interesting concept, and ideal to organize its code when you carry out a Design System project. Again, I'm not UI/UX, so my organization is not perfect, but following this model allowed me to optimize the splitting of my SASS files.

And here is it, a few lines of code later (and some model changes too), I finally have a complete static application ready to be "dynamized".

Architecture

Second step: the architecture of the code! As explained above, I want to create isomorphic applications of each other. I pushed the concept a little bit further by trying to have a common organization. Except for the Angular framework where code organization is already deeply defined at initialization, I managed to get the following application architecture:

+-- public                     # 'index.html' Is Here
+-- src
    +-- assets                 # Resources Like Fonts
    +-- components
        +-- layers             # Dumb Components
        +-- containers / views # Smart Components
    +-- services
    +-- utils                  # Utility Methods
    +-- index.(s)css           # Global Styles
    +-- index.js / main.js     # App Entry Point
    +-- pizzas.json            # Data
+-- .prettierrc                # Formatter Config
+-- package.json

Regulars of React and Vue will recognize this mode of operation. For the others, here is a brief summary of the contents of the various folders:

  • public : Static files including index.html and favicon.png;
  • assets : Resources called by the code, such as fonts;
  • components : Project components split according to the Smart Vs. Dumb pattern (see below);
  • services : Methods for making asynchronous calls, in particular to recover data;
  • utils : Other utility methods.

NB: In Angular, the services folder is conducive to hosting "real" services that will then be injected into the application.

Development

Now that we have the structure in mind, let's get into component development. To do this, and to avoid code redundancy, I opted for a split into two parts: Smart Vs. Dumb. Dumb Components (or presentational components) are devoid of business logic, and are often associated with a part of style to better control graphics rendering. On the other side, Smart Components (sometimes called "containers") consume presentational components while injecting data into them. For this CRUD project, I chose to create 3 views that directly refers to 3 "containers" components. The rest of my components will be dedicated to the visual.

NB: Dan Abramov explains this mode of operation in more detail in his article. Although he has changed his point of view recently, I still use this model (especially with other component-oriented frameworks) to keep components of acceptable size.

React

Very quickly, I turned to React for the realization of my first web application. I have been working with the Facebook library for several years. The difficulty of the .jsx syntax disappears quickly, giving its developer more readability in the components it builds.

To create a React project, there's nothing better than using the create-react-app CLI utility. This dependency makes it possible to generate a stable and flexible code structure (embedding the must-have Webpack bundler) in a very simple way.

import React from 'react';

export default function Block(props) {
  const { height = 60, color = '#FF7043', children } = props;

  return (
    <div className="block" style={{ height }}>
      <div className="frame" />
      <div className="content" style={{ background: color }}>
        {children}
      </div>
    </div>
  );
}

We can read this first component React, as follows: it's a stateless component that takes 3 properties, including one (children) that allows to delegate content in this same component. Here Block.jsx is a presentational component, and is only intended to format the data passed to it. The stateless concept refers to the fact that the component is not written explicitly by being extended from a component instance of React (otherwise it would have been a component called stateful).

import React, { useState, useEffect } from 'react';
import { Block, Row, HyperLink, TextField } from '../layers';
import { readAllPizzas } from '../../services/pizza';

function useInput(initialState) {
  const [value, setValue] = useState(initialState);

  return [
    value,
    event => setValue(event.target.value)
  ];
}

export default function List(props) {
  const [filter, setFilter] = useInput('');
  const [pizzas, setPizzas] = useState([]);

  useEffect(() => {
    readAllPizzas().then(data => {
      setPizzas(data);
    });
  }, []);

  const byLabel = ({ label }) => label.includes(filter);

  return (
    <div id="app">
      <Block color="#EF5350">
        <TextField
          placeholder="All U Need Is Pizza"
          value={filter}
          handleChange={setFilter}
          size={18}
          editable
        />
      </Block>
      <Block height={285} color="#FFCA28">
        <div className="listview">
          <Row leftCell={<HyperLink>New</HyperLink>} />
          {pizzas.filter(byLabel).map(({ label, items, price }, idx) => (
            <Row
              key={idx}
              leftCell={
                <HyperLink handleClick={() => console.log(items)}>
                  {label}
                </HyperLink>
              }
              rightCell={<TextField>{price} €</TextField>}
            />
          ))}
        </div>
      </Block>
      <Block color="#FFA726">
        <TextField size={18}>
          {pizzas.filter(byLabel).length} / {pizzas.length}
        </TextField>
      </Block>
    </div>
  );
}

In this second component (always written in a stateless way), we notice two things: the use of several presentational components (including our Block.jsx) and the presence of the hooks. The "container" List.jsx above, will take care of using Dumb Components, and enrich them with a data set.

Appearing with the 16.8.0 version, hooks allow to add logic to components that are devoid of it (that is, stateless components). By opting for this principle, React has clearly defined its belonging to the functional programming paradigm, and now differs from other component-oriented frameworks.

NB: In the rest of this article, we'll have fun comparing the Block and List components (especially in terms of syntax) with the other libraries presented below.

Vue

In parallel with React, I quickly (re)developed this same CRUD application with the Vue framework. The community framework is second to none to its main competitors: Angular and React. Functionally, it's halfway between these last two, including the concepts of directives or virtual DOM.

Like React, to create my project, I used the Vue CLI tool. Unlike its counterpart from Facebook, it makes it possible to decorate the project structure of optional dependencies (such as SASS, TypeScript or Prettier). Icing on the cake, all these choices can be made graphically! Indeed, in the latest versions, @vue/cli is able to "emulate" the package.json.

<template>
  <div class="block" :style="{ height: `${height}px` }">
    <div class="frame" />
    <div class="content" :style="{ background: color }">
      <slot />
    </div>
  </div>
</template>

<script>
  export default {
    props: {
      height: {
        type: Number,
        default: 60
      },
      color: {
        type: String,
        default: '#FF7043'
      }
    }
  };
</script>

Unlike the previous framework library, we find HTML and JavaScript explicitly here. The strength of Vue lies in the writing of SPCs (Single Page Components). The .vue file syntax allows the developer to have access to HTML, JavaScript (and even CSS) in a single file (per component of course).

Putting aside the syntax, we realize that Block.vue now has 2 properties (a numeric and a string as before). This time, the content is passed through the <slot></slot> element. It's also possible to name these elements in order to make several dynamic ranges of the presentational component.

<template>
  <div id="app">
    <block color="#EF5350">
      <text-field
        placeholder="All U Need Is Pizza"
        :value="filter"
        @input="filter = $event.target.value"
        :size="18"
        editable
      />
    </block>
    <block :height="285" color="#FFCA28">
      <div class="listview">
        <row>
          <hyper-link slot="left-cell">New</hyper-link>
        </row>
        <row v-for="({ label, items, price }, idx) in filteredPizzas" :key="idx">
          <hyper-link slot="left-cell" @click="() => console.log(items)">
            {{ label }}
          </hyper-link>
          <text-field slot="right-cell">{{ price }} €</text-field>
        </row>
      </div>
    </block>
    <block color="#FFA726">
      <text-field :size="18">
        {{ filteredPizzas.length }} / {{ pizzas.length }}
      </text-field>
    </block>
  </div>
</template>

<script>
  import { Block, Row, HyperLink, TextField } from '../layers';
  import { readAllPizza } from '../../services/pizza';

  export default {
    components: {
      Block,
      Row,
      HyperLink,
      TextField
    },
    data() {
      return {
        filter: '',
        pizzas: []
      };
    },
    computed: {
      filteredPizzas() {
        return this.pizzas.filter(({ label }) => label.includes(this.filter));
      }
    },
    mounted() {
      readAllPizza().then(data => {
        this.pizzas = data;
      });
    }
  };
</script>

Always ignoring the syntax. We notice again the "massive" use of presentational components in the section dedicated to the HTML template. Vue inherits operation by AngularJS directives. Thus we find the v-bind and v-on concepts which respectively allow to evaluate code, and to play a DOM event. In the example above, I use shortcuts:

  • : equals the v-bind directive
  • @ equals the v-on directive

For Angular fans, Vue retains the principle of bidirectional binding with the v-model directive. It's easier to become proficient is this technology that it seems. The difficulty of Vue lies (perhaps) in using this for the JavaScript part.

The framework created by Evan You still has good days ahead of him as it represents a real alternative to the Facebook library. In addition, his community is expecting great things by 2020, including the next major version of Vue.

NB: Although the popularity of this framework is low compared to React, it's nevertheless a nice success of open-source project. Indeed, November 3, 2019, Vue has (finally) passed ahead of Angular in terms of downloads on NPM.

Svelte

After React and Vue, I decided to confront a challenger: Svelte! According to Rich Harris (his creator), Svelte defines itself not as a framework, but rather as a compiler. It's also radically lighter than its predecessors, and even claims to be more efficient. Unlike React and Vue, which use a virtual DOM to calculate changes and (re)make only the impacted DOM part, Svelte acts directly on the DOM while remaining reactive in case of data mutations.

To start a project with Svelte, simply clone a template available on the dedicated repository and install dependencies via the package manager (the degit library can do this work for you). In general, templates are configured to work with the RollupJS bundler like other CLI tools that initialize their respective projects by embedding Webpack.

<script>
  export let height = 60;
  export let color = '#FF7043';
</script>

<div class="block" style="height:{height}px;">
  <div class="frame" />
  <div class="content" style="background:{color};">
    <slot />
  </div>
</div>

The syntax of the Block.svelte component above is not so different from Vue. Indeed, one quickly notices similarities with the Vue's SPCs. The difference is that Svelte manages to absolve itself of the use of this. Since the arrival of the third version of the compiler, no need to worry about this.

Just like Vue, the presentational component Block.svelte contains 2 properties exposed to the parent component via the export keyword, as well as the <slot></slot> element that works in the same way as the community framework (it's therefore possible to name the different ranges).

NB: I don't know if Svelte is mature enough to force it to split Dumb Vs. Smart components, but I still use this model here.

<script>
  import { onMount } from 'svelte';
  import { Block, Row, HyperLink, TextField } from '../layers';
  import { readAllPizzas } from '../../services/pizza';

  let filter = '';
  let pizzas = [];

  onMount(() => {
    readAllPizzas().then(data => {
      pizzas = data;
    });
  });

  $: filteredPizzas = () => {
    return pizzas.filter(({ label }) => label.includes(filter));
  };
</script>

<div id="app">
  <Block color="#EF5350">
    <TextField
      placeholder="All U Need Is Pizza"
      value={filter}
      handleInput={e => (filter = e.target.value)}
      size={18}
      editable
    />
  </Block>
  <Block height={285} color="#FFCA28">
    <div class="listview">
      <Row>
        <div slot="left-cell">
          <HyperLink>New</HyperLink>
        </div>
      </Row>
      {#each filteredPizzas() as { label, items, price }, idx}
        <Row>
          <div slot="left-cell">
            <HyperLink handleClick={() => console.log(items)}>
              {label}
            </HyperLink>
          </div>
          <div slot="right-cell">
            <TextField>{price} €</TextField>
          </div>
        </Row>
      {/each}
    </div>
  </Block>
  <Block color="#FFA726">
    <TextField size={18}>
      {filteredPizzas().length} / {pizzas.length}
    </TextField>
  </Block>
</div>

In this second Svelte component, I load the pizza dataset with the onMount function of the library. The data is then injected into my presentational components. Svelte's reactivity lies in its language. The compiler chooses to use the $: symbol to make a variable reactive. Indeed, in JavaScript this use will link one variable to another. Thus, when the related variable is changed, the compiler will recalculate the change and reflect its impact on the DOM.

The compiler has a really interesting syntax with a lot of shortcuts. I advise you to go for a ride on the Svelte site, the documentation is relatively well done, and the learning is done through a playground. Rich Harris's compiler has every chance of reaching the podium of component-oriented frameworks in the next coming years (although it's not really a framework, as React after all). The latest version of Svelte includes two superb implementations:

  • Sapper: Implementation dedicated to SSR (Server-Side Rendering) operation;
  • Svelte Native: Implementation dedicated to mobile development, via NativeScript.

Angular

It took me a long time to (re)work with the Google framework. I even went through an object-oriented programming phase before testing it again. The reason is that the Angular CLI tool (@angular/cli) generates a too complete project structure. That is to say, before even having started to develop components, we already have all tools (and especially a variety of configuration files). Personally, I prefer to add my dependencies as my project progresses.

As you can see, to create a project quickly and easily with Angular, we use @angular/cli. Overall, the Angular CLI is very powerful, it allows to generate the necessary elements for the project: components, services, directives, etc... Generated files will be instantiated directly in the main module of Angular : app.module.ts. The latest versions of @angular/cli even allow you to deploy your code on dedicated platforms such as Now (by Zeit).

import { Component, Input } from '@angular/core';

@Component({
  selector: 'block',
  template: `
    <div class="block" [style.height]="height + 'px'">
      <div class="frame"></div>
      <div class="content" [style.background]="color">
        <ng-content></ng-content>
      </div>
    </div>
  `
})
export class BlockComponent {
  @Input() height = 60;
  @Input() color = '#FF7043';
}

Compared to the frameworks (and libraries) presented above, we immediately see the difference in the writing of components. Angular officially (and natively) supports the typed superset of JavaScript that is TypeScript. Developed by Microsoft since 2012, TypeScript brings rigour to JavaScript data structures. Ideal for developers who come from object-oriented programming / Sometimes too heavy for developers who prefer functional programming.

NB: Note that React and Vue can also be overloaded by TypeScript (in fact, @vue/cli offers this choice when you initialize your project).

Again, the presentational component block.component.ts exposes 2 properties (height and color respectively) through a process called decorator (@Input). Decorators are an integral part of Google's framework, and can add behavior to the function or the variable that it prefixes. Finally, content delegation is done with the <ng-content></ng-content> element in Angular.

import { Component, OnInit } from '@angular/core';
import { PizzaService } from '../../services/pizza.service';

export interface Pizza {
  label: string;
  items: string[];
  price: number;
}

@Component({
  selector: 'list',
  template: `
    <div id="app">
      <block color="#EF5350">
        <text-field
          placeholder="All U Need Is Pizza"
          [value]="filter"
          (handleInput)="setFilter($event)"
          [size]="18"
          [editable]="true">
        </text-field>
      </block>
      <block [height]="285" color="#FFCA28">
        <div class="listview">
          <row>
            <hyper-link leftCell>New</hyper-link>
          </row>
          <row *ngFor="let pizza of getFilteredPizzas(); index as idx">
            <hyper-link leftCell (handleClick)="logItems(pizza.items)">
              {{ pizza.label }}
            </hyper-link>
            <text-field rightCell>{{ pizza.price }} €</text-field>
          </row>
        </div>
      </block>
      <block color="#FFA726">
        <text-field [size]="18">
          {{ getFilteredPizzas().length }} / {{ pizzas.length }}
        </text-field>
      </block>
    </div>
  `
})
export class ListComponent implements OnInit {
  filter = '';
  pizzas: Pizza[] = [];

  constructor(private pizzaService: PizzaService) {}

  ngOnInit() {
    this.pizzaService.readAllPizzas().then((data: Pizza[]) => {
      this.pizzas = data;
    });
  }

  setFilter(event) {
    this.filter = event.target.value;
  }

  getFilteredPizzas(): Pizza[] {
    return this.pizzas.filter(({ label }) => label.includes(this.filter));
  }

  logItems(items: string[]) {
    console.log(items);
  }
}

The second TypeScript component above, contains more code than the previous one. We note here again the presence of presentational components (including block.component.ts), the use of a decorator (@Component) and especially variable typing! The concept of interface makes it possible to declare a data structure then to type the objects of the JavaScript TypeScript code. The decorator serves to transform the following class into an Angular component, and associate it with properties such as a part of HTML template (HTML can be separated from the TypeScript code, and affiliated to the component by its path via templateUrl).

I have a pretty strong opinion about Angular. Google's framework is complete, maybe too much. For example, it natively provides modules for routing management, or for forms management (which other frameworks don't implement to leave this to specific external dependencies). The concept that interests me the most is the paradigm of reactive programming by streams: RxJS (so not really specific to the library). Angular has the merit of exist for the greatest happiness of Java developers.

The End

I had a lot of fun experimenting with these different technologies. I realize that finally these frameworks (and libraries) have a lot in common, especially in the writing of components. The difference between them is made both on the syntax (.jsx, .vue, .svelte, .ts) and on the mode of operation. Indeed, some recommend the use of a virtual DOM to calculate the change and operate it quickly, while others prefer to act directly on the DOM, while being reactive.

Like many projects, the choice of technologies depends mainly on the skills of developers, and the project's purpose. Typically, projects with a lot of backend resources (Java) will move more easily to Angular-based web development. On the other hand, others are suspicious about Google's framework because of the break between AngularJS and Angular 2+. They prefer to take advantage of the Facebook's library (or even the Vue community framework which is on the rise at the moment).

I think that in the view of the challenges that emerging for the coming years, namely accessibility and embedded technologies (wearables, Smart TV, etc...), compilers such as Svelte have a card to play on the performance side. These libraries should be taken into consideration today, perhaps even we'll see the democratization of agnostic component frameworks (Polymer, Stencil) within a year or two... Stay tuned!

I hope this article has made you want to learn more about these technologies. Once again, I present here a draft of what it's possible to do with frontend component-oriented frameworks (and libraries). The functionalities don't go very far (no state management or routing) but the main topic of CRUD allows to initialize several useful boilerplates! For my part, I return to my code challenge, with the objective (in this second part) to develop RESTful APIs that will expose... pizzas of course!

Sources