Proper way of loading javascript objects in memory? - javascript

I my webapp I use about 30 js files, each file containing 1 function. All these function are now selfinvoking and have references to each other.
The problem with this is that the order of scripts in the index.jsp matters. If a method is called on a function which has not been invoked yet we get a undefined error.
For a while we could overcome this by controlling the order of the <script> tags, but I would like to do this by using a loader script.
I have set up a small fiddle to show my concept. My biggest concern is that I have to declare my objects globally, in order to have them be accessible in the jquery(document).ready() function.
Is this an OK pattern? Any hints highly appreciated!

You could use RequireJS or similar loader, which would handle script dependencies for you.
You would need to modify each of JS file to make it a module in a similar fashion to this example:
// File: module3.js
define(["module1", "module2"], function(m1, m2) {
// Here, module1 and module2 are guaranteed to be loaded.
});
Then, you would make one "main" script (I usually call it main.js) and require several modules:
require(["module3"], function (m3) {
// Here module3 is loaded, as well as module1 and module2
// - because module3 depends on them.
});
And put this in your HTML:
<script data-main="scripts/main" src="scripts/require.js"></script>

Try to build your server-side architecture to serve proper js files (and other static files) per page. Create 1 minified js file for page and initialize objects scope in html files.

Related

Question about functions being called from other files - Javascript

I am looking for some help understanding something on a project I am working on. I have written some code that is functioning, though I am not sure why.
In a Node.js server, in /public/js there are two scripts. One (file1.js) has a function func(). file2.js, in the same directory, calls func() successfully. There is no module.exporting or requireing anywhere, yet the two files work together. They are both referenced in the index.ejs file, however. Is this where they become able to communicate?
//file1.js
function func() {
console.log("foo")
}
//file2.js
func()
//index.ejs
...
<script src="public/js/file1.js"></script>
<script src="public/js/file2.js"></script>
...
I've spent all day reading and cannot find anything on this topic.
Your question is about how JavaScript works in a browser.
Node.js isn't relevant here. All it is doing is running an HTTP server program that gives static files to the browser.
When you load a script into a browser using a script element (and don't use type="module"), any variable in the outer most scope of the script file (e.g. which isn't let inside a block or var inside a function) becomes a global and is accessible to any other script loaded into the same HTML document that way.
Globals are messy and a good way for different bits of code to accidentally interfere with each other so modern JavaScript generally avoids using them. This style of JavaScript programming wasn't common when JS was first implemented in browsers: Hence the behaviour described above.
This is why people started to use techniques like the revealing module pattern and why AMD and Node modules were designed before standard JavaScript modules were added to the specification.
You must understand how the global space behaves in Javascript.
This Code:
<script src="public/js/file1.js"></script>
<script src="public/js/file2.js"></script>
Is the same as this:
<script>
//file1.js
function func() {
console.log("foo");
}
func();
</script>
Because soon as file1.js is loaded, everything that is defined inside of it, becomes available anywhere in that page from where it was included.
Since file2.js uses contents of file1.js, it will work because func is available to be used anywhere below file1.js inclusion.

Webpack config with multiple entries and a global site wide entry

I'm moving from requirejs to webpack.
In my require js setup, I have a site-wide file site.js that does some global setup, and non-page-specific stuff, like ajax prefilters, some global ui component setup, etc.
Some pages have self contained js apps specifically for a given page, where I'd have window.appPaths = ['some/path/to/app.js'] set in the template, and site.js would load up the app once the initial site wide stuff was done.
However, moving to webpack, it's reading my require(window.appPaths) and then it converts it to some regex and attempts to bundle every single js file in my src directory.
So basically, I'm wondering if there's a way for me to set this up to have multiple entry points, one being something like site.js and another being something like somePageApp.js wherein site.js is actually a dependency of the latter, but the latter doesn't always exist.
Sigh, hopefully that's clear, i'll elaborate where necessary.
edit: This is how it was set up in require.js setup, my output was:
html for an app page:
<script src="site.js">
<script>
window.appPaths = ['someApp.js'];
</script>
site.js:
require([
// deps for sitewide stuff
], function () {
// doing global general stuff
someTask(function () {
if(window.appPaths) require(window.appPaths)
})
})
someApp.js:
require([
// app specific deps
], function () {
// application code that doesn't run because not `require`d until the site.js tasks are done.
})
My "ideal" setup isn't necessarily the exact same, but I would want someApp.js to not load and/or run until site.js loaded. In my requirejs setup, site.js was also essentially the commons chunk but I don't think that's necessary here... just having trouble finding a parallel style of organization in webpack...

Difference Between data-main and normal script loading

When using RequireJS, what's the difference between including your script with
<script data-main="scripts/main" src="scripts/require.js"></script>
and
<script src="scripts/require.js"></script>
i.e. what does the data-main attribute change about loading in a script? I've read through the docs on this, and the different isn't entirely clear to me.
You will typically use a data-main script to set configuration options and then load the first application module. Note: the script tag require.js generates for your data-main module includes the async attribute. This means that you cannot assume that the load and execution of your data-main script will finish prior to other scripts referenced later in the same page.
The documentation mentions that you'll typically use a data-main script to set configuration options and load the first application module — but can't you also do that via a plain old script tag? Is there a benefit to doing configuration loading the application module with a data-main attribute?
Is the only different with data-main the asynchronous loading? Or is there something more?
data-main is just another way to perform the initial require call of your application. To illustrate... this:
<script data-main="scripts/main" src="scripts/require.js"></script>
is equivalent to this:
<script src="scripts/require.js"></script>
<script>require(["scripts/main"])</script>
The two forms are both asynchronous. This is really all there is to it. Considerations about how many entry points you have or where the RequireJS configuration is going to be located are completely orthogonal to the use of data-main. To put it differently, these considerations play a role in your use of data-main to the exact same extent that they play a role in your use of require(["scripts/main"]).
The part of the documentation you quoted is just obscuring things by mentioning that the script loaded with data-main creates a script element in the head element with the async attribute set, because this is not different from loading any script through RequireJS. Every single script loaded by RequireJS will have a script element created for it, in the head, and have the async attribute set.
It is common to use data-main for applications that have only a single entry point, and to put RequireJS' configuration in the module specified in data-main, but it is not required by any means. For instance, this is a perfectly valid use:
<script>
require = {
// RequireJS config here...
};
</script>
<script data-main="scripts/main" src="scripts/require.js"></script>
<script>
require(["foo"], function (foo) {
foo.something();
});
</script>
The configuration is given to RequireJS by setting require in the global space before loading RequireJS. (If require is defined before RequireJS is loaded, it will take require's value as its configuration.) Besides kicking off the application by loading scripts/main, this code also loads foo and calls a method on it: two entry points.
data-main is for when you want to have a single entry point to your application. That single script line will load RequireJS along with scripts/main.js and kick off your app.
The result of
<script data-main="scripts/main" src="scripts/require.js"></script>
is that <script async src="scripts/main.js"></script> is appended to the document at runtime; this is the script that will contain your require.config() block and pull in your first application script. If you don't specify a data-main, then you're only loading Require and none of your app scripts, unless you explicitly load a config file and the first module.
What do you think Require will load if you don't tell it to load anything?
If you do not use data-main, you must provide an entry point after loading Require (this is how I have always done it, for no good reason other than that's how I learned it.) Read about the configuration options to see how you would do this.
I use this pattern in development:
<script src="js/lib/require.js"></script>
<script src="js/config.js"></script>
<script>
// same as data-main
require.config({baseUrl : 'js'});
require(['js/main']);
</script>
As a single entry point, the contents of config.js and the subsequent require(['js/main']) call would be in whichever script is referenced as data-main.
If you use the static optimizer to build a production bundle, none of this matters because you just load the bundle.
data-main is the script that require.js will process. As the documentation says, it is common to set configuration options in that script. But there are other ways to do that. In many cases it is the easiest and most effective place. But not always.
The script pointed to by data-main will also list the dependencies for the code that the file defines. The dependencies are where the meat is. It is typical, though not required, to have this first module load and execute what is ultimately the actual application.
Addendum in response to the comment:
There are some concepts you need to be aware of that will help make sense of this approach.
First is that there isn't a single (or a couple, or even a few) script(s). This type of loader is designed to handle lots and lots of very small scripts. Each with a very specific and often (preferably) single purpose. Call these scripts modules (or units).
Any given module might depend on any number of other modules in order to function. The AMD pattern allows the dependencies for each module to be listed within that module's definition.
RequireJS will sort out who needs what and in what order and not let scripts execute until all the modules they depend on are loaded and ready.
So this is not at all like putting a script link (or multiple links) in a page as we all grew up doing. It is a much different approach to javascript development. Once you get your head around it and figure out how to breakdown your code into discreet units of functionality it really is quite slick.
<script data-main="scripts/main.js" src="scripts/vendor/requirejs/require.js"></script>
src will load "scripts/vendor/requirejs/require.js" first. Then data-main attribute will execute "scripts/main.js".

How can you determine if Y.add has been called for a YUI3 module?

I want to determine at runtime whether a YUI module has been defined (i. e. whether someone has called YUI.add() for that module).
Based on reading the YUI code, it seems like YUI.Env.mods[moduleName] will do the trick, but I can't find any documentation for this property so I'm not sure if it's meant to be used/works in all cases. Is there a preferred way to do this?
EDIT: here's what I'm trying to accomplish:
We are switching from a system where most assets are manually loaded via link/script tags in the HEAD to one where we depend more on the YUI loader. To support legacy code, I want to make sure that modules preloaded in the HEAD will not get loaded again by YUI (some things like jQuery have issues when loaded twice).
The preloaded modules are a mix of YUI-style and non-YUI-style modules.
So far, I'm emitting code like the following:
<head>
<!-- bunch of script/link tags -->
<script>
var modules = // list of preloaded modules
, i;
for (i = 0; i < modules.length; ++i) {
if (!ISMODULEALREADYDEFINED(modules[i])) {
YUI.add(modules[i], function (Y) { }, '');
}
}
</script>
</head>
The reason I need the ISMODULEALREADYDEFINED check is that if some of the pre loaded modules are YUI-style modules, then YUI might still be loading up their dependencies asynchronously when we run the above script. If that happens, then the noop module definition I'm adding prevents the original module definition callback from ever running.

How to load a specific code regarding the current page?

I'm building a complex web app and to keep things simple, I made various modules (objects) in various files. Those modules can be required on some page, and not on others.
For that reason, I'd like to avoid loading all the modules for any pages, increasing the amount of useless requests.
So far, I work like this :
I include all needed libraries
After that, I instantiate these librairies in jQuery(function() {}); with specifics #ids or .classes arguments for that current page
Everything works fine, but since my app is growing beyond easy, I'd like to manage my JS with RequireJS.
And that's where things start to be a little confusing for me.
I know I can load my module when required, using require(['module1', 'module2', 'etc'], function (module1, module2, etc) {}), but how can I say :
"on this page, you load these modules, and instantiate them with those #ids and .classes"
and
"on this other page, you load only that module, with this #other-id"
?
module1 will, for example, load data from the api, and list them to a specific table given as parameter :
// in page 1
var mod1 = new Module1($('#content>table');
mod1.init(); // will load the data from the api with the url located at <table data-api-url="http://....">
// in page 2
var mod1 = new Module1($('#content .items>table'); // The table where we want the data to be populated is not at the same position!
mod1.init();
That means, depending on the page, I'll have to load my modules differently. That's how I don't know how to do using RequireJs :/
What you need is a javascript file for each page. That file will be responsible to execute your page-specific code.
I'll assume that you will use r.js to optmize and pack your code.
We can interpret Module1, Module2 etc. as libraries, because they will be used on multiple pages. To avoid browser do one request for each library module you can include this modules on your optimized main file:
Configure the "modules:" attribute of your build profile like this:
...
modules: [
{
name: "main" // this is your main file
includes: [
"module1",
"module2",
"etc..."
]
}
]
...
By doing this you tell to requirejs something like this: Optimize my "main.js" file and include in it all its dependencies, also includes the "module1", "module2" etc.
You have to do this because on your main file you do not include these modules on the require()/define() call, but you still want they available in case the page-specific module needs them. You don't have to do this for every library module you have, just for those that will be used by most of your pages.
Then, you create a javascript file for your page to use those modules:
define(function(require, exports, module) {
var $ = require("jquery"),
module1 = require("module1");
var mod1 = new Module1($('#content>table'));
mod1.init();
//other page specific-code.
});
And then on the html file:
<script data-main="main" data-start="home" src="require.js"></script>
So, when page loads, it will make a request for require.js, then another for main.js and then another for home.js and that's all.
I'll put the link for the another question so this answer get some context: How to use RequireJS build profile + r.js in a multi-page project
The creator of RequireJS actually made an example project that uses page-specific main files: https://github.com/requirejs/example-multipage

Categories

Resources