Wie man partielle Ansichten richtig rendert und JavaScript-Dateien in AJAX mit Express / Jade lädt?
Zusammenfassung
Ich bin mit Express + Jade für meine web-Anwendung, und ich habe Mühe mit der Darstellung teilweise den Blick für meine AJAX-navigation.
Ich habe zwei verschiedene Fragen, aber Sie sind Total miteinander verknüpft, so dass ich Sie in der gleichen post.
Ich denke, es wird ein langer post, aber ich garantiere, es ist interessant, wenn Sie bereits kämpfen mit den gleichen Problemen. Ich würde es sehr schätzen, wenn sich jemand die Zeit genommen, zu Lesen & eine Lösung vorschlagen.
TL;DR : 2 Fragen
- Was ist der saubersten und Schnellste Weise zu erbringen Fragmente von Ansichten für eine AJAX-navigation mit Express + Jade ?
- Wie sollte die JavaScript-Dateien relativ zu jeder Ansicht, die geladen werden ?
Anforderungen
- Meine Web-App muss kompatibel sein mit Benutzer deaktiviert
JavaScript - Wenn JavaScript aktiviert ist, wird nur die Seite die eigenen Inhalte (und nicht die
gesamte layout) gesendet werden sollen, die vom server an den client - Die app muss schnell sein, und laden so wenig bytes wie möglich
Problem #1 : was ich versucht habe
Lösung 1 : haben unterschiedliche Dateien für die AJAX - & non-AJAX-Anfragen
Meine layout.jade ist :
doctype html
html(lang="fr")
head
//Shared CSS files go here
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
block content
//Shared JS files go here
script(src="js/jquery.min.js")
Meine page_full.jade ist :
extends layout.jade
block content
h1 Hey Welcome !
Meine page_ajax ist :
h1 Hey Welcome
Und schließlich in router.js (Express) :
app.get("/page",function(req,res,next){
if (req.xhr) res.render("page_ajax.jade");
else res.render("page_full.jade");
});
Nachteile :
- Wie Sie wahrscheinlich schon erraten, ich habe zum Bearbeiten meiner Ansichten, zweimal, jedes mal, wenn ich
Notwendigkeit, etwas zu ändern. Ziemlich frustrierend sein.
Lösung 2 : gleiche Technik, mit `include`
Meine layout.jade bleibt unverändert :
doctype html
html(lang="fr")
head
//Shared CSS files go here
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
block content
//Shared JS files go here
script(src="js/jquery.min.js")
Meine page_full.jade ist jetzt :
extends layout.jade
block content
include page.jade
Meine Seite.jade enthält den eigentlichen Inhalt ohne layout/block/erweitern/include :
h1 Hey Welcome
Habe ich jetzt in router.js (Express) :
app.get("/page",function(req,res,next){
if (req.xhr) res.render("page.jade");
else res.render("page_full.jade");
});
Vorteile :
- Jetzt meine Inhalte nur einmal definiert, so ist besser.
Nachteile :
- Ich noch zwei Dateien für eine Seite.
- Ich habe mit der gleichen Technik in Express auf jeder route. Ich
gerade zog mein code-Wiederholung-problem aus dem Jade-Express. Snap.
Lösung 3 : wie Lösung 2 aber die Befestigung der code-Wiederholung problem.
Mit Alex Ford-Technikdie ich definieren konnte, meine eigenen render
Funktion in middleware.js :
app.use(function (req, res, next) {
res.renderView = function (viewName, opts) {
res.render(viewName + req.xhr ? null : '_full', opts);
next();
};
});
Und ändern Sie dann router.js (Express):
app.get("/page",function(req,res,next){
res.renderView("/page");
});
wobei die anderen Dateien unverändert.
Vorteile
- Es gelöst der code Wiederholung problem
Nachteile
- Ich noch zwei Dateien für eine Seite.
- Die Definition meiner eigenen
renderView
Methode fühlt sich etwas schmutzig. Nachdem alle, ich
erwarte, dass meine template-engine/framework, um diese zu bewältigen für mich.
Lösung 4 : Verschieben der Logik Jade
Mag ich nicht mit zwei Dateien für eine Seite, so was ist, wenn ich ließ Jade sich entscheiden, was zu Rendern, anstatt Express ? Auf den ersten Blick scheint es sehr unangenehm für mich, weil ich denke, dass die template-engine nicht behandeln jede Logik überhaupt. Aber lassen Sie uns versuchen.
Zuerst muss ich übergeben Sie eine variable, um Jade, wird es sagen, welche Art von Anfrage es ist :
In middleware.js (Express)
app.use(function (req, res, next) {
res.locals.xhr = req.xhr;
});
So, jetzt meine layout.jade wäre die gleiche wie zuvor :
doctype html
html(lang="fr")
head
//Shared CSS files go here
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
block content
//Shared JS files go here
script(src="js/jquery.min.js")
Und meine Seite.jade wäre :
if (!locals.xhr)
extends layout.jade
block content
h1 Hey Welcome !
Klasse, oder ? Außer, dass wird nicht funktionieren, weil bedingte erstreckt sind unmöglich in Jade. Also ich konnte das testen von Seite.jade zu layout.jade :
if (!locals.xhr)
doctype html
html(lang="fr")
head
//Shared CSS files go here
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
block content
//Shared JS files go here
script(src="js/jquery.min.js")
else
block content
und Seite.jade zurück zu :
extends layout.jade
block content
h1 Hey Welcome !
Vorteile :
- Jetzt habe ich nur eine Datei pro Seite
- Ich nicht wiederholen Sie den
req.xhr
test in jeder route oder in jedem
Ansicht
Nachteile :
- Es ist die Logik in meinem template. Nicht gut
Zusammenfassung
Diese sind alle Techniken, die ich gedacht und ausprobiert, aber keine davon hat mich wirklich überzeugt. Mache ich etwas falsch ? Gibt es Reiniger Techniken ? Oder sollte ich eine andere template-engine/framework ?
Problem #2
Was passiert (mit einer dieser Lösungen), wenn eine Ansicht hat Ihre eigene JavaScript-Dateien ?
Zum Beispiel mit Lösung #4, wenn ich zwei Seiten, page_a.jade und page_b.jadedie beide Ihre eigenen client-side JavaScript-Dateien js/page_a.js und js/page_b.jswas Ihnen geschieht, wenn die Seiten geladen sind, in AJAX ?
Erste, die ich brauche, um zu definieren, eine extraJS
block in layout.jade :
if (!locals.xhr)
doctype html
html(lang="fr")
head
//Shared CSS files go here
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
block content
//Shared JS files go here
script(src="js/jquery.min.js")
//Specific JS files go there
block extraJS
else
block content
//Specific JS files go there
block extraJS
dann page_a.jade wäre :
extends layout.jade
block content
h1 Hey Welcome !
block extraJS
script(src="js/page_a.js")
Wenn ich getippt localhost/page_a
in meine URL-Leiste (non-AJAX-request)ich würde eine kompilierte version von :
doctype html
html(lang="fr")
head
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
h1 Hey Welcome A !
script(src="js/jquery.min.js")
script(src="js/page_a.js")
Sieht gut aus. Aber was würde passieren, wenn ich ging jetzt page_b
mit meinem AJAX-navigation ? Meine Seite wäre eine kompilierte version von :
doctype html
html(lang="fr")
head
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
h1 Hey Welcome B !
script(src="js/page_b.js")
script(src="js/jquery.min.js")
script(src="js/page_a.js")
js/page_a.js und js/page_b.js sind beide geladen, auf der gleichen Seite. Was passiert, wenn es einen Konflikt gibt (gleichen Variablen-Namen, etc...) ?
Plus, wenn ich zurück zu localhost/page_a mit AJAX, ich hätte dieses :
doctype html
html(lang="fr")
head
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
h1 Hey Welcome B !
script(src="js/page_a.js")
script(src="js/jquery.min.js")
script(src="js/page_a.js")
Den gleichen JavaScript-Datei (page_a.js) geladen ist zweimal auf der gleichen Seite ! Wird es dazu führen, dass Konflikte, Doppel-Entlassung von jedem Ereignis ?
Ob oder nicht, das ist der Fall, ich glaube nicht, dass es sauberen code.
So dass man sagen könnte, dass bestimmte JS-Dateien sollte in meinem block content
so, dass Sie entfernt, wenn ich auf wechseln zu einer anderen Seite. So, meine layout.jade werden sollte :
if (!locals.xhr)
doctype html
html(lang="fr")
head
//Shared CSS files go here
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
block content
block extraJS
//Shared JS files go here
script(src="js/jquery.min.js")
else
block content
//Specific JS files go there
block extraJS
Recht ? Err....Wenn ich mich localhost/page_a
bekomme ich eine kompilierte version von :
doctype html
html(lang="fr")
head
link(type="text/css",rel="stylesheet",href="css/bootstrap.min.css")
body
div#main_content
h1 Hey Welcome A !
script(src="js/page_a.js")
script(src="js/jquery.min.js")
Wie Sie vielleicht bemerkt haben, js/page_a.js ist tatsächlich geladen vor jQuery, so wird es nicht funktionieren, denn jQuery ist noch nicht definiert...
Also ich weiß nicht, was zu tun für dieses problem. Ich dachte, der Umgang mit Skript-Anfragen der client-Seite verwenden (Beispiel) jQuery.getScript()
, aber der Kunde möchte wissen, welche Skripte' mit dem Namen, sehen Sie, wenn Sie schon geladen, vielleicht entfernen Sie Sie. Ich glaube nicht, dass es getan werden sollte-client-Seite.
Wie soll ich das machen JavaScript Dateien geladen via AJAX ? Server-Seite mit einer anderen Strategie/template-engine ? Client-Seite ?
Wenn Sie es bis hierher geschafft haben, du bist ein echter held, und ich bin dankbar, aber ich wäre noch dankbar, wenn Sie könnten mir einige Ratschläge geben 🙂
InformationsquelleAutor der Frage Waldo Jeffers | 2014-08-05
Du musst angemeldet sein, um einen Kommentar abzugeben.
Große Frage. Ich habe nicht eine perfekte option, aber ich werde bieten eine Variante Ihrer Lösung #3, den ich mag. Gleiche Idee wie Lösung #3 bewegen sich aber die jade-template für die _full-Datei in Ihrem code, da ist es boilerplate und javascript generieren können, wenn benötigt, für eine volle Seite. Haftungsausschluss: ungetestet, aber ich demütig vorschlagen:
Und Sie können mehr gut mit dieser Idee, wie Sie Ihre Szenarien komplizierter werden, zum Beispiel, speichern Sie diese Vorlage, um eine Datei mit Platzhalter für Dateinamen.
Natürlich ist dies noch keine perfekte Lösung. Sie implementieren features, die sollte wirklich behandelt werden, indem Sie Ihre template-engine, wie Sie Ihre ursprünglichen Einwände Lösung #3. Wenn Sie am Ende schreiben, mehr als ein paar Dutzend Zeilen code für diese dann versuchen einen Weg zu finden, passen Sie die Funktion in Jade und senden Sie einen pull-request. Zum Beispiel, wenn der jade "extends" nahm ein argument, dass deaktivieren die Verlängerung des layout für xhr-Anfragen...
Für dein zweites problem, ich bin mir nicht sicher, dass alle template-engine kann Ihnen helfen. Wenn Sie mit ajax-navigation, können Sie nicht sehr gut "entladen" page_a.js wenn die navigation erfolgt über ein back-end-Vorlage Magie. Ich denke, dass Sie bei Verwendung der herkömmlichen javascript-isolation Techniken für diese (client-Seite). Zu Ihrem konkreten Anliegen: Umsetzung der Seite spezifische Logik (und Variablen), die in Schließungen, für starter und eine sinnvolle Zusammenarbeit zwischen Ihnen, wo nötig. Zweitens, Sie brauchen nicht zu viel sorgen machen Einhaken Doppel-event-Handler. Vorausgesetzt, der wesentliche Inhalt wird gelöscht, ajax navigation und auch das sind die Elemente, die den event-Handler angebracht aufrufen, werden wieder (natürlich), wenn die neue ajax-Inhalte in das DOM geladen.
InformationsquelleAutor der Antwort Segfault
Nicht sicher, ob es wirklich um Ihnen zu helfen, aber ich löste das gleiche problem mit Express und ejs-template.
meinem Ordner:
meine app.js
meiner Seite.ejs
sehr einfach, aber funktioniert perfekt.
InformationsquelleAutor der Antwort lmaix
Gibt es mehrere Möglichkeiten, das zu tun, was Sie gefragt, aber alle von Ihnen sind schlecht, architektonisch. Sie können nicht feststellen, dass jetzt, aber es wird schlimmer und schlimmer mit der Zeit.
Klingt seltsam, an diesem Punkt, aber Sie nicht wirklich wollen übertragen Sie JS, CSS via AJAX.
Was Sie wollen, ist in der Lage, um das markup via AJAX und Javascript nach. Auch Sie wollen flexibel sein und rational, während dies zu tun. Skalierbarkeit ist ebenso wichtig.
Den gemeinsamen Weg ist:
Preload alle
js
Dateien, die verwendet werden könnten, an die bestimmte Seite und seine AJAX-Anfragen-Handler. Verwenden browserify wenn Ihr Angst vor möglichen Namen, die Konflikte oder ein Skript Nebeneffekte. Laden Sie asynchron ( script async ), um nicht die Benutzer zu warten.Entwickeln eine Architektur, die es Ihnen ermöglicht, das zu tun, im Grunde, das auf dem client:
loadPageAjax('page_a').then(...).catch(...)
. Der Punkt ist, da haben Sie vorab heruntergeladen, alle JS Module, laufen Sie AJAX-Seite im Zusammenhang mit code an der Anrufer der Anfrage-und nicht an der angerufene.So, Ihr Lösung #4 ist fast gut. Nur es verwenden, um abrufen der HTML, aber nicht in Skripts.
Dieser Technik wird Ihr Ziel getan, ohne Gebäude einige obskure Architektur, aber mit der einzigen robusten Ziel-begrenzt.
Ihnen gerne weitere Fragen beantworten und Sie zu überzeugen, nicht das zu tun, was Sie gehen zu.
InformationsquelleAutor der Antwort Kirill Rogovoy