Webpack - The Why and the How
Don't let yourself get confused by all the fancy stuff Webpack does.
What is Webpack then? Well its a module bundler, there we go. Just kidding, this doesn't tell the beginner very much at all. I believe that the why is important to getting webpack so the bulk of this answer will focus on that.
At its core Webpack allows us to use javascript modules within our browser by taking multiple files and assets and combining them into one big file as shown below in this image from the new docs for Webpack 2.
All the extra shiny things such as compiling es6/7 to es5 or allowing us to use css modules are just nice extras afforded to us by Webpack.
The powerful ecosystem of Webpack plugins and extras makes Webpack seem confusing as it appears to do so much. While the additional features afforded to us through plugins is great we need to stay focused on the core reason Webpack exists - module bundling. Therefore loaders and plugins are outside the scope of this high level discussion on the fundamental problem that Webpack helps solve.
Webpack is a command line tool to create bundles of assets (code and files). Webpack doesn't run on the server or the browser. Webpack takes all your javascript files and any other assets and transforms then into one huge file.
This big file can then be sent by the server to a client's browser.
Remember the browser and server don't care that this big file was generated using Webpack it just treats it like any other file.
webpack-dev-server vs webpack cli
webpack-dev-server is a fundamentally different tool from the webpack cli (command line tool) described above. It is a development server that runs on node / express. When this server is running we load our app from one of the ports on this development server and we can access additional features while developing our app that makes our life easier such as hot module reloading and autobundling (runs webpack cli to bundle automatically when a file changes). The advantage of hot module reloading is that we can keep the app running and inject new versions of the files that are edited during runtime. Therefore we can see what changes to some files in the application look like without losing the state of the whole app.
The Why
Traditional Server Rendered Apps
Traditionally apps have been server side rendered. This means that a request is made by a client to a server and all the logic is on the server. The server spits out a static html page back to the client which is what they see in their browser. This is why whenever you navigate in old server-side rendered apps you will see the page flash as it refreshes.
Single Page Apps - SPAs
However nowadays single page applications are all the rage. On a single page application our app is windowed within one url and we never need to refresh. This is considered a nicer experience for the user as it feels slicker not having to refresh. In SPAs if we want to navigate from the home to say the signin page we would navigate to the url for the signin page. However unlike traditional server side rendered pages when the client's browser makes this request the page will not refresh. Instead the app will dynamically update itself in order to show the signin content. Although this looks like a separate page in our Single Page Application our app just dynamically updates for different pages.
Dynamic SPAs mean more code in the browser
So in SPAs with all this dynamic content there is way more javascript code that is in the browser. When we say dynamic we are referring to the amount of logic that lives within the client's browser in the form of javascript. Our server side rendered apps spit out static pages that are not dynamic. The dynamism all happens in the server while generating the static page but once it hits the browser it is relatively static (there isn't a lot of javascript in it)
The How
Managing this new abundance of browser logic i.e more Javascript
Webpack was primarily designed to deal with the emerging trend of having more and more javascript in the client.
Ok, so what we have a lot of javascript in the browser why is this a problem?
We need to split the code into multiple files on the client so the app is easier to work on
Well, where do we put it all? We can put it all in one big file. However, if we do this it will be a nightmare to wade through it and understand how all the parts work. Instead, we need to split this one huge chunk of code into smaller chunks according to the function of each chunk - i.e splitting the code across multiple files.
As you probably know decomposing large things into smaller things grouped by function is what we mean when we talk about 'making something modular'. You may be thinking why not just split the big chunk of code into little chunks and be done with it. The problem is the client doesn't magically know which files import stuff from other files. So we could have multiple isolated files without Webpack but the app won't work properly as it is more likely than not that within the big chunk of code most of the code within it will rely on other parts of code in the big chunk to work.
We need something like Webpack or one of its alternatives (browserify) to create a module system. On the server side Node has a built-in module resolver where you can "require" modules. However browsers do not come with this functionality.
However if we have multiple files some will import from each other and we need a way of knowing which files rely or are dependent on each other. Webpack allows us to use JavaScript modules on the front end by traversing through the files from an entry point and then mapping their dependencies. Think of the entry point as the top of the hierarchy in a chain of files that are dependent on each other.
Module/ Dependency resolution in Webpack
By mapping out a graph of the dependencies Webpack is able to load the different modules in the correct order asynchronously and in parallel. Internally Webpack has its own resolver for figuring out the dependency graph between different modules. The webpack docs state that
The resolver helps webpack finds the module code that needs to be
included in the bundle for every such require/import statement.
Then the docs explain that the resolver takes a different approach according to the type of path that is referenced by imports in the files that Webpack is bundling.
The resolving process is pretty simple and distinguishes between three
types of requests:
- absolute path: require("/home/me/file"), require("C:\Home\me\file")
- relative path: require("../src/file"), require("./file") module path:
- require("module"), require("module/lib/file")