Learn JavaScript: Novice to Ninja

Chapter 15: Modern JavaScript Development

As you build more and more complex JavaScript projects, you’ll find the amount of code you’re using increases into hundreds and then thousands of lines. This can be difficult to manage without some sort of organizing. The first step is to break the code into separate files, but this presents its own problems, such as how to include all the files on a web page, and which code to put in which files. Indeed, how do you ensure that a file has access to the code in another file?

Just as real-life ninjas have lots of nifty weapons such as nunchakus and shuriken stars, there are lots of cool tools that a JavaScript ninja can use to help organize code and make it run more efficiently. In this chapter, we’ll look at the various frameworks and tools that can be employed to improve the quality of your code. In turn, it will make it more organized and easier to maintain, promoting reuse. We’ll also look at how to make your applications ready for production.

In this chapter, we’ll cover the following topics:

  • Libraries
  • Modular JavaScript
  • MVC frameworks
  • Package managers
  • Optimizing your code with minification
  • Build processes using Webpack
  • Our project ― we’ll separate our code into a modular architecture and prepare for it for deployment.

Libraries

A JavaScript library is a piece of code that provides several methods that make it easier to achieve common tasks. JavaScript is an extremely flexible language that can accomplish most programming tasks – but not all undertakings are as easy to do as they should be. A library will abstract functionality into easier-to-use functions and methods. These can then be used to complete common tasks without having to use lots of repetitive code.

DOM Manipulation Example

A good example of how libraries can help save time is in DOM manipulation. The DOM API provides all the tools required to manipulate the DOM, but some can be verbose and take several lines of code to attain even the most basic of tasks.

For example, if we wanted to add a class to a paragraph element referenced by the variable para , then append another paragraph on the end, we could do it using the following:

para.classList.add('important');const newPara = document.createElement('p');newPara.textContent = 'Another Paragraph';para.appendChild(newPara);

Yet by using the jQuery library, we can achieve the same result using a single line of code:

$(para).addClass('important').append('<p>Another Paragraph</p>');

This shows how using a library can reduce the amount of code you have to write, as well as making common tasks easier to implement. Popular libraries such as jQuery are often well-tested, and help to iron out edge cases and bugs in older browsers that would be difficult to do with your own code. They also unify a number of implementations of the same feature that are different across different browsers. For example, adding event listeners uses a different syntax in some older browsers, but you can use a single method in jQuery that will select the implementation depending on the browser.

jQuery

jQuery is the most popular of all the JavaScript libraries used today, as seen in these statistics onW3TechsandBuilt With.. It is used in a huge number of commercial websites and has a plugin system that makes it easy to extend and use to build common web page elements, such as a lightbox or carousel widget.

jQuery was released in 2006, originally as a DOM manipulation library. It has since grown much bigger, and now provides hundreds of methods for selecting nodes, as well as traversing the DOM, animation effects, Ajax and events. It also has its own testing library:QUnit.

A big advantage of using jQuery is its support for older browsers, particularly Internet Explorer. If you find yourself having to support these browsers then jQuery could save you a huge amount of time and headache. For example, classList isn’t supported in older versions of Internet Explorer, so you’d have to write your own polyfill code to fix it. But if you used jQuery, you’d just have to use the addClass() method and it would make sure the code worked in most browsers.

jQuery is a very powerful and polished library that provides a considerable number of useful methods. It has become so popular that many online tutorials assume you’re using jQuery rather than just JavaScript. You can learn more about jQuery by reading the excellentjQuery: Novice to Ninja: New Kicks and Tricks by Earle Castledine and Craig Sharkie.

Dollar Symbols

The jQuery library uses the $ symbol as a convenient alias for the the global jQuery object that contains all of jQuery’s methods. This prevents the global scope from being polluted with any of jQuery’s methods. The $ symbol has become synonymous with jQuery, and you can confidently expect that any mention of it implies that jQuery is being used.

Underscore & Lodash

UnderscoreandLodashare very similar libraries of functions that provide additional functionality to the language. They both provide a large number of utility functions under the namespace _ (this is where they get their name from as the _ character is often referred to as an ‘underscore’ or a ‘lodash’). A number of JavaScript libraries list one of these as a core dependency.

It’s worth considering using one of these libraries in your projects as it will give you access to a large number of well-tested utility functions that are often required in projects, and particularly geared towards a functional style of programming that was discussed in Chapter 11. Some of these functions have been added in recent versions of ECMAScript (You can read about which ones inthis post on SitePoint), but there are still a large number that will help save you the time of writing your own implementation and effectively reinventing the wheel. A good example can be seen by looking at some of the functions that act on arrays. These provide some very useful functionality that is often required and, some may argue, criminally missing from language itself:

// flatten an array_.flatten([1, [2, [3, [4]], 5]]);<< [1, 2, [3, [4]], 5]// return the last element in an array_.last([1, 2, 3]);<< 3// randomly shuffle an array_.shuffle([1, 2, 3, 4]);// => [4, 1, 3, 2]

Both of these libraries are very similar. In fact, Lodash started life as a fork of Underscore. Since then, they have diverged a little, but still have many similarities. The underlying code, however, has become quite different. Lodash can be thought of as a ‘superset’ of Underscore as it does everything Underscore does, but also adds some extra features. Lodash also has a modular architecture that allows you to selectively use only the functions that you require in your projects, rather than having to include the whole library.

Advantages and Disadvantages of Libraries

A big advantage of utilizing a popular library is that it will be used by lots of people and thoroughly tested. It will most likely have been optimized and battle-tested for nearly every eventuality. Using a library means you can be confident that your code will be as bullet-proof as possible in many browsers. In addition, there will usually be lots of online documentation and a strong community ready to help out if you become stuck. The popularity of libraries often means that others will have encountered the same problem as you, often making it easy to find a solution by searching on the internet.

There are some disadvantages to using libraries, however. You need to include the code for the library as well as your own code. This increases the amount of code that needs to be downloaded by a website, which in some cases can cause performance issues. Thankfully, most modern libraries are relatively small once server-side optimizations are made (such as gzip compression), minimizing any latency issues. Another problem with libraries is that they might fail to implement the functionality in the precise way that you want it to perform. This might not be a problem, but sometimes you’ll have to get your hands dirty and write your own functions in order to achieve the functionality for which you are looking. Using a library can also make your code slower than using plain vanilla JavaScript. This is because there are often more lines of code in using the abstracted functions in a library rather than writing a direct implementation in just JavaScript, which is ‘closer to the metal’, so to speak. These speed differences can be barely noticeable, although there are occasions when using a library is a poor choice for some operations. Using plain JavaScript can be significantly faster than a library, as seen in these examples in thispost by Craig Buckler.

The debate about whether to use a library or not is a big one that stretches back to the start of programming, and refuses to go away. Indeed, there has been a movement towards using plain JavaScript in recent years. Additionally, theVanilla JSwebsite showcases plain JavaScript as if it were a library, highlighting that many tasks can be accomplished with a similar amount of code but much better performance. This is now truer than ever since ES6, as a lot of problems that some libraries addressed have been fixed in the vanilla flavor of JavaScript. The relatively fast uptake and adoption of ES6 means there are less reasons to use a library for some functionality. Having said that, the native implementation of some functions and APIs can often be awkward and difficult to use as well as lacking support in older browsers. This means that libraries will continue to be used by developers as an aid to productivity and consistency.

You only need to look at any professionally produced website to see that some sort of library has been used in its production. Libraries are often the pragmatic choice to complete a project in a realistic time frame, especially when working in a large team. They can also be useful in supporting older browsers and ironing out any browser-specific bugs or quirks, or if performance isn’t the most important factor (when prototyping sites, for example).

When to Use a Library

It can be helpful to use a library, but you should certainly question whether it’s worth the extra work. You have to learn the library’s notation, which can either be similar or very different to standard JavaScript. Every library you use will add to the total file size that’s downloaded so you need to assess whether the extra overhead is worth it. Having said that, browsers will cache it after the first download, and if you use a CDN, the chances are it will already be in the browser’s cache.

It’s also advisable to consider that the popularity of a particular library can be ‘here today, gone tomorrow’. Some of the most popular libraries of the past have fallen out of favor and even discontinued, almost overnight. This can potentially cause problems if you’ve relied on one particular library in most of your projects.

Many libraries have become monolithic, with a plethora of methods that try to do everything. An example of this is jQuery. While it contains a large number of useful methods, it also provides many features that are often unnecessary. jQuery’s modular structure means that you can include only the parts you need on a module-by-module basis. But if you find that you’re not using many of the methods a library offers, you should consider using a lighter alternative that only focuses on solving one problem (some suggestions are given below). And if you’re only using a handful of methods, maybe avoid using a library altogether and try using plain old JavaScript. You could even package together useful functions you’ve created in a module that serves as your own personal library.

Finding Libraries

It is certainly worth considering using a library to make some common tasks easier. There’s a large number of high-quality JavaScript libraries inthis list on Wikipedia, whileMicroJSis a high-quality repository of small JavaScript libraries that focus on specific tasks, andJustis a library of functions that just do one task without depending on any other functions or libraries.

Be careful not to rely on a library and find that you’re learning how to use the library’s methods, rather than the language itself. A library should not be used because of a lack of understanding of JavaScript. Instead, it should be used to speed up JavaScript development by making it easier to complete common tasks. Using a library can sometimes make your code more sloppy. It’s easy, for example, to write short jQuery expressions that look concise but are spectacularly inefficient. And even if you do choose to use a library, remember that a ninja should always be inquisitive as to how things work. In fact, reading a library’s source code is a great way of learning some powerful JavaScript programming techniques.

Modular JavaScript

A module is a self-contained piece of code that provides functions and methods that can then be used in other files and by other modules. This helps to keep code organized in separate, reusable files, which improves code maintainability. The code in a module should have a single purpose, and group together functions with distinct functionality. For example, you might keep any functions used for Ajax in their own module. This could then be used in any projects where Ajax was required. Keeping code modular helps to make it moreloosely coupledand interchangeable, meaning you can easily swap one module for another without affecting other parts of a project. Indeed, small single-purpose modules are the exact opposite of large monolithic libraries as they enable developers to use only the modules that are needed, avoiding any wasted code. Modules also allow a public API to be exposed, while keeping the implementation hidden away inside the module.

Coupling

The coupling of code refers to how dependent certain elements or modules of code are on each other. Two pieces of code are said to be tightly coupled if one relies on the other to run. This often occurs if a piece of code makes hard-coded references to another piece of code, requiring it to be used. This will often mean that changes to one piece of code will necessitate changes in the other. On the other hand, two pieces of code are said to loosely coupled if one piece of code can be easily substituted by another without affecting the final outcome. This is often achieved by referring to common methods that are shared by the alternative modules. For example, you might have a choice of two modules that simplify the process of connecting to a websocket. Both of these modules would likely implement a connect() method, so your code could simply refer to connect() rather than having to explicitly refer to a particular module. This would then allow you change between the two modules without having to change any of the underlying code. It is considered good design to keep code as loosely coupled as possible as this allows for the most flexibility in developing systems of code, as different modules can be used independently and in a variety of different applications, rather than being restricted to a single use-case.

ES6 Modules

For a long time, JavaScript didn’t support modules, but native support for them was finally added in ES6. They allow you to keep parts of code in self-contained files.

There are a few important points about modules that are worth keeping in mind:

  • All code in modules is always in strict mode without the need for ‘use strict’ and there is no way to opt out of this.
  • A module has its own global scope, so any variables created in the top-level of a module can only be accessed within that module.
  • The value of this in the top level of a module is undefined , rather than the global object.
  • You can’t use HTML-style comments in modules (although this isn’t very common in any JavaScript program these days).

A ES6 module file is just a normal JavaScript file, but uses the keyword export to specify any values or functions that are to be made available from the module. This highlights another important fact about modules – not everything in the module needs to be used.

Browser Support

At the time of writing, most major browsers are on the cusp ofsupporting ES6 modules, so you should be able to run these examples straight in your browser. If they don’t work, however, you can use a build process that packages all the modules together into one file. We explain how to do this later in this chapter.

For example, a very simple Pi module would have the following code saved in a file called ‘pi.js’:

export const PI = 3.1415926;

This would then be imported into your main JavaScript file, main.js using the following code:

import { PI } from './pi.js';

This would then allow you to use the variable PI inside the main.js file.

Functions can also be exported from a module. For example, we could create a library for our stats functions that we used earlier:

function square(x) {    return x * x;}function sum(array, callback) {    if(callback) {        array = array.map(callback);    }    return array.reduce((a,b) => a + b );}function variance(array) {    return sum(array,square)/array.length - square(mean(array))}function mean(array) {    return sum(array) / array.length;}export {    variance,    mean}

Notice that an alternative to using export when the function is defined is to add the export directive after the function definition, as seen in the example above with the variance() function.

To import these functions into the main.js file, you’d add this line of code:

import  { mean, variance } from './stats.js';

Now the mean() and variance() functions can be used in the main.js file. Notice that the square() and sum() functions are not available because they were not exported in the module. This effectively makes them private functions of the stats module.

You can be selective in which values or functions to import from the module. For example, if you only wanted to use the mean() function, you could use the following line of code instead:

import  { mean } from './stats.js';

If there are lots of values and functions that need to be imported, then everything in a module file can be imported using the wildcard symbol * along with a namespace for the imported values and functions using the following notation:

import * as stats from './stats.js';

This will then import all the functions from the stats.js module and they’ll be given a namespace of stats . So, the mean function could be used as follows:

stats.mean([2,6,10]);

Default Exports

Default exportsrefer toa singlevariable, function or class in a module that can be imported without having to be explicitly named. The syntax for default exports is purposely easier to read because this is how modules were designed to be used.

The following example demonstrates how this would be done for a variable:

const PI = 3.145926;export default PI;

The next example demonstrates exporting a single default function:

function square(x) {    return x * x;}export default square;

The last example shows how to export an object as the default value:

const stats = {    square(x) {        return x * x;    },        sum(array, callback) {        if(callback) {            array = array.map(callback);        }            return array.reduce((a,b) => a + b );        },    mean(array) {        return this.sum(array) / array.length;    },    variance(array) {        return this.sum(array,this.square)/array.length - this.square(this.mean(array))    }}export default stats;

Don’t Use More Than One Default Export

Having more than one default export will result in a syntax error.

To import these default values, you would use the following code:

import PI from './pi.js';import square from './square.js';import stats from './stats.js';

The big difference with default exports is that you don’t need to use curly braces or make any mention of the value that is being imported, making the statement read more elegantly.

Aliases

The alias that is assigned to the imported module does not have to match its name in the actual module. For example, you could import the square function in the following way:

import sq from './square.js';

The function would then be called using sq() rather than square() :

sq(8)<< 64

Node.js Modules

Node.js had already implemented modules before they were introduced in ES6, and used a slightly different notation calledCommon JS modules. At the time of writing it is proving difficult to merge the two notations in an elegant way, although it is expected that Node.js will support ES6 modules in some way in the future. Despite this, I expect you will continue to see the Common JS module pattern used by Node.js tutorials for a long time to come.

A Common JS module is created in a separate file, and the module.exports method is used to make any functions available to other files, in a similar way to ES6 modules. For example, we could create a module for squaring numbers using the following code inside a file called squareFunction.js:

module.exports = x =>  x * x;

This is simply the square() function we saw earlier in the chapter written as an anonymous function that’s assigned to module.exports as if it was a variable.

To use the module, it needs to then be required inside the another JS file (or from within the Node REPL). This is done using the require() method. This takes the file that contains the module as an argument and returns the function that was exported:

const square = require('./squareFunction');

The function that was exported in the module is now assigned to the variable square , which is then used to call the function in the usual way:

square(6);<< 36

MVC Frameworks

Model-View-Controller (MVC) is a design pattern that’s been used for a long time in server-side languages. It’s a common way of designing software, and used by server-side frameworks such as Ruby On Rails and Django. In recent years it has been used in JavaScript code to make it easier to organize large-scale web applications.

MVC separates an application into three distinct, independent components that interact with each other:

  • Modelsare objects that implement the functionality for creating, reading, updating and deleting (known as CRUD tasks) specific pieces of information about the application, as well as any other associated logic and behavior. In a to-do list application, for example, there would be a task model providing methods to access all the information about the tasks such as names, due dates and completed tasks. This data will often be stored in a database or some other container.
  • Viewsprovide a visual representation of the model showing all the relevant information. In a web application, this would be the HTML displayed on a web page. Views also provide a way for users to interact with an application, usually via forms. In a to-do list application, the views would display the tasks as an HTML list with checkboxes that a user could tick to say a task had been completed.
  • Controllerslink models and views together by communicating between them. They respond to events, which are usually inputs from a user (entering some data into a form, for example), process the information, then update the model and view accordingly. In a to-do list application, the controller functions would respond to the event of a user clicking on a check box and then inform the model that a task had been completed. The model would then update the information about that task.

MV*

It is quite common to see the acronym MV* used to describe JavaScript frameworks, rather than MVC. This is because many JavaScript implementations do not strictly follow the controller pattern. Sometimes controller code is mixed into the views, and sometimes other patterns are used, such as Model-View-Presenter (MVP), Model-View-ViewModel (MVVM), and AngularJS, which calls itself a Model-View-Whatever (MVW) framework. These tend to be only slight variations on the MVC pattern, but for simplicity, MV* is used as a catch-all term. There has been a recent trend for many of these frameworks to embrace a more component-based architecture, which can be found in Angular (from version 2 onwards) and Ember 2.

A Quick List Example

Here’s an example of how the MVC architecture can be implemented using JavaScript. It will be a simple list creator that allows the user to add items to a list by entering them into a form field.

To start, create a folder called MVC and save the following as list.html :

<!doctype html><html lang='en'><head>    <meta charset='utf-8'>    <title>MVC List</title></head><body>    <form id="input">        <label for='name'>Name:</label>        <input type='text' name='name' autofocus required >        <button type='submit'>Submit</button>    </form>    <ul id='list'></ul>    <script src='main.js'></script></body></html>

This is a basic HTML web page containing a form with a single input field for entering a list item. It also contains an empty <ul> element in which to place the list items. Now we need to create the JavaScript file. Create a file called main.js saved in the same folder.

In JavaScript, a model is often represented by a class that can create new instances of an object. This will keep track of any properties the list item has, as well as any methods. In this example, we’ll create an Item class, and use the constructor function to instantiate an Item object with a name property provided as an argument to the constructor function. We also assign a reference to the form to a variable called form . Add this code to main.js :

'use strict'const form = document.forms[0];class Item {    constructor(name) {        this.name = name;    }}

Each new list item that is created will be an instance of the Item class.

Next we’ll create a controller object. This will be responsible for adding an event listener to the form to see when the user adds information. When this happens, it will create a new instance of the model and then render the updated view. Add the following code to main.js :

const controller = {    watch(form) {        form.addEventListener('submit', (event) => {        event.preventDefault(); // prevent the form from being submitted        this.add(form.name.value);        }, false);    },    add(name) {        const item = new Item(name);        view.render(item);    }};

After this, we create a view object with a render() method. This is used to produce an HTML fragment that shows the instance’s name (from the name property stored in the model). It is dynamically inserted into the list using DOM API methods. Add the following code to the main.js file:

const view = {    render(item) {        const list = document.getElementById('list');        const li = document.createElement('li');        li.innerHTML = item.name;        list.appendChild(li);        // reset the input field        form.name.value = '';    }};

Finally, we have to call the watch() method of the controller. This keeps an eye on the form and checks when it is submitted. Add the following line to the end of the main.js file:

controller.watch(form);

Open up list.html in your browser and have a go at adding some items to the list. It should look a little like the screenshot shown below.

An MVC to-do list

This is just a small and simple example of the MVC pattern to give an idea of how it works. In reality, the model would contain many more properties and methods. The controller would also contain more methods for editing and deleting instances of the model. There’s also likely to be more views to display the different states of the model, and there would need to be more robust code used in order for the controller to monitor the changes that may happen in the views. Most MVC implementations also tend to be more generalized in their implementation and avoid hard-coding details about which elements are being updated on the page (such as the reference to the ‘list’ id in the example). Despite this, I hope the example demonstrates how to separate code into the three distinct components of MVC.

Persistence

Most web applications will need some form of persistence to save the information held in the models in order to maintain state. This could be done using the Web Storage API that we covered in the last chapter. Another option that’s often used in real-world applications is to send a JSON representation of the model to a back-end database using Ajax whenever a model changes.

MV* Frameworks

An MVC architecture can take a lot of code to implement, and many frameworks have emerged that take care of much of the setup code for you. One of the main features of MVC frameworks is data binding, which is the process of linking the model and view together. As a result, a large amount of boilerplate controller code is not needed as the framework takes care of it all in the background for you. One-way data binding is when a change in the model will automatically result in the view being updated. And two-way data binding is when a change in the view automatically updates the model.

The views are simply web pages written in HTML, although it is common to use templating languages so dynamic data can be inserted into the page (more about these in the section that follows):

  • Aureliais a modern framework that uses ES6, and focuses on letting you write dynamic web applications while keeping the implementation details in the background.
  • Angularis a powerful framework by Google to make creating dynamic web applications easier. This is done by extending the HTML language using custom ng- attributes.
  • Emberis a framework designed to make building large web applications easier. It does this by using common conventions that avoid the need for lots of set-up code, though it can become more difficult if you don’t follow these conventions.

The websiteTodoMVChas lots of examples of to-do list applications written in many of the popular MVC frameworks.

Templates

Many MVC frameworks use templating languages to insert dynamic data into the page. Templates can be written in HTML or another language, such as markdown, which compiles into HTML. They can be whole web pages, but are often just partials — parts of a page. This means that the application can update part of the page without having to make a request to the server, saving an HTTP request. This is usually done by dynamically inserting the fragment of HTML into the DOM.

Templating languages allow HTML to be separated from the JavaScript program, making maintenance easier because they’re no longer tightly coupled. The templates are often stored in separate files or inside their own script tags, so they can be reused and quickly edited in one place if changes need to be made. It also means that inserting large strings of HTML into a document (which can have adverse effects on performance) is avoided. All that’s needed is a reference to the relevant file that contains the template.

Templating languages often have a mechanism for inserting dynamic data into the HTML. These tend to fall into two camps: placing dynamic code inside curly braces (the ‘mustache’ symbol) or inside the special <% %> tags made popular by Embedded Ruby (ERB).

For example,Mustache,PugandHandlebarswould use this to insert the value of the variable name into a heading tag:

<h1>Hello {{ name }}</h1>

EJS, on the other hand, would use the following to achieve the same result:

<h1>Hello <%= name %></h1>

Templating languages also enable you to insert basic programming logic into views, allowing you to conditionally show different messages or use loops to show multiple pieces of similar code.

For example, say we wanted to display the following array of to-do objects:

const tasks = [    { name: 'Get Milk' },    { name: 'Go for a run' },    { name: 'Finish writing last chapter' },    { name: 'Phone bank' },    { name: 'Email Craig' }    ]

Mustache implements ‘logic-less’ templates that don’t require any lines of logic to be explicitly written in JavaScript; instead, it is inferred from the context. This is how it would iterate over the task array to display a list of tasks:

<ul>{{#tasks}}<li>{{name}}</li>{{/task}}</ul>

EJS uses more explicit JavaScript coding to achieve the same result. Each line of JavaScript code is placed inside the special <% %> tags. If any values need to be evaluated, they are placed inside <%= %> instead:

<ul>    <% tasks.forEach(function(task) { %>    <li><%= task.name %></li>    <% }); %>    </ul>    <% } %>>

Both of these templates would return this HTML code:

<ul>    <li>Get Milk</li>    <li>Go for a run</li>    <li>Finish writing last chapter</li>    <li>Phone bank</li>    <li>Email Craig</li>    </ul>

There are a number of popular templating languages to choose from, a selection of some of the most popular are shown in the list below:

Web Components

The W3C are working on developing a standard calledWeb Componentsthat attempts to extend HTML and introduce new features such as templates, custom tags, the ability to import HTML partials, and a shadow DOM. The idea is to use it to develop modular and self-contained components that can be reused in different applications. The proposal is currently a living standard. ThePolymer Projectis a JavaScript library that attempts to implement web components.

View Libraries

Over the past few years, a number of libraries have sprung up that deal with just the view layer of MVC. They have the single goal of making it easier to update the user interface while also keeping what is displayed synchronized with underlying data that it’s representing. This means they can be integrated into a project and used either on their own or alongside other libraries that deal with other aspects of the application. The two view libraries that have emerged as the most popular so far are React and Vue.js.

Reactis developed by Facebook and it has been hugely successful; quickly becoming one of the most popular JavaScript libraries that’s used by a large number of popular websites such as the BBC, PayPal and Netflix.Vue.jswas developed by Evan You after he worked on AngularJS at Google. His idea was to design a lightweight view library that built on all the good things about AngularJS. It has become a very popular library (second only to React in terms of GitHub stars) and is starting to be adopted by a number of large websites.

Both React and Vue.js have changed the way developers think about the way user interfaces are built; particularly the way in which JavaScript is used. They have a number of similarities, but also solve the problems in very different ways.

React and Vue.js both use the concept of avirtual DOMto speed up the process of updating HTML pages. Instead of directly changing the DOM, as we did in Chapter 6, a virtual DOM is updated in memory. The real DOM is only updated when it needs to be and this is done by comparing any differences between the virtual DOM and actual DOM and only updating those elements that have actually changed. This is a process known asreconciliationand it’s what makes React and Vue extremely fast at rendering web pages.

They also use the concept of components to represent visual elements. This means you can create multiple components that can then be reused in other projects as well as being combined to make larger components. Each component is entirely self-contained, with all the HTML, CSS and JavaScript being defined inside the component itself.

The main differences between React and Vue.js are to do with their respective implementations. React uses its own language called JSX. This allows HTML and JavaScript to be mixed together in an elegant and concise way. The result is transformed into JavaScript that is then used to produce the HTML views. Vue.js uses HTML-like templates that can contain logic and directives along with JavaScript objects to store any data.

There has been a certain amount of controversy created by view libraries such as React and Vue.js regarding the mixing of HTML and JavaScript and the use of inline JS and CSS in HTML files. This directly contradicts the ‘Three Layers of the Web’ separation of concerns principle that was discussed all the way back in Chapter 1, and for a long time represented the gold-standard, best practice of web development. Many people have now started to feel that this is an outdated belief that worked forweb pagesthat are just documents that use JavaScript to add extra functionality to the top. React and Vue.js were designed primarily for building dynamicweb applicationswhere the line between presentation and logic becomes much more blurred, since you’re dealing with interactive elements. The stance that many React and Vue.js developers take is that everything to do with those interactive components should be encapsulated together in the component: presentation, markup and logic. This is an argument that looks like it will rage on for a long time though!

React and Vue.js aren’t the only view libraries available. There are a number of alternatives that have a similar goal of managing the view layer and keeping data synchronized, but go about it in a slightly different way. Some focus on improving the rendering speed, while others use different notations.

Some popular alternatives are:

View libraries are worth considering if you find your user interface is starting to become more complicated or has to react quickly to changes in data. They also make it easier to build more modular user interfaces with reusable components that can be combined together in different ways. There are a number of options available, and it’s worth experimenting with a few of them in order to find one that fits your needs.

Package Managers

As modules have become more widely used in JavaScript, there’s been a need for tools to manage them. Package managers allow developers to download modules from a central repository. Apackageis just a directory with one or more files of code, as well as a package.json file that contains information about the code in the package. Developers can also share any modules they create by uploading them to the central repository for others to use. A package manager makes it easy to install, upgrade and remove any packages that are used in a project. They also allow you to manage different versions of packages.

When you start to use more modules, you’ll find that some of them depend on other modules to work. These are known asdependencies. Dependency management is the process of ensuring that all the dependencies a module requires are met. This can be difficult to do manually once a project becomes large, so package managers can also help with dependency management.

npm

The Node Package Manager was developed soon after Node.js was introduced to allow JavaScript code to be shared by developers. It has since grown into the largest repository of code libraries available.

npm allows you to install JavaScript packages onto your machine. Some packages rely on other packages, so npm acts as adependency manager. For example, if you use npm to install a package, it will also install all the packages that the original package depends on as well.

If you followed the instructions to install Node.js all the way back in chapter 1 then you’ll already have npm installed. If you didn’t, you’ll need to go back and do that before moving on, if you want to follow the examples in this section.

Searching for Packages

The npm repository is huge – it contains almost half a million different packages, ranging from popular libraries, such as jQuery and Lodash, to tiny modules that are literally a single line of code.

To find a package, you can use the search command. For example, if you wanted to find a package for testing, you could use the command npm search test . This will return a list of packages, including a description, the author and the date they were last updated.

You can also search for a package on thenpm website.

 npm init

The npm init command is used when you start a project. It walks you through creating a package.json file. This is a file that contains information about a JavaScript project in JSON format. This includes meta-information, such as the name and version number. It also includes information about any dependencies the project has, and can be used to specify how tests will be run.

To try it out, create a new directory called ‘project-x’ and navigate to it in a terminal. Then enter the following command:

npm init

You’ll then be walked through a number of questions about the project. Enter ‘project-x’ as the name, then you can just accept the default suggestions for all the other questions by simply pressing ENTER. This should result in a file called package.json saved in the root of the directory, with contents similar to those shown below:

{"name": "project-x","version": "1.0.0","description": "","main": "index.js","scripts": {    "test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC"}

This file will be updated by npm when you use it to install packages, so it keeps track of any packages used by the project and which dependencies it has. You can also edit it manually to specify any information about the project that might be required by other tools.

Installing Packages Locally

By default, any packages that are installed by npm are only installed in the current directory. They are all saved in a folder called ‘node_modules’ in the root of the project directory. Any files in here can then be imported into your main JavaScript file and used.

To install a package locally, the install command is used. Adding the --save flag will ensure the package.json file is updated with the relevant information about the package.

For example, if we wanted to install the Lodash library we met in Chapter 11, we’d use the following command, from inside our project directory:

npm install --save lodash

Now, if you take a look inside the package.json file, you’ll see that the following properties have been added:

"dependencies": {    "lodash": "^4.17.4"}

This states that the lodash package is now a dependency of this project. The value of “^4.14.4” refers to the version of Lodash that has been installed. This usessemantic versioningwhere the first number refers to the major version number, the second is the minor version number, and the third number is for patches. The caret symbol ( ^ ) means that when npm is installing the package, it install the latest version that matches the major version number only.

Dependencies

When you install a package using npm it will become adependencyof your project by default. This will get listed under “dependencies” in the package.json file as we saw in the example above. If a package is a dependency, it means the application requires it to run.

Alternatively you can install a package as a devDependency . These are packages that aren’t required by the application itself but are used in development to complete tasks to do with the application. Examples of these could be running tests or transpiling the code. Any packages installed as a devDependency will not be packaged in the final build of an application that is deployed.

To install a package as a devDependency , you need to add the --save-dev flag, like so:

npm install --save-dev jest

This will install the Jest testing framework that we met in chapter 10 and add the following lines to the package.json’ file:

"devDependencies": {    "jest": "^20.0.4"}

You can also install a specific version of a package by placing the @ symbol after the package name, followed by the version you require. For example, you could install version 18.1.0 of Jest, using the following command:

npm install --save-dev jest@18.1.0

Which Version?

It is usually best to just install the latest version of a package, although there may be times when you need to install a specific version of a package so it’s compatible with other packages.

The package.json File

The package.json file is used to manage any packages that are installed locally. It keeps track on which packages are installed, and what dependencies they rely on. It also allows you to specify specific versions of packages to use.

All package.json require the following pieces of information:

  • “name” – This must be all in lowercase and contain no spaces.
  • “version” – This uses semantic versioning of the form major.minor.patch.

You can create a package.json file inside a directory by running the npm init command. This will ask you some questions that will be used to populate the file. If you don’t want to go through the process of answering all the questions, you can use the --yes or -y flag to bypass the questions and use the default options:

npm install --yes

You can also set most of the default options using npm set command:

npm set init.author.email "daz@sitepoint.com"npm set init.author.name "DAZ"npm set init.license "MIT"

README

Most projects will have a file in the root directory called ‘README’. This files serves as the application’s documentation and contains information about it. If you leave the description field blank, then npm will use the first line of the README file as the description.

One of the most powerful features of the package.json file is that it contains all the information about a project’s dependencies. So if you clone somebody else’s project, all the information about the packages it requires are contained inside the package.json file. npm also makes it trivially easy to then install all the packages that are required to run the application by simply entering the following command from inside the root directory of the project:

npm install

This will then download and install all the necessary packages, as well as any dependencies.

Version 5 of npm introduced the ‘package-lock.json’ file, which is automatically created when any changes are made to the ‘node_modules’ directory or the package.json file. This acts as an exact snapshot of the packages used in a project at any single point in time, and helps avoid problems that were caused when a package updated and conflicted with the latest versions of some of its dependencies.

Installing Globally

It is also possible to install packages globally so they’re available system-wide from any directory on your machine. While this might sound like a good idea at first, it isn’t usually recommended, as the package files are not saved with the project (they are installed in the system folders on your machine) and there will be no reference to the package as a dependency in the package.json file.

There are some cases, however, when it makes sense to install a package globally. This is usually when the package is to be used on a system-wide basis, rather than just for a specific project.

To install a package globally, the global flag needs to be added to the install command:

npm install --global lodash

There are also some cases where it makes sense to have a global install as well as a local install. Lodash is a good example of this. You might want it available on your system so you can have quick access to the library when you’re experimenting with code in the Node REPL, but you might also want it as a specific dependency in a web application you build. If you didn’t install it locally, this would mean the code would work fine on your machine (because you have a global install), but it wouldn’t work on a machine that didn’t have a global install of Lodash. The easiest solution to this is to install the package both globally and locally. There is nothing wrong with this, it just means the files exist in two (or more) different places on your machine.

Permissions

You need to be careful about permissions when installing packages globally. When you install locally, you are installing packages to a directory that the current user owns and has permission to alter. When you install globally you are sometimes required to install the package into a system directory that you don’t have permission to access. This means you often need to sign in as a ‘super user’ to install them. There are some security issues with doing this, and there can also be permission issues if packages try to alter or write to any files.

Three ways to fix this problem are explained on thenpm website.

Listing Installed Packages

You can list all the packages that have been installed locally using the list command:

npm list

This will often produce a much longer list than expected, as it also lists the packages that have also been installed as dependencies. The list is presented as a tree that shows the dependency hierarchy.

If you only want to see the packages that you have installed directly, you can use the depth flag, with a value of 0 :

npm list --depth=0

This will only list the direct dependencies of the application (including devDependencies ).

You can find out which packages have been installed globally by adding the --global or -g flag:

npm list --global

Updating A Package

You can find out if any of the packages you have installed have been updated by running the following command:

npm outdated

This will display a list of any packages that are listed in package.json and have a more recent version available than what’s currently installed.

It’s worth using this command every now and then to check if any updates are available, as it is often beneficial to keep the packages up to date.

npm makes it easy to update a package to the latest version using the update command. For example, you can update to the latest version of Jest using the following command:

npm update jest

You can updateallthe packages in the directory by simply entering the following command:

npm update

To find out if any packages that have been installed globally are out of date, run the following command:

npm outdated -g

If any global packages need updating, this is done using the update command, but you need to add the --global or -g flag:

npm update --global

Uninstalling Packages

You can use npm to uninstall a package using the uninstall command:

npm uninstall lodash

This will remove the package from the ‘node_modules’ directory.

Use npm To Remove Packages

You can easily remove a package that has been installed locally by simply deleting its directory from the ‘node_modules’ directory, but it is preferable to use npm as this will also take care of removing any redundant dependencies as well as updating the package.json file to reflect the changes.

To remove it from the dependencies in package.json, you will need to use the save flag:

npm uninstall --save lodash

If it was installed as a devDependency , you will need to add the --save-dev flag to remove it from the package.json file:

npm uninstall --save-dev jest

Global packages can be uninstalled by adding the --global or -g to the uninstall command:

npm uninstall --global lodash

Aliases

npm uses a number of aliases for common commands that can be used to cut down on a few keystrokes.

 i is an alias for install  un is an alias for uninstall  up is an alias for update  ls is an alias for list

You can find out more about npm in thisdetailed post on SitePoint.

Yarn

Yarnhas emerged recently as a popular alternative to npm. It was developed to try and get around some problems that npm had with the speed and consistency of installing packages, as well as some security concerns to do with npm allowing packages to run on installation.

Yarn generally installs packages much faster than npm due to the fact that it caches packages. It has a slightly different set of commands, but generally works in the same way as npm. A comparison between Yarn and npm can be seen inthis post on SitePoint.

Content Delivery Networks

Content delivery networks(CDNs) are systems of distributed servers that can deliver web resources, such as JavaScript, CSS or image files to a user based on their geographic location. This means they’re able to deliver the content quickly and efficiently with a high degree of availability. These resources also get cached on the user’s machine, meaning that less requests need to be made to download the resource. This is particularly beneficial when using a CDN for a popular library such as jQuery. This is because once the user has downloaded the file from a site, it will be cached, and can then be used again by other sites without the need for another network request.

unpkgis a globalcontent delivery network(or CDN) that allows you to include any package from npm in a web page without having to install it on your own machine. Using a CDN, such as unpkg, is a good way to quickly add a npm package to your project, especially if you just want to test it out. But using this approach has some drawbacks. Some libraries, such as React, put the burden of processing onto the browser, rather than when the code is transpiled. This is fine when experimenting, but in a production environment, this would take too much toll on the browser, and result in a sub-optimal experience for the user. For this reason, you will eventually need to consider using a build tool such as Webpack to take care of managing any external libraries that you use, and for that reason, this is covered later in the chapter.

Deploying JavaScript

When it comes to deploying your JavaScript program, it’s time to think about optimizing the code. If you’ve used multiple external libraries and lots of modules, you might have a large number of files that need to be included in your HTML file. One way of doing this is to simply include a different <script> tag for each JavaScript file. However, this is not optimal for a number of reasons:

  • The scripts must be included in the correct order.
  • Each file represents a separate request to the server.
  • The files will be large.

The solution is to combine all the scripts into a single minified and compressed file. This file is often named ‘bundle.min.js’ to signify that it’s a number of files bundled together and has also been minified. Once you’ve combined all the files into a single file, and minified and compressed it, the next step is to add it to the HTML file. The optimal position for the <script> tag is right at the end of the page, just before the closing <body> tag, which we have used in all our examples:

<!doctype html><html lang='en'><head>    <meta charset='utf-8'>    <title>Ninja JavaScript</title></head><body>    ...    <script src='bundle.min.js'></script></body></html>

This will ensure the page has finished loading before the JavaScript code is processed.

Transpiling Code

Code transpilers take JavaScript code and turn it into JavaScript code! That might sound strange but it’s all to do with the fact that browsers struggle to keep up with the fast-moving pace of ECMAScript versions. A code transpiler allows you to write your code in the latest version of ECMAScript, using all the latest features, then convert it into an older variant of JavaScript that is compatible with most browsers.

The most common use of transpilers recently has been to allow developers to write in ES6 code then transpile it into ECMAScript 5, which was widely supported by most browsers. This has helped to facilitate the swift adoption of ES6 code, and meant it could be used in production code and tutorials. This, in turn, probably contributed to browser vendors implementing ES6 features into their browser engines quickly.

Most ES6 features have now been implemented in the most up-to-date browsers, but this probably won’t make transpilers redundant. Developers will always want to use the most up-to-date language features, and browser engines will always be slightly behind the curve (although browser vendors are gettingmuchfaster at implementing features these days). It also means you can ensure your deployed code will still work on any older browsers that some of your audience might still be using.

Babelis the most popular transpiler for converting the most up-to-date version of ECMAScript into an older flavor of JavaScript.

There are also a number of transpilers that allow you to write in a different language then compile it into JavaScript. This is useful if you prefer using an alternative coding style – for example, CoffeeScript, which has many similarities to the Ruby language. Some more examples are listed below:

Minification

Minification is the process of removing any unnecessary characters from your code to reduce the file size. This includes all comments, whitespace, and other characters that are superfluous.

Tools are available to do this, known asminifiers. Some popular choices include:

These tools can also change variable and function names to single letters. This is often referred to as code obfuscation as it can make the code more difficult to read. They will also try to employ optimizations to make the code run faster. For example, here is the myMaths object that we created at the start of this chapter after it has been converted to ES5 and then minified using UglifyJS:

var myMaths={square:function(n){return n*n},sum:function(n,t){return t&&(n=n.map(t)),n.reduce(function(n,t){return n+t})},mean:function(n){return this.sum(n)/n.length},variance:function(n){return this.sum(n,this.square)/n.length-this.square(this.mean(n))}};

As you can see, it is significantly smaller in size, but much more difficult to read and make sense of!

Minifying your code can have a major effect on the overall size of your files, making them download and run faster. It also means that your code can use descriptive naming conventions and be well-commented, as these will be stripped away by the minifier tools. As a general rule, you should aim to use well-commented and descriptive code in development and minified code in production (since there’s no need for end users to read comments).

Files can also be compressed on the server using a file-compression tool such as gzip, which can have a dramatic effect reducing the file size. Using both minification and compression in production means that JavaScript files are a mere fraction of their original size, making them much quicker to download.

Folder Structure

As you application gets larger, you will need to start thinking about having a more structured approach to organizing your files. You can organize your files in any way you like, but there are some conventions that are often used for directory names that are worth knowing about.

Root Files

In the root of any folder structure you will find a file called index.html . This is traditionally the page that a web browser will load by default, although it is possible to instruct it to loadanyfile by programming how the server responds to requests.

For small projects, such as most of the examples in this book, it is usually okay to putallthe JavaScript into a single file in the root directory. It is usually called main.js , ‘index.js’, ‘app.js’ or something similar. As a project gets bigger, you’ll find that it helps to separate different parts of functionality into different modules then use this file as anentry-pointfor loading any modules. You might also want to move all of your JavaScript into a separate folder, called ‘js’, ‘scripts’ or something similar.

In a similar way, it is customary to have a single CSS file in the root directory or keep multiple CSS files saved in a folder called ‘CSS’, ‘stylesheets’ or similar.

src folder

Code that needs to be transpiled is often placed inside a folder called src (short for ‘source’). The transpiler can then compile the JavaScript code into a single file in a different location (usually this is either the root directory or the ‘dist’ folder).

dist folder

The output of any transpiled code from the src folder is usually placed inside a folder called ‘dist’ (short for ‘distribution’). This is the only part of the application code that is actually distributed when it is deployed. Even though there might be many different files inside the src folder, the result of transpilation means there is usually only a single JavaScript file inside the ‘dist’ folder, usually called budle.js or ‘bundle.min.js’ if it has also been minified.

lib folder

Any modules are usually stored in a folder called lib (short for ‘library’) that is usually placed inside the src folder. Third-party libraries are often placed inside a folder called vendor , although they are sometimes also placed inside the lib folder.

Third-Party Code

Any third-party code installed using npm will be installed into a directory called node_modules . This can be configured to use a different folder name, but for consistency it is best to stick to this convention.

If you only use a small number of modules, you can just keep them all in the root of the lib directory, but as the size of your application grows, you might need to start thinking about how to organize the modules into separate folders.

Organization By Type

This structure has folders for the different types of file that will be used in your project such as Javascript, CSS, Images and Tests. All your JavaScript files then go in the JS folder, all your stylesheet files go in the CSS folder, all your HTML files go in the views folder etc.

A typical folder structure based on type might look like the following:

  • src
    • CSS
    • JS
    • views
    • tests
  • dist
    • index.html
    • bundle.min.js
  • package.json
  • node_modules
  • README

Modular Organization

This structure groups files byfeaturerather thantype. So instead of placing all the JavaScript files in one place (based on type), we would put all the files associated with a certain feature together in a single folder. So all the JavaScript files, stylesheets, views and tests associated with ‘feature X’ would go in a folder called featureX , for example.

There are a number of advantages to this approach, particularly in big projects:

  • It keeps all the code related to that particular feature all in one place, making it easy to find.
  • It’s extensible – modules can grow to incorporate their own file structure and sub-directories as required.
  • It allows developers to work on a specific feature without affecting another feature. This is particular useful when you’re working in teams.
  • The modular nature of this approach makes it easy to switch one feature for another, without affecting the rest of the project.
  • Each module is self-contained, making them easy to test.

A typical modular folder structure might look like the following:

  • src
    • index.html
    • lib
      • app
        • app.js
        • app.css
        • app.test.js
          • moduleA
        • moduleA.html
        • moduleA.js
        • moduleA.css
        • moduleA.test.js
          • moduleB
        • moduleB.html
        • moduleB.js
        • moduleB.css
        • moduleB.test.js
  • dist
    • index.html
    • bundle.min.js
  • package.json
  • node_modules
  • README

Note that the app folder is forsharedresources that are used by the main application itself.

Note also that inside each module’s directory, you might begin to have sub-directories for JavaScript, CSS and tests rather than single files, if the modules start to become large themselves.

Start Small

Personally I would recommend that you start any project with just the root files: index.htmlmain.js and main.css .

As the project starts to grow, you could progress using an ‘application’ folder to place the application specific JavaScript and CSS code, as well as any tests and HTML views.

If you start to transpile your code, you should consider keeping your original code in a src directory and transpiling the code into a ‘dist’ directory.

If the project grows further still, you might begin to separate the code into separate modules that require their own folders, particularly if the project is being worked on by a team of developers.

The important thing when choosing a directory structure is that it works for you and remains consistent. Also remember that your code structure will have to fit in with any third-party tools that you choose to use. Most tools will allow you to configure the directory structure to suit your needs, although sometimes it’s easier to stick to the conventions that they used.

Webpack

Webpack is a module bundler that takes all the JavaScript files used in your project and bundles them altogether in one file.

Webpack also uses the concept of ‘loaders’, which allows you to perform tasks such as:

  • Transpiling code
  • Minifying code
  • Linting code
  • Testing code
  • Compiling CSS preprocessor files (such as Sass, Less or Stylus) into standard CSS files.

Webpack’s loaders also let you compile multiple CSS and image files into a single destination folder. It’s also possible to write your own loader file to automate a specific task.

Webpack is an important tool for managing a modern JavaScript project and getting it ready for deployment. We’re going to have a look at how to install Webpack, as well as a few examples of how to use it. We’ll only be focusing on bundling JavaScript files in these examples, but you can see how to do the same with your CSS by checking out theWebpack documentation.

A Webpack Example

To get started, we need to create a new folder called webpack-example , then navigate to that folder in the terminal and create a package.json file:

npm init -y

Next, we use npm to install Webpack. Since it’s only used as part of the development process, we use the --save-dev flag:

npm install --save-dev webpack

Webpack should now be added to the devdependencies property in the package.json file.

Webpack basically bundles files together into an output file. To test it out, we’re going to try and use our installation of Lodash. If you didn’t install Lodash earlier, do it now, using the following command:

npm install lodash --save

Now create a simple HTML file called webpack.html that includes the following code:

<!doctype html><html lang='en'><head>    <meta charset='utf-8'>    <title>Webpack Example</title></head><body>    <script src='bundle.js'></script></body></html>

This page doesn’t really do anything, although it refers to a file called budle.js . This is going to be created by Webpack. It is convention to call the output file budle.js as it’s all the JavaScript files bundled together as a single file.

Next we need to create a JavaScript file called main.js that contains the following code:

import _ from 'lodash';console.log(_.now());

This uses the ES6 module syntax to import the Lodash library, and assigns it to the variable _ , which acts as a namespace for the library (you could assign it to any variable name, but the _ symbol is the convention, due to the name of the library). We then call the now() from Lodash and use console.log() to display the result in the console. The now() method simply returns the number of seconds since the Epoch.

On its own, this code won’t work, but we can use Webpack to sort out importing the Lodash library from the ‘node_modules’ directory. To do this, we simply need to run the following command in the terminal:

./node_modules/.bin/webpack main.js bundle.js

The first argument is the file that contains our code (‘main.js), and the second argument is the file that we want the output to be written to (bundle.js).

After you run this code, you should notice that a new file called budle.js has been created. Now, if you open ‘webpack.html’ and open the console, it should display a number that represents the number of seconds since the epoch (at the time of writing, it was around 1,500,000,000,000). This means the Lodash library has been successfully imported into our main.js file and can now be used.

To make things simpler, going forward, we can create a webpack configuration file called ‘webpack.config.js’. This is actually just a standard JavaScript file and should be saved in the root of the directory and contain the following code:

module.exports = {    entry: './main.js',    output: {        filename: 'bundle.js',        path: __dirname    }};

This can be run using the following line of code in the terminal:

./node_modules/.bin/webpack

We can use npm to simplify this process. Update the “scripts” property in your package.json file to the following:

"scripts": {    "test": "echo \"Error: no test specified\" && exit 1",    "build": "webpack"}

Now you can run Webpack using the following command:

npm run build

Running Other Processes, Too

The technique above can be used to run other processes, by adding them as properties of the “scripts” object. npm’s run command can then be used to run the code specified.

The property name “build” is arbitrary (although it is the convention), and it could be named anything … even “webpack”.

Quiz Ninja Project

We’ll now put some of the ideas we’ve learned in this chapter into practice in our quiz project. First of all we’ll move our code into separate modules. We’ll create a module for the view object, another for the game object and one more for our utility functions, random() and shuffle() . We’ll also update our file structure to keep all our source files in a src folder then use Webpack to compile them all together into a ‘dist’ folder.

To get started, create a folder called ‘quiz’ (or something similar), navigate to it in the terminal and enter the following code:

npm init

Answer all the questions to create a package.json file that is similar to the one below.

{    "name": "quiz-ninja",    "version": "1.0.0",    "description": "A JavaScript quiz.",    "main": "main.js",    "scripts": {        "test": "echo \"Error: no test specified\" && exit 1"    },    "keywords": [        "quiz",        "ninja",        "javascript"    ],    "author": "DAZ",    "license": "MIT"}

Now we need to create our directory structure. Create a folder called ‘dist’ at the root of the directory and copy the index.html and styles.css files inside it.

A Simulated Example

The ‘dist’ directory is normally only used for files that have been compiled from the src directory. In reality you would probably use some sort of pre-processors to create your HTML and CSS files. In this example, we’re just going to pretend that this has happened and index.html and styles.css have been compiled into the ‘dist’ directory.

We will also need to make a small update to the index.html file so that it loads a JavaScript file called ‘bundle.min.js’ in the ‘dist’ directory, which is the file that Webpack will build:

<!doctype html><html lang='en'><head>    <meta charset='utf-8'>    <meta name='description' content='A JavaScript Quiz Game'>    <title>Quiz Ninja</title>    <link rel='stylesheet' href='styles.css'></head><body>    <section class='dojo'>        <div class='quiz-body'>        <header>            <div id='timer'>Time: <strong>20</strong></div>            <div id='score'>Score: <strong>0</strong></div>            <div id='hiScore'>High Score: <strong></strong></div>            <h1>Quiz Ninja!</h1>        </header>        <div id='question'></div>        <div id='response'></div>        <div id='result'></div>        <div id='info'></div>        <button id='start'>Click to Start</button>        </div>    </section>    <script src='bundle.min.js'></script></body></html>

Now we’re going to create our JavaScript modules in the src directory. Create src folder in the root directory and save the following in a file called ‘utilities.js’:

function random(a,b=1) {// if only 1 argument is provided, we need to swap the values of a and b    if (b === 1) {        [a,b] = [b,a];    }    return Math.floor((b-a+1) * Math.random()) + a;}function shuffle(array) {    for (let i = array.length; i; i--) {        let j = random(i)-1;        [array[i - 1], array[j]] = [array[j], array[i - 1]];    }}export {    random,    shuffle}

This contains our utility functions random() and shuffle() that we will use. Separating them into their own module is a good move as it will make it easier to update and use them in future projects.

The next module will include the code for the quiz itself. Save the following code in ‘quiz.js’:

import { random, shuffle } from './utilities.js';const view = {    score: document.querySelector('#score strong'),    question: document.querySelector('#question'),    result: document.querySelector('#result'),    info: document.querySelector('#info'),    start: document.querySelector('#start'),    response: document.querySelector('#response'),    timer: document.querySelector('#timer strong'),    hiScore: document.querySelector('#hiScore strong'),    render(target,content,attributes) {        for(const key in attributes) {            target.setAttribute(key, attributes[key]);        }        target.innerHTML = content;    },    show(element){        element.style.display = 'block';    },    hide(element){        element.style.display = 'none';    },    setup(){        this.show(this.question);        this.show(this.response);        this.show(this.result);        this.hide(this.start);        this.render(this.score,game.score);        this.render(this.result,'');        this.render(this.info,'');        this.render(this.hiScore, game.hiScore());    },    teardown(){        this.hide(this.question);        this.hide(this.response);        this.show(this.start);        this.render(this.hiScore, game.hiScore());    },    buttons(array){        return array.map(value => `<button>${value}</button>`).join('');    }};const game = {    start(quiz){        console.log('start() invoked');        this.score = 0;        this.questions = [...quiz];        view.setup();        this.secondsRemaining = 20;        this.timer = setInterval( this.countdown , 1000 );        this.ask();    },    countdown() {        game.secondsRemaining--;        view.render(view.timer,game.secondsRemaining);        if(game.secondsRemaining <= 0) {            game.gameOver();        }    },    ask(name){        console.log('ask() invoked');        if(this.questions.length > 2) {        shuffle(this.questions);        this.question = this.questions.pop();        const options = [this.questions[0].realName, this.questions[1].realName, this.question.realName];        shuffle(options);        const question = `What is ${this.question.name}'s real name?`;        view.render(view.question,question);        view.render(view.response,view.buttons(options));        }        else {        this.gameOver();        }    },    check(event){        console.log('check(event) invoked')        const response = event.target.textContent;        const answer = this.question.realName;        if(response === answer){        console.log('correct');        view.render(view.result,'Correct!',{'class':'correct'});        this.score++;        view.render(view.score,this.score);        } else {        console.log('wrong');        view.render(view.result,`Wrong! The correct answer was ${answer}`,{'class':'wrong'});        }        this.ask();    },    gameOver(){        console.log('gameOver() invoked')        view.render(view.info,`Game Over, you scored ${this.score} point${this.score !== 1 ? 's' : ''}`);        view.teardown();        clearInterval(this.timer);    },    hiScore(){        const hi = localStorage.getItem('highScore') || 0;        if(this.score > hi || hi === 0) localStorage.setItem('highScore',this.score);        return localStorage.getItem('highScore');    }};export {    view,    game}

This is the same code for the view and game objects that we used previously in main.js , but made into a module by using the export declaration at the end. We also imported the ‘utilities.js’ module at the start, which allows us to use the random() and shuffle() functions in this module.

Now that we have our modules in place, we need to update main.js to import them:

import { view, game } from './quiz.js';const url = 'http://spbooks.github.io/questions.json';fetch(url).then(res => res.json()).then(quiz => {    view.start.addEventListener('click', () => game.start(quiz.questions), false);    view.response.addEventListener('click', (event) => game.check(event), false);});

This file will serve as an ‘entry’ point, in that Webpack will look at this file and determine which modules to load.

Next, we’ll use Webpack to prepare our code for deployment. This will involve transpiling our code into ES5 using Babel, and minifying the code using theBabili plugin. The minified code will then be placed inside a single file in the ‘dist’ directory.

To do this, we need to install Webpack locally, as well as Babel, some Babel modules and the Babili plugin:

npm install --save-dev webpack babel-core babel-loader babel-preset-env babili-webpack-plugin

We use the --save-dev flag to install these modules as all of them are only used in development.

Next, we need to configure WebPack. Create a file called webpack.config.js in the root directory and add the following code:

const webpack = require('webpack');const BabiliPlugin = require("babili-webpack-plugin");module.exports = {    context: __dirname + '/src',    entry: './main.js',    output: {        path: __dirname + '/dist',        filename: 'bundle.min.js'},module: {    rules: [    {        test: /\.js$/,        exclude: /node_modules/,        use: {        loader: 'babel-loader',        options: {            presets: ['env' ]        }        }    }    ]},plugins: [    new BabiliPlugin(),    new webpack.DefinePlugin({    'process.env': {    'NODE_ENV': JSON.stringify('production')    }})    ]};

The context and entry properties tell WebPack to take the code found in ‘/src/main.js’, and the output property tells it to place the transpiled code in ‘/dist/bundle.min.js’. The rules property contains the standard rules to transpile from the latest version of ECMAScript into ES5 code. We have also added a reference to the Babili plugin in the plugins property that will minify the output.

To run this as a build script, add the following line to the “scripts” property of your package.json file:

"build": "webpack --progress --colors --profile"

Flags

The flags used with the webpack command above are quite common when transpiling JavaScript. --progress will display what is happening as WebPack does its thing, and --colors will display it all in different colors. The --profile flag will make WebPack show how long each part of the build takes, which can be helpful in identifying any bottlenecks in the build process.

Our last job is to run the build script:

npm run build

This should create a file called ‘bundle.min.js’ inside the ‘dist’ directory. This contains all the JavaScript the application needs in just one, minified file without having to load any external libraries at runtime.

Have a go at playing the game by loading index.html in your browser:

Quiz Ninja

Although we haven’t changed the functionality of the quiz game in this chapter, we have created a modular structure and separated the source files from the files that are deployed. If this project was to be deployed in public, then only the files in the ‘dist’ directory would actually be distributed. The files in the src directory would only be used for development.

This makes the code easier to maintain in the future, as well as ensuring that the distributed code uses the optimal file size and works in a large number of browsers. These are both important tasks to consider when writing JavaScript that will be deployed to a server.

Chapter Summary

  • JavaScript libraries provide methods to make common tasks easier to achieve.
  • Libraries can make programming much easier, but you should think carefully about whether you require a library, and which one is best for your needs.
  • jQuery and Lodash are two popular libraries that provide a large number of useful and well-tested functions.
  • npm and Yarn are package managers that can be used to install JavaScript packages, as well as any dependencies that they require.
  • A module is a self-contained piece of code that provides functions and methods that can then be used in other files and by other modules.
  • ES6 added support for modules, allowing code to be abstracted into their own self-contained files and imported into another file.
  • The MVC pattern is used to organize code into distinct sections that are responsible for different elements of an application.
  • Template files can be used to separate view code from JavaScript; they also enable dynamic code and programming logic to be used to generate markup.
  • React and Vue.js are popular JavaScript view libraries that render components and keep track of their state.
  • Minification is the process of removing any redundant characters from the code in order to reduce its file size.
  • Files can be compressed on the server using the gzip compression tool.
  • Webpack can be used to bundle multiple files into a single bundle, and automate common tasks such as transpiling, minifying code and running tests.
  • Before code is deployed, it should be concatenated into a single file, minified and compressed. The script tag should be placed just before the closing </body> tag to ensure that all elements on the page have loaded before the script runs.

In the next chapter, we’ll be looking at some of the features in the next version of JavaScript, as well as some ideas of what you can build using JavaScript.

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16