© Francesco Strazzullo 2019
F. StrazzulloFrameworkless Front-End Developmenthttps://doi.org/10.1007/978-1-4842-4967-3_1

1. Let’s Talk About Frameworks

Francesco Strazzullo1 
(1)
TREVISO, Treviso, Italy
 

You don’t need a framework. You need a painting, not a frame.

—Klaus Kinski

So, why should you read a book about effectively developing front-end applications without frameworks? Because, sometimes, a framework is just not enough to fulfill your tasks. This book helps you understand the strategies for developing frameworkless applications, and more importantly, it shows you how to choose the right tool for the right job.

This chapter begins with my opinions on frameworks and why I believe that it’s important to learn to live without them. After this short introduction, you start learning how to work without frameworks. I show you examples of rendering, routing, state management, and so on. After you learn how to go frameworkless, you should be able to figure out if it is the right choice for your project.

The last chapter in this book helps you decide which tool is best for you. I also talk about technical decision making and how to evaluate the trade-offs in every decision.

Now that you have an idea of what you can expect from this book, let’s talk about frameworks.

What Is a Framework?

Before going deeper, let’s find a definition of framework that will guide us through the entire book. This is how the Cambridge Dictionary defines it:

A supporting structure around which something can be built.

This definition is consistent with the general idea of a software framework. If you think about the structure of an Angular application, it matches this definition exactly. Angular offers this structure with out-of-the-box elements like services, components, and pipe, around which you build your application.

In real-life applications, a stack contains other elements. You can use Lodash to manipulate arrays or objects, or Moment.js to parse dates.

Are these framework tools? The JavaScript community tends to call them libraries.

What’s the difference between a library and a framework? I often use often the following definition during my presentations:

A framework calls your code. Your code calls a library.

A framework could internally use one or more libraries, but that fact is usually hidden to the developer that sees the framework as a single unit or a bunch of modules if you choose a modular framework. The relationship between your codebase, a framework, and a library is condensed in Figure 1-1.
../images/476371_1_En_1_Chapter/476371_1_En_1_Fig1_HTML.png
Figure 1-1

Relationship between frameworks, libraries, and code

Comparing Frameworks to Libraries

I am going to use some code snippets to show the difference between frameworks and libraries. For this comparison, I will use Angular and Moment.js.

Listing 1-1 and Listing 1-2 are basic examples of Component and Service in Angular.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
const URL = 'http://example.api.com/';
@Injectable({
  providedIn: 'root',
})
export class PeopleService {
  constructor(private http: HttpClient) { }
  list() {
       return this.http.get(URL);
  }
}
Listing 1-1

Angular Service Example

import { Component, OnInit } from '@angular/core';
import { PeopleService } from '../people.service';
@Component({
  selector: 'people-list',
  templateUrl: './people-list.component.html'
})
export class PeopleListComponent implements OnInit {
  constructor(private peopleService: PeopleService) { }
  ngOnInit() {
    this.loadList();
  }
  loadList(): void {
    this.peopleService.getHeroes()
        .subscribe(people => this.people = people);
  }
}
Listing 1-2

Angular Component Example

Listing 1-3 is an example of using Moment.js to format the date.
import moment 'moment';
const DATE_FORMAT = 'DD/MM/YYYY';
export const formatDate = date => {
      return moment(date).format(DATE_FORMAT);
}
Listing 1-3

Moment.js Example

Given the previous definition, it’s quite easy to understand that Angular is a framework, while Moment.js is a library (used to manipulate dates). In Angular, to let PeopleListComponent interact with PeopleService, you should use the @Injectable annotation and put the instance in the constructor. Angular offers a structure to fill with your code and a set of utilities (like HttpClient) to help with standard tasks.

Moment.js is completely unopinionated on how to structure your application code. You just import it and use it. As long as you respect the public API, you’re good to go. Using this same definition, you can categorize a lot of your favorite npm packages. Frameworks include Angular, Vue.js, and Ember.js. A lot of libraries can be categorized by purpose, as seen in Table 1-1.
Table 1-1

Some JavaScript Libraries

Purpose

Libraries

Utilities

Lodash, Underscore.js

Date manipulation

Moment.js, date-fns

Data visualization

D3.js, Highcharts

Animations

Tween.js, Anime.js

HTTP requests

axios

I deliberately left out React—one of the most popular tools front-end developers—from this list. So, is React a library or a framework? Before answering this question, I want to introduce a new concept that will help shed some light on this topic: the framework’s way.

The Framework’s Way

As you saw in Listing 1-3, Moment.js has no opinion on how to integrate it into your code. Angular is very opinionated. You may see some of its strong ideas in the simple example shown in the previous section. The following sections discuss some of the constraints.

Language

Even if it is actually doable to build an Angular application with plain ECMAScript, TypeScript is the de facto standard in the Angular ecosystem. TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Apart from type checking, it lets you use features that are not present in the original language, such as annotations.

TypeScript could be very useful if you are used to working with strongly typed languages. But if you use Angular, all of your code is written in a language that requires a transpiler.

Dependency Injection

To let elements communicate in an Angular application, you need to inject them with a dependency injection mechanism based on types. The old AngularJS had a dependency injection mechanism based on a service locator pattern. The same injection in AngularJS looks like Listing 1-4.
const peopleListComponent = peopleService => {
       //Actual Code
};
angular.component('people-list',[
       'peopleService',
        peopleListComponent
]);
Listing 1-4

AngularJS Dependency Injection

Later in the book , you will see how to create a very simple service locator to keep your code well organized.

Observables

Angular is heavily designed around RxJS, a library for reactive programming using observables. To get data from PeopleListService, you have to use the subscribe method of the Observable object. This approach is different from the other front-end frameworks, where HTTP requests are designed like promises. Promises are a standard way to represent the eventual completion (or failure) of an asynchronous operation. RxJS lets you easily transform an observable into a promise, and vice versa.

If you need to integrate a promise-based library in your Angular project, you need to do some extra work. Listing 1-5 and Listing 1-6 show how to use axios, a library for making HTTP requests based on promises.
import axios from 'axios';
const URL = 'http://example.api.com/';
export default {
      list() {
             return axios.get(URL);
      }
}
Listing 1-5

Angular Service Without Observables

import people from 'people.js';
export class PeopleList {
      load(){
            people
                    .list()
                    .then(people => {
                           this.people = people
                    });
      }
}
Listing 1-6

Angular Component Without Observables

Note

PeopleListComponent is a class that uses the people service. It has no particular usefulness apart from showing you how to work with promises. I use to call all the constraints of a framework “the framework’s way.”

Apart from the constraints created by the core team of the framework, other constraints are part of the framework’s way. For example, the de facto standards from the community are almost as important as the core ones. In the AngularJS ecosystem, John Papa’s style guide ( https://github.com/johnpapa/angular-styleguide/tree/master/a1 ) was “the way” to write AngularJS applications. You were not enforced to use it, but most of the code that you read on the web was built that way.

Keep in mind that these constraints are neither bad nor good, but it’s very important to analyze “the way” of the framework that a team chooses in order to assess if it’s the right tool for the project.

Let’s Talk About React

How is the framework’s way related to knowing if React is a library or a framework? React is defined on its web site as “a JavaScript library for building user interfaces.”

That sounds easy enough: React is a library. But the reality is far more complex than that. The main constraint of React is the usage of the declarative paradigm. You don’t manipulate the DOM; instead, you modify the state of a component, and then React modifies the DOM for you. This way of programming is present in most of the libraries in the React ecosystem. Listing 1-7 is a very simple example of Pose, a library for animating React components. The purpose of this snippet is to show/hide a square by using an animation every time the user presses a Toggle button (see Figure 1-2).
import React, { Component } from 'react';
import posed from 'react-pose';
const Box = posed.div({
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
  transition: {
    ease: 'linear',
    duration: 500
  }
});
class PosedExample extends Component {
  constructor (props) {
    super(props)
    this.state = {
      isVisible: true
    }
    this.toggle = this.toggle.bind(this)
  }
  toggle () {
    this.setState({
      isVisible: !this.state.isVisible
    })
  }
  render () {
    const { isVisible } = this.state
    const pose = isVisible ? 'visible' : 'hidden'
    return (
      <div>
        <Box className="box" pose={pose} />
        <button onClick={this.toggle}>Toggle</button>
      </div>
    )
  }
}
export default PosedExample
Listing 1-7

React-Pose Animation Example

../images/476371_1_En_1_Chapter/476371_1_En_1_Fig2_HTML.jpg
Figure 1-2

Example of React animation using Pose

As you can see in Figure 1-2, you don’t directly animate the square. You just declare how to map the state with the animation (visible or hidden), and then change the state. This is the core of the declarative pattern used in React.

Listing 1-8 produces the same output, but it’s based on a standard API called the Web Animations API ( https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API ).
import React, { Component } from 'react'
const animationTiming = {
  duration: 500,
  ease: 'linear',
  fill: 'forwards'
}
const showKeyframes = [
  { opacity: 0 },
  { opacity: 1 }
]
const hideKeyframes = [
  ...showKeyframes
].reverse()
class PosedExample extends Component {
  constructor (props) {
    super(props)
    this.state = {
      isVisible: true
    }
    this.toggle = this.toggle.bind(this)
  }
  toggle () {
    this.setState({
      isVisible: !this.state.isVisible
    })
  }
  componentDidUpdate (prevProps, prevState) {
    const { isVisible } = this.state
    if (prevState.isVisible !== isVisible) {
      const animation = isVisible ? showKeyframes : hideKeyframes
      this.div.animate(animation, animationTiming)
    }
  }
  render () {
    return (
      <div>
        <div ref={div => { this.div = div }} className="box" />
        <button onClick={this.toggle}>Toggle</button>
      </div>
    )
  }
}
export default PosedExample
Listing 1-8

React Animation with Web Animations API

If you’re a React developer, this second example may seem out of place. This is because you’re moving the square with an imperative pattern. This “strangeness” is why I believe that React is a framework and not just a library. I believe this not because of its code, but because of the constraints that the community accepted by using it. In other words, the declarative pattern is a part of React’s way.

Tip

Where there is a “framework’s way” of doing things, there is a framework.

Brief History of JavaScript Frameworks

This section is a very brief history of front-end frameworks. It’s not meant to be comprehensive, but it’s an opinionated view of the most important milestones in the front-end ecosystem.

jQuery

Created by John Resig in 2006, jQuery is the mother of all JavaScript frameworks. By far, it is the most-used framework in production, as you can see at http://libscore.com/#libs . The most important feature of jQuery is its famous selector syntax: var element = $('.my-class').

It may seem useless today but you have to consider that in 2006, browsers were not aligned as they are today. This is the real value that jQuery brought to the front-end world. jQuery created a lingua franca between the browsers. It helped the community to grow around a common ground. In addition to the selector syntax, a lot of features were added to the core project, such as AJAX requests, animations, and other utilities. It rapidly became the Swiss Army knife of front-end development.

jQuery has an official UIKit called jQueryUI, which is easily pluggable, and so the web is full of plugins for every need. Today, front-end developers tend to joke about jQuery, but it’s been a cornerstone of modern web development.

AngularJS

If jQuery can be seen as the invention of writing, AngularJS is probably the equivalent of Gutenberg’s printing press. AngularJS was originally developed in 2009 by Miško Hevery as a side project; later, he became a Google employee. For this reason, AngularJS is actually maintained by Google engineers. The 1.0 version was released in May 2011. AngularJS was hugely successful in making single-page applications a mainstream pattern.

The most notable feature is two-way data binding. You can see an example of this characteristic in Listing 1-9; in this case, I used ng-model, which is probably the most famous AngularJS directive.
<div ng-app="app" ng-controller="ctrl">
    Value: <input ng-model="value">
    <h1>You entered: {{value}}</h1>
</div>
<script>
    angular
        .module('app', [])
        .controller('ctrl', [
            '$scope',
            $scope => {
                $scope.value = 'initial value'
            }
        ]);
</script>
Listing 1-9

AngularJS Two-Way Data Binding Example

The core of this mechanism is the $scope object. Every change to $scope is automatically applied to the DOM. Events from the input produces new values in the $scope object. You can see a schema of the two-way data binding system in Figure 1-3.
../images/476371_1_En_1_Chapter/476371_1_En_1_Fig3_HTML.png
Figure 1-3

Two-way data binding schema

Two-way data binding lets developers quickly create web applications. Over time, however, a lot of developers left AngularJS because two-way data binding is not suitable for large applications. In any case, AngularJS has the merit of introducing a lot of developers to the front-end ecosystem.

React

Created by Facebook in 2011 and open sourced in 2013, React is currently the most popular front-end library (or “framework”). Let’s look at the code of a Timer component that simply renders the number of seconds elapsed from its first rendering (see Listing 1-10).
import React, { Component } from 'react'
import { render } from 'react-dom'
class Timer extends Component {
    constructor(props){
        super(props)
        this.state = {
            seconds: 0
        }
    }
    componentDidMount() {
        this.interval = setInterval(() => {
            const { seconds } = this.state
            this.setState({
                seconds: seconds + 1
            })
        },1000)
    }
    componentWillUnmount() {
        clearInterval(this.interval)
    }
    render(){
        const { seconds } = this.state
        return (
            <div>
                Seconds Elapsed: {seconds}
            </div>
        )
    }
}
const mountNode = document.getElementById('app')
render(<Timer></Timer>, mountNode)
Listing 1-10

Basic React Component with Some Lifecycle Methods

React works with a declarative paradigm. Usually, you don’t modify the DOM directly; instead, you change the state with the setState method and let React do the rest.

Technically, React is a rendering library and not a framework. This fact allows the front-end community to fill in the gaps with a lot of very interesting ideas, especially for state management. I talk about some of these libraries, including Redux and MobX, in Chapter 7.

Angular

Angular was previously known as Angular2 because the project was intended to be a new version of AngularJS. The team behind the project took the semantic versioning very seriously, thus Angular2 became a completely different framework. Such a different approach between the two versions caused a period of panic around the project. After the first release of Angular2 in September 2016, the team decided to rename the project Angular, probably due to a planned release cycle featuring a new major version every six months.

Angular tried to appeal to the corporate world. A lot of corporations developed single-page applications with AngularJS, but the tool was not designed for very large applications. The fact that TypeScript is the de facto standard for working with Angular helped a lot of Java and C# developers start developing front-end applications.

Technical Debt

When you need to add a feature to a project, you always have a range of options. Some of them are quick and messy, while others are well designed but slower to put in production. In order to better understand the impact of this kind of decision, Ward Cunningham created the concept of technical debt ( http://wiki.c2.com/?WardExplainsDebtMetaphor ). The metaphor itself is quite simple: every time that you choose the dirty solution, you incur a debt.

As you can see in Figure 1-4, if you start to incur debt, the cost of a new feature or to change an existing feature increases exponentially over time, similar to financial debt, which, if not paid, increases due to interest.
../images/476371_1_En_1_Chapter/476371_1_En_1_Fig4_HTML.png
Figure 1-4

Technical debt

The Cost of Frameworks

Why did I dedicate a section to technical debt? Because I firmly think that every framework has technical debt. I know that this is a very strong statement, but let’s think again about the debt metaphor. You start incurring debt when you choose a path that is not optimal in order to gain something else, usually velocity. My point is that someone else’s code is not optimal for solving my exact problem. Of course, I’m talking about an ideal world where in every new project, a team has the time to build everything from scratch. I would be naive to think that I will work with plain JavaScript in every project. But I would be equally naive to think that frameworks are free of charge. Every framework has a cost in terms of the difficulty to change the code in the future. The cost is the fact that a framework imposes its architecture on your code. Over time, software needs change, because of the market or other factors, and our architecture should change too. But most of the time, a framework is a roadblock in this sort of change.

Technical Investment

At the beginning of this chapter, I stated that this book is not against frameworks. This statement seems in contrast with the idea that every framework has technical debt. You are probably wondering how frameworks can be a good thing if they always have technical debt. Technical debt is not always a bad thing. In the financial world, debt is not automatically a bad thing. For example, to buy a house, you usually need a loan, which is debt. But people tend to not consider a loan a bad thing, but an investment. On the other hand, if a friend without a stable job wants to go to a bank to get a loan to buy a Ferrari, you will probably try to stop him. The difference is not in the debt itself but in the reason behind the debt.

In software development, there is the same kind of mechanism. If we use a quick solution for a good reason, it is not technical debt, it is a technical investment. An investment is a type of debt, but it is not reckless. And a framework, when chosen for a good reason, is not a cost but an asset. In Chapter 8, I will show you some of the techniques that I use as a consultant to understand if a framework is an asset for a project, and how to choose the framework that “costs” less.

Summary

This chapter defined a “framework” and explained how it is different from a library. I discussed my personal history of JavaScript frameworks, and I pointed out values to the front-end ecosystem. Lastly, you learned the meaning of technical debt and its relationship with frameworks.

In the next chapter, I talk about rendering and the basic principles behind DOM manipulation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.0.159