RequireJS ist ein Framework zum Laden von Dependencies in Javascript.

Konfiguration#

Zunächst wird require.js selbst durch einen script-Tag im HTML-Header geladen:
<script type="text/javascript" src="js/require.js" data-main="main"></script>

Das main.js befindet sich üblicher Weise im Wurzelpfad der Anwendung, um andere Scripts relativ dazu laden zu können. Das Attribut data-main spezifiziert dabei das Main-Script.

Main#

Das Main kann wie folgt aussehen:
requirejs.config({
	paths: {
		jquery: "js/jquery-3.4.1.min",
		"jquery-ui": "js/jquery-ui.min",
		vue: "js/vue.min",
		"vue-jquery": "js/vue-jquery",
		reactor: "js/reactor",
		css: "js/require-css"
	}
});

require(["app/app"]);

paths: Deklaration von Require-Dependencies, die mit einfachem Namen geladen werden können. require: Laden der eigentlichen App.

Loader#

Für andere Datentypen als JS-Dateien werden Loader als Plugins genutzt. Dafür wird die Syntax "ext!path/to/file" verwendet. "ext" ist dabei die Dateiextension, also z.B. "css". Require versucht dann automatisch das Dependency mit dem Namen "css" zu laden, also "css.js".

Loader für CSS#

Der Loader für einen Dependency-Typ kann dabei selbst wieder unter "paths" definiert werden. Oben wird angegeben:
requirejs.config({
	paths: {
		css: "js/require-css"
	}
});

Die require-css.js kann wie folgt aussehen:

define(["jquery"], function($) {
	return {
		load: function(name, req, onload, config) {
			onload($("<link>")
				.attr({
					type: "text/css",
					rel: "stylesheet",
					href: name + ".css"
				})
				.appendTo("head")
			);
		}
	};
});

Die CSS-Dateien können dann, wie JS-Requirements, im require- oder define-Statement geladen werden:

define(["vue", "jquery", "reactor", "app/test-element", "css!css/reactive-component"], function(Vue, $) {
	[...]
});

Base URL#

Möchte man den config.baseUrl berücksichtigen, wäre die theoretisch richtige Antwort:
function(name, req, onload, config) {
	var url = req.toUrl(name + ".css");
}

Wie auch in verschiedenen Foren zu finden ist, legt RequireJS unterschiedliches Verhalten an den Tag. Vor allem im Browser entstehen dadurch offenbar falsche URLs. In eigenen Loadern lässt sich das beheben durch:

function(name, req, onload, config) {
	var url = req.toUrl(config.baseUrl + name + ".css");
};

Packages#

Mit RequireJS können können komplette Projekte als Packet importiert werden:
requirejs.config({
	packages: [{
		name: "static-ui",
		location: "/service/static-ui"
	}]
});

Dabei sind folgende Dinge zu beachten:

  • Es wird ein main.js in der location erwartet
  • Es wird nur das define aus dem main importiert
    • Untergeordnete Module werden nur im importierten Package selbst sauber geladen
    • Werden dieselben Module aus dem übergeordneten Package benötigt, müssen diese dort (nochmals) definiert werden
  • Packages sind kein Massen-Path-Import
  • Jedes Package, egal ob übergeordnet oder untergeordnet, hat die Paths sauber zu definieren

Erklärung: Aufgrund des asynchronen Ladens ist unklar, ob das Package zuerst aus dem Unterpackage oder Hauptpackage geladen wird. Wird es aus dem Unterpackage geladen, ist die Definition richtig. Wird es zuerst aus dem Hauptpackage geladen und fehlt dort die Definition, wird die Standardauflösung baseUrl/<name>.js verwendet, die möglicherweise falsch ist.

JQuery#

Obwohl JQuery mit Unterstützung für RequireJS kommt, installiert es die globalen Variablen $ und jQuery. Die Begründung liegt wohl in der Kompatibilität mit anderen JQuery-Modulen. Der Work-around liegt darin, JQuery zunächst unter anderem Namen zu laden und dann unter "jquery" zu definieren und dann mit jQuery.noConflict(true) umzudefinieren:
requirejs.config({
	paths: {
		"jquery-internal": "vue/js/jquery-3.4.1.min",
		jquery: "vue/js/jquery-noconflict"
	}
});

Mit jquery-noconflict.js:

define(["jquery-internal"], function() {
	return jQuery.noConflict(true);
});

Hinweis: Im Browser ist es leider nicht möglich, auf Ressourcen zuzugreifen, die nicht in "paths" definiert wurden. Ansonsten wird die absolute URL falsch zusammengesetzt.