Before diving deep into React, I'll take some time with this blog to understand and appreciate the bundler that we'll be using: Parcel.
NPM
NPM officially does not stand for Node Package Manager according to their official website. However, it is a tool used for package management and it is the default package manager for Node projects. NPM is installed when NodeJS
is installed on a machine. It comes with a command-line interface (CLI) used to interact with the online database of NPM. This database is called the NPM Registry, and it hosts public and private 'packages.' To add or update packages, we use the NPM CLI to interact with this database.
To initialise npm, you can run the following command in the root of your project folder:
npm init -y
You may skip -y
to pass in options yourself.
Once you initialise npm you get package.json
which has the configurations that npm needs.
Our react app cannot be built just by injecting react into our webpage, we also need a lot of packages to power our app, which is why we use npm
. Suppose we have to minify our app, bundle things up, remove console.log(), and optimise our app, for that we need a lot of helper packages and they can be installed using npm. It is like Maven for UI.
Parcel
We'll ignite our app using Parcel. It's a bundler, a tool that allows developers to package their code into a single file or bundle. This bundle can then be used to run the application in the browser. Bundlers are used to reduce the size of the code and improve the performance of the application. Common bundlers used with React are Webpack and Parcel. We'll install Parcel using npm as a dev dependency i.e. we need it in our dev environment and we don't want it in production.
npm install -D parcel
After installation, we can see a new folder node_modules
, a new file package-lock.json
and we can also see that our package.json
gets updated with the following dev dependency:
Here we can see the package that we just installed and its version. But what is ^
just before the version? Sometimes you'll also see ~
before the version. What do these mean?
^version
“Compatible with version”, will auto-update you to all future minor/patch versions, without incrementing the major version. For eg:^1.2.3
will use releases from 1.2.3 to <2.0.0.~version
“Approximately equivalent to version”, will auto-update you to all future patch versions, without incrementing the minor version. For eg:~1.2.3
will use releases from 1.2.3 to <1.3.0.
FYI, versions are written in the format: X.Y.Z (Major.Minor.Patch). Bug fixes not affecting the API increment the patch version, backwards compatible API additions/changes increment the minor version, and backwards incompatible API changes increment the major version. We call this system “Semantic Versioning.” Under this scheme, version numbers and the way they change convey meaning about the underlying code and what has been modified from one version to the next.
package-lock.json
You may have either used or at least heard of the statement "It is working on my local but it is not working in production !". This happens because the package may have been automatically upgraded if there is a ^
or a ~
in front of the version in the package.json file. See, this introduces a lot of uncertainty as to what exact version is being used in production, and we do not want that. That is why we use package-lock.json
to lock the version in our system. Thus, never put package-lock.json
in the .gitignore
file because it is a very important file.
node_modules
Every package/library that we install using npm gets downloaded and stored in node_modules. Our package-lock.json file has sufficient information on each dependency that is being used to run our project and more importantly, node_modules tends to become very heavy, so we should put it in .gitignore
, because we don't want it in git.
In our last blog, we injected react into our app using CDN scripts but that is not a good way. We can instead install it using npm so that we can directly access it from our server inside node_modules. So, remove those two script tags containing CDN links and run these commands:
npm install react
npm install react-dom
Time to ignite our app:
npx parcel index.html
Here, npx
means execute using npm. index.html
is the entry point into our app. Once you open the app on the browser using the link on the terminal:
Check the console, you'll see:
This is because we just removed those script tags before our app.js
script tag. To resolve this error, we need to import React and ReactDOM before using them inside our app.js
file.
It still won't work:
The error says "Browser scripts cannot have imports or exports". To resolve this error we need to put type="module" in our script tag containing app.js
. This means that it is not a normal js file in the browser, but it is a module. Now our imports will work.
<script type="module" src="app.js"></script>
In the recent upgrades, ReactDOM comes from "react-dom/client", so our imports become:
import React from "react";
import ReactDOM from "react-dom/client";
Parcel does tree shaking for us. It is the process of removing the unwanted code that we do not use while developing the application. In computing, tree shaking is a dead code elimination technique that is applied when optimizing code.
If we change anything on our app, you may notice that you don't have to refresh the page and the changes automatically get reflected on the browser. This is done by parcel for us. It is known as hot module replacement
(HMR). Parcel uses the file watcher algorithm, which was originally written in C++, to keep track of any changes we make in the files for HMR.
If you closely observe, two more folders .parcel-cache
and dist
also have been created. What are these?
.parcel-cache
Parcel automatically creates this folder for itself. It uses the files inside this folder for whatever it does, be it HMR, minifying, file watching etc.
We should put .parcel-cache
in our .gitignore
. Anything which we can generate on the server will be put inside .gitignore
.
dist
dist
is a development build. It keeps the files minified for us. When we run the command npx parcel index.html
, it creates a faster development version of our project and it hosts it on the server. To make a production build you can use this command:
npx parcel build index.html
Since we are passing the entry point of our app in the above command itself we should remove "main": "app.js"
from our package.json
file.
Now, after running the above command you can see the minified files:
Parcel features:
HMR (Hot Module Replacement) - parcel keeps track of file changes via file watcher algorithm and renders the changes in the files
File watcher algorithm - made with C++
Minification
Cleaning our code
Tree shaking or dead code elimination
Manages Development and Production builds
Super fast building algorithm
Image optimization
Caching while development
Compresses
Compatible with older versions of the browser
HTTPS in the development environment
Manages port number
Zero configuration needed
Automatic code splitting
We have our package manager that handles and takes care of all the transitive dependencies of our code.
When a programmer explicitly declares a dependency in their project (such as via Maven or NPM), those are called direct dependencies because they are used directly by the developer. They’re often the dependencies the developer is thinking about when asked what third-party dependencies are in their project.
However, just as that developer is using dependencies to simplify their job, many times library developers will also use dependencies. A transitive dependency is one not imported directly into the project at hand, but instead imported by a direct dependency or another transitive dependency.
The depth of a dependency refers to how many layers removed a dependency is from your project, which is at the root of the dependency tree.
Now, can we say that we have created a basic version of npx create-react-app
? Definitely yes!
Browserslist
Browserslist is a tool that allows specifying which browsers should be supported in your frontend app by specifying "queries" in a config file. To check the list of browsers that are compatible with particular query strings, check out browserslist.dev. Browserslist is used by frameworks/libraries such as React, Angular and Vue, but it's not limited to them.
That's all for now folks. See you in the next blog!