Buy

Yo friends! I am ecstatic about this tutorial on Webpack... because we've packed a ton of great stuff into it. But it's more than that! Webpack is going to change the way you develop front-end code, big time. And that's the real reason I'm so pumped!

But... Webpack is tough! And sometimes, it's weird and it mis-behaves and it's frustrating. Huh, it's kind of a like baby - super great and all... but with a mind of its own.

Anyways, that's why we're here: to unpack this beast from beginner to pro. In just a little while, you're going to have new super-powers that you never dreamed of.

Oh, what does Webpack actually do? Did I not mention that? I'll show you soon.

Grab the Code!

Like always, JavaScript is best written together, so download the course code from this page to code along with me. When you unzip the file, you'll find a start/ directory inside that has the same code you see here. Oh, and if you've been coding along with the first two JavaScript tutorials - you're amazing! But also, make sure to download the new code: I've made a few tweaks since the last tutorial to make things more interesting.

Then, open the README.md file for fascinating instructions on how to get the project setup. The last step will be to find your favorite terminal, move into the app, and run:

php bin/console server:run

to start the built-in PHP web server.

Pull up the site in your browser: http://localhost:8000. Welcome to Lift Stuff! Log in as ron_furgandy, password pumpup. Over the past 2 courses, we've lovingly built this activity-tracker-for-programmers into a nice, JavaScript-powered front-end. Now, we're going to revolutionize the way that code is organized.

Node and require

What am I talking about? Well, in the last tutorial, in addition to running JavaScript in the browser, we actually wrote a bit of Node.js - aka JavaScript that runs right on your server. Open up play.js:

7 lines play.js
let foods = new Set();
foods.add('gelato');
foods.add('tortas');
foods.add('gelato');
console.log(foods);

We used this as a simple way to test out new ES6 features.

To execute this, open a new terminal tab and run:

node play.js

Awesome! If you look at most Node.js code, one thing will jump out immediately: the require() function. On the surface, it's a lot like PHP's require statement: it allows you to separate code into multiple files.

Try it out: at the root of our project, create a new foods.js file. Then, copy the old foods code from play.js, delete it, and paste it in foods.js:

7 lines foods.js
let foods = new Set();
foods.add('gelato');
foods.add('tortas');
foods.add('gelato');
... lines 5 - 7

Modules & module.exports

Now, as you're probably expecting, we're going to require foods.js from play.js. But there is one really important difference between the way require works in PHP versus Node. In PHP, when you require a file, you magically have access to all functions, classes or variables inside. But in Node, that's not true: you need to explicitly export a value from the required file.

How? By saying module.exports = foods:

7 lines foods.js
let foods = new Set();
foods.add('gelato');
foods.add('tortas');
foods.add('gelato');
module.exports = foods;

By the way, in JavaScript, each file is known as a module and is executed in isolation. Nothing is returned from this file other than what we export.

Ok! Back in play.js, we can now say, const foods = require('./foods'):

4 lines play.js
const foods = require('./foods');
console.log(foods);

Before I say more, try it! Head back to your terminal and re-run the code:

node play.js

It still works! Back in the code, notice that we do not need the .js on the end of the filename:

4 lines play.js
const foods = require('./foods');
... lines 2 - 4

We can add it - that would totally work. But if it's not there, Node knows to look for foods.js.

Also, that ./ before foods is no accident: that's super important. When a path starts with a ., Node knows to look for that file relative to this file. If we just said foods without the ./, well, that means something very different. More on that later.

Using require in the Browser

So the require() function is an awesome feature. The question is: can we use this in the browser JavaScript world? Because if we could, just imagine how easy it would be to organize our JavaScript!

And yes! We can totally get it working... otherwise this would be a really short tutorial.

But first, a little bit of cleanup: open app/Resources/views/lift/index.html.twig:

67 lines app/Resources/views/lift/index.html.twig
... lines 1 - 53
{% block javascripts %}
{{ parent() }}
<script src="https://cdn.jsdelivr.net/sweetalert2/6.1.0/sweetalert2.min.js"></script>
<script src="{{ asset('assets/dist/RepLogApp.js') }}"></script>
<script>
$(document).ready(function() {
var $wrapper = $('.js-rep-log-table');
var repLogApp = new RepLogApp($wrapper);
});
</script>
{% endblock %}

This is the template for the main page. And at the end of the last tutorial, we used a library called Babel to "transpile" our source RepLogApp.js into a dist/RepLogApp.js file. We are going to use Babel again, but for now, delete the dist file. Then, point our page back to the source RepLogApp.js:

67 lines app/Resources/views/lift/index.html.twig
... lines 1 - 53
{% block javascripts %}
... lines 55 - 57
<script src="{{ asset('assets/js/RepLogApp.js') }}"></script>
... lines 59 - 65
{% endblock %}

This file contains two classes: RepLogApp and, near the bottom, another called Helper:

249 lines web/assets/js/RepLogApp.js
'use strict';
(function(window, $, Routing, swal) {
... lines 4 - 6
class RepLogApp {
... lines 8 - 194
}
/**
* A "private" object
*/
class Helper {
... lines 201 - 228
}
... lines 230 - 247
})(window, jQuery, Routing, swal);

Hey! For organization, let's move this class into its own file. In that same directory, create a new RepLogAppHelper.js file. I'll add the 'use strict'; on top and then paste the class:

38 lines web/assets/js/RepLogHelper.js
'use strict';
/**
* A "private" object
*/
class Helper {
constructor(repLogs) {
this.repLogs = repLogs;
}
calculateTotalWeight() {
return Helper._calculateWeights(
this.repLogs
);
}
getTotalWeightString(maxWeight = 500) {
let weight = this.calculateTotalWeight();
if (weight > maxWeight) {
weight = maxWeight + '+';
}
return weight + ' lbs';
}
static _calculateWeights(repLogs) {
let totalWeight = 0;
for (let repLog of repLogs) {
totalWeight += repLog.totalWeightLifted;
}
return totalWeight;
}
}
... lines 36 - 38

At the bottom, add module.exports = Helper:

38 lines web/assets/js/RepLogHelper.js
'use strict';
/**
* A "private" object
*/
class Helper {
... lines 7 - 34
}
module.exports = Helper;

You can export anything you want from a module - a value, like we did earlier - a class, a function, an object - whatever!

Back in RepLogApp.js, now that the Helper class is gone... well, we're not going to have a good time. On line 10, PhpStorm is giving me a cryptic error: element is not exported... a funny way of saying "Variable undefined"!

Fix this: at the top, add const Helper = require('./RepLogAppHelper'):

217 lines web/assets/js/RepLogApp.js
'use strict';
const Helper = require('./RepLogAppHelper');
(function(window, $, Routing, swal) {
... lines 6 - 215
})(window, jQuery, Routing, swal);

My editor is happy!

Based on what we saw in Node... this should just work! Back in my browser, I'll open the console and then, refresh. Yep, we somehow knew life wouldn't be so simple. We get an error:

require is not defined

Here's the deal: the require() function does not work in any browser. And it's not that browsers are behind... it's just not possible to make it work!

Think about it: in PHP, when we use the require statement, we're reading a file from our file system... which is basically instant. But on the web, a browser would need to go download that file. Imagine if we waited while it downloaded this file... then this file required 5 other files.... so we waited for those... then those files required 10 other files... so we finally decide to go have lunch while the web page loads. It just doesn't work!

Enter Webpack.

Leave a comment!

  • 2018-05-15 Diego Aguiar

    Hey Milan Vlach
    Thanks for sharing your thoughts :)
    Showing a message when a user has JS disabled, sounds like a good idea

    Cheers!

  • 2018-05-14 Milan Vlach

    Yo @weaverryan!

    Love your thoughts! I think you just covered all of the main issues people could have. :))

    I would like to add just a few bits out of my head that indirectly confirms your thoughts.

    If you (the person reading this) are a Google Chrome user, press CTRL + SHIFT + I (open your DevTools) and navigate into Settings, you'll notice that the "Disable JavaScripts" option is located under the "Debugger" option, indicating, that it is not meant for casual user to operate like that. I think the main purpose of that option still available in the browsers is just for a testing environment that might come out by using certain browser add-ons. Thus, Google developers don't actually think that disabled JS is a standard behaviour (and I certainly cannot argue with Google on that one :)).

    There are yearly a lot of statistics about the most used programming languages all around the world. JavaScript is still on the front lines as "One of the most used programming language of all time". If people think that JS has no value, they would abandon it and use alternatives that might fulfil their needs.

    and lastly - I am so glad you made your own research about how many sites work without enabled JS! This is one of the fastest growing real estate sites in the Czech Republic: https://www.ulovdomov.cz/ (for anyone interested in Czech - "ulov domov" means "catch home"). I am using it as an example because it is composed more-less only by JS and remote API. Search engines are aware of pages located on that domain and work nicely with its routing system. If you disable JS in your browser and try to access the site, 0% of it will work. Another indication that developers just count with enabled JS nowadays.

    I would just add, that it is nice to let a user know about need for enabled JS by some kind of notification - to let them be aware of that fact.

    Anyway, thanks for your help, Ryan! Always love your comments/videos and hope we will discuss more stuff in the future! :)

  • 2018-05-14 weaverryan

    Hey Milan Vlach!

    GREAT question. And, it's something I've thought a lot about, but am not exactly an expert. But, for what it's worth, here are my thoughts.

    Years ago, I would develop a site that worked with with and without JavaScript. I don't do that anymore. First, I think we're beyond the time where users having "JavaScript disabled" is a problem. Also (and I'm FAR from an expert on this topic), I no longer think that a well-written JavaScript application has any "accessibility" concerns. Years ago, you may have wanted to make your site work without JavaScript, because JavaScript confused screen readers for blind users. That is no longer the case (you CAN still build badly accessible sites using JS, but the point is, accessibility tools now understand JS, so if you render an accessible-friendly site purely in JS, it's no problem).

    So, if JS doesn't present accessibility problems, nor (probably) search engine indexing problems, and 0% of your users have JavaScript disabled, it becomes very difficult to make a business case for the added time and complexity if making your site work without JS. If you turn JS off for KnpU, the site will somewhat work, but the most important parts will be broken.

    As an experiment, I turned JavaScript off and went to a few HUGE news sites. The first site I went to did not load at all, beyond the header and the footer. Yep, 0 content with JavaScript disabled. If a huge new site doesn't care, you probably shouldn't either.

    So, yea! When we think about rendering a pure JS front-end versus something more traditional, I give more weight to (A) what is your team's expertise with JavaScript (B) Do you really need a full JS frontend, or would it be easy enough to render HTML and make parts of your site super fancy and (C), even though it shouldn't be a problem, I still somewhat fear search engine indexing problems. But, I think this may be an out-of-date view, honestly.

    Anyways, I hope this helps!

    Cheers!

  • 2018-05-11 Milan Vlach

    Hi guys!

    As always - thanks for interesting stuff! I am a Symfony backend developer, but very interested in code design and philosophy. I have been wondering for quite some time about webpacking - as for doing so, your application becomes depending on enabled javascript in your browser. My question is more philosophical, but in my opinion very important.

    Should application's core functionality be depending on enabled javascript inside your browser?

    This question came up to my mind after reading this interesting article on server medium.org: https://medium.freecodecamp...

    and seeing this diagram with an interesting concept: https://cdn-images-1.medium...

    I like the idea that backend just sends data via some kind of API and javascript using React renders the view - but in doing so you are depending on javascript in your browser and in doing so - breaking application for your non-js users.

    What are your thoughts about this concept, guys?

  • 2018-02-05 Victor Bocharsky

    Hey Mudita,

    Don't worry about your comments, we're not going to remove them ;)
    Glad you solved the problem!

    Cheers!

  • 2018-02-04 mudita again

    oups wrong, this is fine, I must have done something wrong, please delete my com (^^;)
    (also I checked the option disable HTTP cache when dev tools are open)

  • 2018-02-04 mudita

    nowadays it works better with: import Helper from './RepLogAppHelper'
    (otherwise I got the error: Uncaught TypeError: Class constructor Helper cannot be invoked without 'new')

    love your tutos <3

  • 2017-08-18 Diego Aguiar

    Hey Gaotian!

    You are right, we have a tiny error in our script, we are going to fix it as soon as possible :)
    Thanks for letting us know!

    Have a nice day

  • 2017-08-18 gaotian

    Hi,
    Thanks for the tuto, I think there may be a small typo in the script with : "const Helper = require('./RepLogHelper'); " for "const Helper = require('./RepLogAppHelper');" (not in the video and in the text before ^^)

  • 2017-08-14 Victor Bocharsky

    Thanks! Fixed in https://github.com/knpunive...

    Cheers!

  • 2017-08-11 Diego Aguiar

    Hey Michael

    You are totally right! We have a typo in our script.
    Thanks for letting us know, we will fix it as soon as possible.

    Cheers!

  • 2017-08-11 Michael

    Hi Ryan, I think you have typo in login, you have ron_furgundy but it should be ron_furgandy