Modern Javascript Development Environment

Nowadays, the web app codebase is increasing, and using a single script.js file to fit the entire javascript code in one file is not a good practice in terms of page load time.

Let's have a brief introduction about how a Modern Javascript Development Environment works.

We divide our entire project into multiple modules, then these modules can share data between them and make our code more organized and maintainable. Now what's great about modules is we can use third-party modules within our application. There are various packages shared on the NPM repository which can be used e.g. React, Leaflet and etc. NPM stands for Node Package Manager because it was originally built for Node.js and for Node.js However, NPM has established itself as the goto repository for all kinds of packages in Modern JavaScript Development.

So basically NPM is both the repository in which packages live and a program that we use on our computers to install and manage these packages. So let's say that we are done writing our project code, we divided it into multiple modules and we included some 3rd-party modules as well. And so now the development step is complete. Now, this is not the end of the story. At least not when rebuilding a real-world application.

Instead, our project now needs to go through a build process, where one big final JavaScript bundle is built. And that's the final file, which we will deploy to our web server for production. So basically it's the JavaScript file, that will be sent to browsers in production. And production simply means that the application is being used by real users in the real world. Now, a build process can be something really complex, but keeping it simple here and can be divided into two major steps. And the first step, we'll bundle all our modules together into one big file. This is a pretty complex process that can eliminate unused code and compress code as well. Now, this step is super important for two big reasons. First, older browsers don't support modules at all. And so code that's in a module could not be executed by any older browser. And second, it's also better for performance to send fewer files to the browser, and it's also beneficial that the bundling step compresses our code. But anyway, as the second step, we do something called Transpiling and Polyfilling, which is basically to convert all modern JavaScript syntax and features back to old ES5 syntax, so that even older browsers can understand our code without breaking. And this is usually done using a tool called Babel. So these are the two steps of our build process, and after these two steps, we end up with that final JavaScript bundle, ready to be deployed on a server for production.

Now, of course, we don't perform these steps ourselves. Instead, we use a special tool to implement this build process for us. And the most common build tools available, are probably Webpack and Parcel. And these are called JavaScript bundlers because well, as the name says they take our raw code and transform it into a JavaScript bundle. Now Webpack is the more popular one, but it can be really hard and confusing to set it up. So that's because there's a lot of stuff that we need to configure manually, in order to make it work properly. Parcel, on the other hand, is a zero-configuration bundler, which simply works out of the box. And so in this bundler, we don't have to write any setup code, which is really amazing.

Oops, Probably it wasn't a brief introduction. But let's now deep dive into the topics step by step

1. Overview of Modules, Importing and Exporting in ES6 Modules

Let's understand this by the example of Car.
car.png

There are various components of Car which are built in isolation, where each component has its own functionality. These all when combined with their functionalities and working make a complete Car in working. In the same way, modules are small reusable components that can be imported and exported between them and combined to form a bigger application.

Advantages of Modules:

  • Composition: These small building blocks put together to build complex applications.

  • Isolation: Modules are developed in isolation without thinking about other codebases.

  • Abstraction: Hiding implementation details from other components and only sharing functionalities.

  • Organization: Modules lead to a more organized codebase.

  • Reusability: Modules can be easily used with other projects for their specific functionality.

Now let's know what's the difference between a module and a script.js file

Now let's take a look at Behind the scenes: How ES6 Modules are imported

//1. index.html

    <script type="module" defer src="index.js"></script>

// 2. getCoords.js

const getCoords = function () {
    //return latitude and longitude
    return { lat: 57.9, lng: 63.99 }
}

export { getCoords }

// 3. displayCountry.js

const displayCountry = function (lat, lng) {
    //some code here
    console.log(`You're in India. Welcome to India`);
}

export { displayCountry }

//4. index.js

import { getCoords } from './assets/getCoords'
import { displayCountry } from './assets/displayCountry'


const { lat: latitude, lng: longitude } = getCoords()

displayCountry(latitude, longitude)

In the above example, let's understand the parsing of index.js file step by step:

  • Asynchronous downloading of getCoords.js and displayCountry.js

  • Linking imports to getCoords.js and displayCountry.js respectively.

  • Execution of getCoords.js and displayCountry.js respectively.

  • Execution of index.js

Few things to keep in mind,

  1. Imports are always hoisted in importing module
  2. ES6 Modules are live connections between each other, not COPIES. Infact import is just reference to that module.

How to Use Import and Export in ES6?

To make objects, functions, classes, or variables available to the outside world it’s as simple as exporting them and then importing them where needed in other files.

  • Exporting

We can export members one by one. Members which are not exported won't be available outside the module.

export const frontendFrameworks = ['React', 'Angular', 'Vue']

const hobbies = ['Singing', 'Football', 'Movies']  
// Not available outside module

export const student = {
    name: 'Nikhil',
    birthYear: '1999',
    education: 'Graduate',
    university: 'ABC'
}

Or we can export desired members in a single statement at the end of the module:

export {frontendFrameworks, student}

Exporting with alias

You can also give aliases to exported members with the as keyword:

export { frontendFrameworks, student as user }

Default Export

You can define a default export with the default keyword:

export default function displayCity(){

console.log(`You're in Mumbai. Welcome to Mumbai!`)

}
  • Importing

Importing is also very straightforward, with the import keyword, members to be imported in curly brackets and then the location of the module relative to the current file:

import {frontendFrameworks, student} from 'app.js'

Importing with alias

You can also alias members at import time:

import student as user from 'app.js'

Importing all exported members

You can import everything that’s imported by a module. This allows us to access members with dot notation :

import * as Utils from 'app.js'

/* While accessing */
Utils.displayCity()

Importing a module with a default member

You import the default member by giving it a name of your choice. In the following example Cart is the name given to the imported default member:

import Cart from 'app.js'

Along with the default one you import non-default members like this:

import Cart, { frontendFrameworks, student } from 'app.js'

OMG !🤯 This was a very long explanation. I hope you understand

Now as development of a project is completed, modules are coded with their functionalities. Let's move forward with NPM.

** 2. Why we need NPM? What's purpose of using it? **

NPM is a package manager for Node.js with hundreds of thousands of packages. Although it does create some of your directory structure/organization, this is not the main purpose.

The main goal, is automated dependency and package management. This means that you can specify all of your project's dependencies inside your package.json file, then any time you (or anyone else) needs to get started with your project they can just run npm install and immediately have all of the dependencies installed. On top of this, it is also possible to specify what versions your project depends upon to prevent updates from breaking your project.

It is definitely possible to manually download your libraries, copy them into the correct directories, and use them that way. However, as your project (and list of dependencies) grows, this will quickly become time-consuming and messy. It also makes collaborating and sharing your project that much more difficult.

$ npm init 
/*Ths would initialize npm in the folder*/

$ npm install
/*This will install npm, you will see a package.json file and
 inside the folder a node_ modules folder is added.
This node_modules contains all dependencies required for a project*/

/*Package .json consists of */
{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
}

/*Now if we want to have leaflet library for our project*/

$npm install leaflet@version_num

/*The leaflet library gets added to node_modules folder 
and in package.json leaflet is added into dependencies. 
Package.json changes to*/

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
"dependencies": {
    "leaflet": "^1.7.1"
  },
  "author": "",
  "license": "ISC",
}

Tip: While uploading your code to Git or copying it from some source to destination, never include node_modules folder. There is no sense to move that folder as all the dependencies can be installed just by npm install.

Hopefully this makes it more clear what the purpose of npm is. As a Javascript developer (both client-side and server-side), NPM is an indispensable tool in modern and huge applications workflow.

Now after installing all dependencies, we can't run the file index.html in browser. As browser doesn't understand modules. We need to convert these entire modules to scripts this process is called Bundling

** 3. Bundling with Parcel and it's purpose **

There are various tools for Bundling, Webpack and Parcel are most used. Webpack requires a config file to setup the configurations but Parcel doesn't require configuration.

Let's move further with Parcel and how it is used?

As Parcel is a development tool we install it as a dev dependency like this:

$ npm install parcel --save-dev

It gets added to the package.json file as:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
"dependencies": {
    "leaflet": "^1.7.1"
  },
"devDependencies": {
    "parcel": "^2.0.0-beta.2"
  },
  "author": "",
  "license": "ISC",
}

Now it's time to use parcel for bundling we run following command:

$ npx parcel index.html

It converts entire modules into script and bundles html, css, js files into scripts and creates a folder named dist.

It gives us a deployed server address like in terminal Server running at http://localhost:1234

Now whenever we change modules and save them, the server gets auto reloaded, due to which states are lost of the file. So to overcome this, Parcel includes a feature of Hot Module Replacement. Hot Module Replacement (HMR) improves the development experience by automatically updating modules in the browser at runtime without needing a whole page refresh. This means that the application state can be retained as you change small things. This will only apply in development; HMR is automatically disabled when bundling in production mode.

if (module.hot) {
  module.hot.accept()
}

Parcel is also smart enough to find path for the importing modules. This works for all types of assets like css, sass which means:

/*importing module*/

/*Lets say we want to import react library*/

import React from './node_modules/react'

/*No need to specify path as shown above*/

import React from 'react';

Now there's another way to run the scripts instead of everytime giving command parcel index.html.
We automate this by adding scripts to the package.json file according to our needs.

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "parcel index.html",
    "build": "parcel build index.html"
  },
"dependencies": {
    "leaflet": "^1.7.1"
  },
"devDependencies": {
    "parcel": "^2.0.0-beta.2"
  },
  "author": "",
  "license": "ISC",
}

This allows us to run scripts by $ npm run start and build a bundle for entire project with $npm run build.
After building the bundle, now you can deploy the dist folder wherever you want on any hosting service. You only need to upload this dist folder on Netlify or any hosting services.

Now there's a doubt that all ES6 features which we might have used in our scripts like arrow function, or spread operator, or let, const etc., these are supported by modern browsers what about the old ones, our application won't run on them?

No, it's not like that, our bundle will work for all types of browsers. This is because Parcel has built-in transpiler named Babel which converts ES6 specifications to ES5. You can have a look at functions in bundled scripts how arrow functions are converted to normal functions and used. All the unused characters like spaces are removed and a minified version in created and unused code is eliminated.

I hope it wasn't boring enough!

If you have read it till the end, you must have understood extremely well. Don't forget to follow me on Twitter.

If you find it useful do share it with your friends.

I will be posting more such articles. Thanks for reading it till the end.

15