5

I'm developing some Javascript front-end code using JQuery with some back-end JSON webservices. IDE is Netbeans, and debugging using that and Chrome.

Coming from a C++ background I'm used to small source files, each encapsulating one (or a small handful) of classes. With JS, the lack of simple 'include files' means that I have gradually found myself dealing with monolithic blocks of 1K+ lines of Javascript.

I've looked at using nginx server-side includes to split these, but this ties the code tightly to the webserver, which isn't ideal. Dynamically loading each source file as a separate request seems inefficient, so I guess I want something that will combine (and optionally minimize) the source code as part of the IDE build/debug cycle.

I'm fairly new to JS development, so have no idea about best practices for this.

My code is already object-oriented, but I have a few very large source files, which makes change management, editing, etc. difficult. I'm looking for the best approaches to splitting the code into multiple smaller source files, and how best to get those delivered to the client side.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Roddy
  • 769
  • 5
  • 8
  • Possible duplicate of [Are there any OO-principles that are practically applicable for Javascript?](http://programmers.stackexchange.com/questions/180585/are-there-any-oo-principles-that-are-practically-applicable-for-javascript) – gnat Sep 30 '16 at 08:11
  • 1
    I don't have time to write a full answer, but ECMAScript 2015 and beyond have statements such as [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) that allow you to split your source code and properly do encapsulation. Of course, as most browsers do not (fully) support these new ideas yet, transpilers such as [Babel](https://babeljs.io/) allow this to be used today. – Qqwy Sep 30 '16 at 08:35
  • @Qqwy - Thanks. That's given me the nudge I needed. I've actually switched to using Typescript, with require.js handling the module delivery. – Roddy Oct 03 '16 at 10:28
  • Javascript has a lot of shortcomings if you're used to a strongly-typed, modular language. Have you looked into [TypeScript](https://www.typescriptlang.org/)? It might be a more comfortable language for you, and it compiles *to* Javascript. So you wouldn't have to worry about making your Javascript modular any more than you worry about structuring your object code. – John Wu Jun 06 '18 at 03:14

2 Answers2

2

The modern JS approach is to:

  • Maintain lots of separate *.js source files, much the same as we do in OO languages
  • Use a JavaScript build pipeline like gulp or grunt to concatenate and minify the *.js files into one single asset before shipping

This allows you to:

  • Avoid huge files and maintain code readability
  • Deliver all the files to the browser in one single minified request

For very large applications, even the single minified asset might become too large. This is where module loaders like requirejs come in - allowing you to only load what you need.

jamesfmackenzie
  • 211
  • 2
  • 2
1

In order to avoid one monolith file containing all the code of your application, you may use two techniques:

  • Frameworks. There are a bunch of JavaScript frameworks which will let you declare dependencies between files, and load those files on demand. A popular framework is RequireJS.

    This gives you a straightforward approach where all the dependency work is done for you, and you can focus on coding, like you do in most mainstream languages such as Python, Java or C#. Note that Node.js follows a similar approach.

  • Module pattern. Here, you are not relying on a third party library for the dependencies, but simply split your code into parts which are very isolated one from another. Since you're familiar with OOP, the module would be close to at the same time an object and a namespace: you'll find ways to clearly declare which functions can be called from the outside world, and keep the other functions private.

    Once you have your separate modules, you can split your monolith file into smaller files—usually one file per module. From there, you have a choice: either you simply load all the files one by one from the browser every time (which is not the same as “every request”: if you configured your client-side caching properly, the files will be cached for a very long time). Or you bundle all your files into one (or several, whatever makes sense in your case) to reduce the number of requests.

    If you want to bundle those files, there are a bunch of server-side libraries which do that. Depending on your environment, you may search for one, or write one yourself.

    Minification could also be a good idea, and Google Closure Compiler is one of your options. Note that Google Closure Compiler have different levels from a simple “let's strip whitespace” to a very capable and very aggressive transformation of your code. If you try to use the aggressive one on existent code, chances are you won't be pleased with the results, at all. It takes time to become accustomed to how Google Closure Compiler, at this level, wants you to write your code.

Note that bundling your JavaScript doesn't necessarily lead to better performance. If, for instance, your website has a lot of pages, and each page uses little common code, RequireJS approach would mean two things:

  • A user arriving on this page will download only a tiny fragment of all your JavaScript code, i.e. just what is needed for this page. Pushing a huge bundle composed of dozens of files to your user who just needs two or three of them isn't the nicest thing you can do.

  • If you change one of the files frequently, RequireJS will just download the new version of this file for the users who already visited your site before. If it's a bundle, a tiny change would invalidate the entire bundle.

Note that bundling and minification are two different things. You can do one without the other.

Finally, remember that there are some very important aspects which should be your primary concern:

  • Client-side caching. JavaScript files can and often should be cached for a very long time, without the need for the browser to even do the check and receive HTTP 304 Not Modified in response. This also means that whenever you change a JavaScript file, all links to it should be changed, i.e. http://cdn.example.com/js/hello.js will become http://cdn.example.com/js/hello.js?r=1, then http://cdn.example.com/js/hello.js?r=2, etc.

  • CDN. If you are serving Chinese users from Europe, or Indian users from USA, you're doing it wrong. More importantly, if you are serving static content from your server which is already in charge of dynamic content, you may not only degrade performances, but also pay simply too much for the bandwidth. Services such as Amazon CloudFront ensure excellent delivery performance for your static content.

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513