Require.js Optimizer for a SPA with Backbone - javascript

I've built a big Backbone app (lot of views, models, collections) and used require.js for module loading. Everything works as expected and smoothly.
Now I'd like to ship to the net this app, but I'd like to optimize network requests, by compacting everything on a (single?) file. I've read about Require.JS Optimizer, installed node.js and created a build file.
These are my tests:
create a build file WITHOUT specifying a single module in modules section --> I obtain a copy of the source app, with all the js files uglyfied. Application works, but nothing seems changed from the network requests point of view.
create a build file WITH a single module (i.e. my main.js file) --> I obtain a copy of the source app, with all the js files uglyfied and one huge new main.js file that I suppose it includes all its dependencies (i.e. all the used files). That would solve the network requests problem, but the app doesn't start at all, starting to give me strange errors.
Which is the correct way to use Require.JS optimizer? I suppose #2, but what's your opinion? If that is the correct way, I should find out how to solve those errors...
Thanks
EDITS
This is my app folder structure:
app
|
--- scripts
|
--- libs
--- collections
--- views
--- models
--- main.js
--- styles
--- templates
--- index.htm
At the same level of "app" folder I created a build folder containing r.js and build.js, whose content is this:
({
//Directory relativa a questo file dove si trova l'app
appDir: '../app',
//Directory relativa ad appDir di dove si trovano i moduli
baseUrl: 'scripts',
//Percorso verso il file main
mainConfigFile: '../app/scripts/main.js',
paths:
{
jquery: 'libs/jquery/jquery-min',
jqueryui: 'libs/jqueryui/jquery-ui-1.10.2.min',
underscore: 'libs/underscore/underscore-min',
backbone: 'libs/backbone/backbone-min',
kendo: 'libs/kendo/kendo.web.min',
kendo_it: 'libs/kendo/amd/kendo.culture.it.min',
relational: 'libs/relational/backbone-relational',
nested: 'libs/nested/backbone-nested-v1.1.2.min',
rivets: 'libs/rivets/rivets.min',
i18n: 'libs/i18next/i18next.amd.withJQuery-1.6.3',
jvalid: 'libs/jqueryvalidate/jquery.validate.min',
chartjs: 'libs/chartjs/chart.min',
toastr: 'libs/toastr/toastr.min',
templates: '../templates'
},
//Where to save the output
dir: '../app-build',
modules:
[
{
name: 'main'
}
]
})
I launch the building process using this command:
node r.js -o build.js
And if I run the "compiled" main.js, I got this error back:
Uncaught Error: Mismatched anonymous define() module
Any ideas?

Depending on requirement of your application you can choose to
Concatenate all files into single file
Concatenate module-wise
Having every thing concatenated to single file works well for small applications but it will not scale to larger applications. requirejs has detailed documentation for this. If every thing is right, whatever method you adopt, that should not throw an error. Please provide more info about the error you are getting, that might help people answer your question properly.

Related

ember.js include custom js

Attempting to wrap my head around Ember.js.
Seems I understand the complex things, but miss out on the little things.
How would one go about adding an example.js file?
For simplicity, let's say the example.js file only contains:
(function(){
console.log("example is alive in console");
})(window);
This should display "example is alive in console" within the browser console.
I have tried:
adding app.import('vendor/javascripts/example.js'); within ember-cli-build.js and adding <script src="{{rootURL}}vendor/javascripts/example.js"></script> to index.html
Console is showing
ⓧ GET http://localhost:4200/vendor/javascripts/example.js
DEBUG: -------------------------------
DEBUG: Ember : 2.11.3
DEBUG: Ember Data : 2.12.1
DEBUG: jQuery : 3.2.1
DEBUG: -------------------------------
ⓧ GET http://localhost:4200/vendor/javascripts/example.js
All of the answers I have found stated that just adding custom.js to vendor file works. Sadly, I am missing something.
When modifying ember-cli-build.js you MUST RESTART the ember server manually. The livereload server will not pick up the changes.
This works for me when I don't nest assets in the /vendor directory. The ember-cli build process bundles JS files in /vendor into a single vendor.js file, which you can see linked in app/index.html. So place your example.js file at the root of /vendor, and then add the import to ember-cli-build.js:
app.import('vendor/example.js`);
Now when you start the server, your code from example.js should execute, since it will be included in assets/vendor.js.
Firstly, Ember.js has Convention Over Configuration approach, and your URL can do a lot of things than a normal HTML website.
Whatever you may want to do with your custom.js file it is not ember way of having it as a path. You need routes for navigation across the app. Although routes do much more than navigation. You specify the structure of your app that a user can browse through using Router's map function in app/router.js file.
However if you want to include custome.js file in your app, and have custom.js do some set of tasks for your app. You can simply go ahead and create a directory with any name, javascript for instance inside app directory. Have your javascript files placed inside it. Then you can import these files as simply as referencing any other files in ember:
import customObject from 'yourApp/javascript/custom.js';
Here, your custom.js should be exporting customObject.
Do read the guides if you want to learn more. And the API docs if you actually want to learn more.
Note: At the time of writing this answer current ember-cli version is #2.12.0

How to optimize a subset of JavaScript files created as RequireJS modules

I have single-page web application that uses RequireJS to organize and structure JavaScript code. Inside the app, I have some JS files that I want to optimize using r.js because they belong to an API that will be consumed by my customers. So, I just want to optimize those files and keep the rest of the code as it is now.
This is my application structure:
app
main.js
many other files and folders
scripts
myAPI
app.js
many other files and folders
require.js
jquery.js
build.js
index.htm
All the JavaScript code is located under the app and scripts folders. As I mentioned before, all those files are defined as RequireJS modules. This is how the main.js looks now:
require.config({
baseUrl: 'scripts',
paths: {
base: 'myAPI/base',
proxy: 'myAPI/proxyObject',
localization: 'myAPI/localization',
app: '../app',
}
});
require(['myAPI/app'],
function (app) {
//app.init....
}
);
As you can see, in the paths configuration I'm defining some aliases that point to myAPI (the folder I want to optimize).
This is how I reference RequireJS from the index.htm file:
<script data-main="app/main" src="scripts/require.js"></script>
This is the build file I created for r.js (scripts/build.js):
({
baseUrl: '.',
out: 'build/myAPI.js',
name: 'myAPI/app',
include: ['some modules'],
excludeShallow: ['some modules defined in the app folder'],
mainConfigFile: '../app/main.js',
optimize: 'uglify2',
optimizeCss: 'none'
})
The optimized file is generated, but I have some challenges trying to use it:
How do I reference that file from the app?
The dependencies to myAPI modules are broken now. RequireJS doesn't find the modules defined in myAPI.
What can I do to keep the aliases defined in require.config.paths working?
Could you please help me with some suggestions or feedback for this situation?
Thanks!!
After doing research, I could solve my problem. I based my solution in this Github project created by James Burke: https://github.com/requirejs/example-libglobal.
First, I had to remove all the dependencies to myAPI individual modules, and create an object that centralizes the access to any internal dependency. Then, I created a r.js script to generate a single file for myAPI. That file is the one that will consumed by the other JS files. That single file can be referenced as a global object or as an AMD module, as James Burke shows in the Github project.
The aliases defined in require.config.paths were no longer necessary.

requirejs optimizer - need help optimizing our application

We have a large scale javascript application that we are trying to concatenate and minify using grunt-contrib-requirejs. We use the Aura framework. We use bower to pull in dependencies from other repositories into our main application.
Here is our app structure
- app/
|
|_ main.js
|_ index.html
|_ css
|_ bower_components/
|_ core/
|_ widget1/
|_ main.js
|_ views/
|_ models/
|_ widget2/
|_ extension1/
|_ extension2/
|_ other third party libraries, etc
- Gruntfile.js
- bower.json
main.js:
requirejs.config({
shim: {
...
},
paths: {
'core': 'bower_components/core/app/main',
'aura': 'bower_components/aura/dist/',
'jquery': 'bower_components/jquery/jquery',
'backbone': ...,
... lots of other paths
}
});
define(['core', 'jquery', 'jqueryui'], function(core, $) {
// Here we start Aura, which finds our extensions and widgets in
// bower_components
});
Our current requirejs task config:
requirejs: {
compile: {
options: {
mainConfigFile: 'app/main.js',
name: 'main',
include: ['bower_components/widget1/main', 'bower_components/widget2/main',
'bower_components/extension1/main', 'bower_components/extension2/main'],
exclude: ['jquery'],
insertRequire: ['main'],
out: 'dist/app.js'
}
}
}
This concats our app/main and its dependencies, but when we try to run it, we get errors like:
GET http://www.my-machine:8080/bower_components/underscore/underscore.js 404 (Not Found)
even though underscore is a dependency of many of the widgets we include.
We have extensively tried different options in the r.js examples, and read through many stackover flow issues trying to find an answer.
We want advice on how to build this into one minified file with this structure:
UPDATE #2: Correct file structure
- dist/
|_ index.html // copied using grunt copy
|_ css // copied using grunt copy
|_ app.js // Built with grunt requirejs
UPDATE
We have included underscore in our shim which fixed the above error, but we're still getting another error:
Failed to load resource: the server responded with a status of 404 (Not Found)
http://my-machine.com:8080/bower_components/core/app/main.js
This is included in the minimized file so I don't understand why it can't find that file:
define("bower_components/core/app/main.js", ["aura/aura", "bootstrap"], function(){
...
});
UPDATE #3
The define above came from the file generated by the optimization tool! The original define for that module, core/app/main.js, looks like:
define(['aura', 'bootstrap'], function(Aura) {
...
});
You mention having this in your optimized file:
define("bower_components/core/app/main.js", ["aura/aura", "bootstrap"], function(){
...
});
But this does not make sense. If I reproduce a structure similar to what you show in your question and run an optimization on it then the file located at bower_components/core/app/main.js is optimized as:
define("core", ...
because it is known as core to the rest of your application due to the paths setting 'core': 'bower_components/core/app/main'.
I can reproduce the incorrect module name if I add 'bower_components/core/app/main.js' to the include setting in the build configuration. When I add this name to the include list, I also get the loading error you mentioned. Make sure that your include list does not contain this name, and make sure that there is nothing anywhere which lists 'bower_components/core/app/main.js' as a dependency. This name should not appear in any define or require call.
A couple of considerations first:
First of all, you should avoid defining module with a name. The name is to be generated by the optimization tool normally. Please read here: http://requirejs.org/docs/api.html#modulename
Second, you should understand what the baseUrl is and when it's used to located resources. The baseUrl seems to be "app/" in your case although you have not defined it explicitly. You should read this also:
http://requirejs.org/docs/api.html#config-baseUrl
So if you use the ".js" extension, then the baseUrl won't be taken into account.
Now what you should try:
To start with, remove the first argument "bower_components/core/app/main.js" from define function. Then you should refer to this module by auto-generated module id that will be a path relative to the baseUrl without ".js" extension.

Is it possible to exclude a module from a require optimizer build config but have that module and it's dependencies be optimized separately?

So the situation is the following:
I have a bunch of page specific js files which I'm optimizing using r.js.
99% of them define a a module called core.js as a dependency. Core has 5 dependencies of it's own.
I wanted to leverage caching by excluding core from the optimised versions of the page js files.
So in my build.js I tried something along the lines of:
modules: [
{
name : 'modules/core'
},
{
name: 'homepage',
exclude : ['modules/core']
}
]
When I run the optimiser it optimises both modules/core & homepage just fine.
Going to a page that uses homepage.js the issue is that: core.js & it's dependencies are being loaded in individually, leading to 7 requests instead of 2.
In the above example, what I'm trying to achieve is: have homepage.js and it's dependencies optimised into a single file and have it load in an optimised version of core.js rather then loading in the core.js and it's dependencies separately.
Is that even possible?
You have two options, after a build either:
1) modify the top level loading such that modules/core is loaded before any other loading:
require(['modules/core'], function () {
//Now do normal require stuff in here
});
Use another nested require() call in there for homepage if you see its modules requested before homepage finishes loading.
2) Insert a require.config block that points all the modules in core to the core file. requirejs will only fetch the core.js file once when multiple module IDs all point to it:
require.config({
paths: {
'mod/one': 'modules/core',
'mod/two', 'modules/core',
...
}
});
Or see this kind of example project that sets up loading a common and then a page-specific layer, but works without having to do a source modification after a build (just uses a variation of #1, but sets it up to work in source form):
https://github.com/requirejs/example-multipage

How to use RequireJS build profile + r.js in a multi-page project

I am currently learning RequireJS fundamentals and have some questions regarding a build profile, main files, and use of RequireJS with multi-page projects.
My project's directory structure is as follows:
httpdocs_siteroot/
app/
php files...
media/
css/
css files...
js/
libs/
jquery.js
require.js
mustache.js
mains/
main.page1.js
main.page2.js
main.page3.js
plugins/
jquery.plugin1.js
jquery.plugin2.js
jquery.plugin3.js
utils/
util1.js
util2.js
images/
Since this project is not a single-page app, I have a separate main file for each page (although some pages use the same main file).
My questions are:
Is RequireJS even practical for projects that are not single-page?
Without using the optimizer, each of my main files start with essentially the same config options:
requirejs.config({
paths: {
'jquery': 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min'
},
baseUrl: '/media/js/',
// etc...
});
require(['deps'], function() { /* main code */ });
Is there a way to avoid this? Like having each main file include the same build profile without having to actually build it?
Should r.js go in httpdocs_siteroot's parent directory?
Is there something glaringly wrong with my app dir structure or my use of RequireJS?
First of all, this is not a question with a unique solution. I'll explain the way I use RequireJS that works for me, and may work for you :)
Second, English is not my mother language. Corrections and tips about the language will be very appreciated. Feel free, guys :)
1) Is require js even practical for projects that are not single-page?
It depends. If your project does not have shared code between pages for example, RequireJS help will be modest. The main idea of RequireJS is modularize the application into chunks of reusable code. If your application uses only page-specific code, then using RequireJS may not be a good idea.
2) Without using the optimizer, each of my main files start with essentially the same config options. Is there a way to avoid this? Like having each main file include the same build profile without having to actually build it?
The only way I see is making the configuration on the main file, or create a module that will configure RequireJS and then use that module as the first dependency on main.js. But this can be tricky. I do not use many main.js files in my applications; I use only one that acts as a loader (see below).
3) Should r.js go in httpdocs_siteroot's parent directory?
Not necessarily. You can put it inside the /media directory, since all your client stuff is there.
4) Is there something glaringly wrong with my app dir structure or my use of requirejs?
I would not say that. On the other hand, the structure is perhaps a bit too fragmented. For example, you can put all '3rd party stuff' inside a /vendor directory. But this is just sugar; your structure will work well and seems right. I think the major problem is the requirejs.config() call in multiple main files.
I had the same problems you are having now and I ended up with the following solution:
1) Do not wrap the non-AMD-compliant files with a define. Although it works, you can achieve the same results using the "shim" property in requirejs.config (see below).
2) In a multi-page application, the solution for me is not to require the page-specific modules from the optimized main.js file. Instead, I require all the shared code (3rd party and my own) from the main file, leaving the page-specific code to load on each page. The main file ends up only being a loader that starts the page-specific code after loading all shared/lib files.
This is the boilerplate I use to build a multi-page application with requirejs
Directory structure:
/src - I put all the client stuff inside a src directory, so I can run the optimizer inside this directory (this is your media directory).
/src/vendor - Here I place all 3rd party files and plugins, including require.js.
/src/lib - Here I place all my own code that is shared by the entire application or by some pages. In other words, modules that are not page-specific.
/src/page-module-xx - And then, I create one directory for each page that I have. This is not a strict rule.
/src/main.js: This is the only main file for the entire application. It will:
configure RequireJS, including shims
load shared libraries/modules
load the page-specific main module
This is an example of a requirejs.config call:
requirejs.config({
baseUrl: ".",
paths: {
// libraries path
"json": "vendor/json2",
"jquery": "vendor/jquery",
"somejqueryplugion": "vendor/jquery.somejqueryplufin",
"hogan": "vendor/hogan",
// require plugins
"templ": "vendor/require.hogan",
"text": "vendor/require.text"
},
// The shim section allows you to specify
// dependencies between non AMD compliant files.
// For example, "somejqueryplugin" must be loaded after "jquery".
// The 'exports' attribute tells RequireJS what global variable
// it must assign as the module value for each shim.
// For example: By using the configutation below for jquery,
// when you request the "jquery" module, RequireJS will
// give the value of global "$" (this value will be cached, so it is
// ok to modify/delete the global '$' after all plugins are loaded.
shim: {
"jquery": { exports: "$" },
"util": { exports: "_" },
"json": { exports: "JSON" },
"somejqueryplugin": { exports: "$", deps: ["jquery"] }
}
});
And then, after configuration we can make the first require() request
for all those libraries and after that do the request for our "page main" module.
//libs
require([
"templ", //require plugins
"text",
"json", //3rd libraries
"jquery",
"hogan",
"lib/util" // app lib modules
],
function () {
var $ = require("jquery"),
// the start module is defined on the same script tag of data-main.
// example: <script data-main="main.js" data-start="pagemodule/main" src="vendor/require.js"/>
startModuleName = $("script[data-main][data-start]").attr("data-start");
if (startModuleName) {
require([startModuleName], function (startModule) {
$(function(){
var fn = $.isFunction(startModule) ? startModule : startModule.init;
if (fn) { fn(); }
});
});
}
});
As you can see in the body of the require() above, we're expecting another attribute on the require.js script tag. The data-start attribute will hold the name of the module for the current page.
Thus, on the HTML page we must add this extra attribute:
<script data-main="main" data-start="pagemodule/main" src="vendor/require.js"></script>
By doing this, we will end up with an optimized main.js that contains all the files in "/vendor" and "/lib" directories (the shared resources), but not the page-specific scripts/modules, as they are not hard-coded in the main.js as dependencies. The page-specific modules will be loaded separately on each page of the application.
The "page main" module should return a function() that will be executed by the "app main" above.
define(function(require, exports, module) {
var util = require("lib/util");
return function() {
console.log("initializing page xyz module");
};
});
EDIT
Here is example of how you can use build profile to optimize the page-specific modules that have more than one file.
For example, let's say we have the following page module:
/page1/main.js
/page1/dep1.js
/page1/dep2.js
If we do not optimize this module, then the browser will make 3 requests, one for each script.
We can avoid this by instructing r.js to create a package and include these 3 files.
On the "modules" attribute of the build profile:
...
"modules": [
{
name: "main" // this is our main file
},
{
// create a module for page1/main and include in it
// all its dependencies (dep1, dep2...)
name: "page1/main",
// excluding any dependency that is already included on main module
// i.e. all our shared stuff, like jquery and plugins should not
// be included in this module again.
exclude: ["main"]
}
]
By doing this, we create another per-page main file with all its dependencies. But, since we already have a main file that will load all our shared stuff, we don't need to include them again in page1/main module.
The config is a little verbose since you have to do this for each page module where you have more than one script file.
I uploaded the code of the boilerplate in GitHub: https://github.com/mdezem/MultiPageAppBoilerplate.
It is a working boilerplate, just install node and r.js module for node and execute build.cmd (inside the /build directory, otherwise it will fail because it uses relative paths)
I hope I have been clear. Let me know if something sounds strange ;)
Regards!
<script data-main="js/main" src="js/lib/require.js"></script>
// file: js/main
require(['./global.config'], function(){
require(['./view/home'], function() {
// do something
});
});
This is what I used in my project.

Categories

Resources