概要
WebアプリケーションにExpress + Jadeを使用していますが、AJAXナビゲーションの部分ビューのレンダリングに苦労しています。
私には2つの異なる質問がありますが、それらは完全にリンクされているため、同じ投稿にそれらを含めました。長い投稿になると思いますが、同じ問題に既に苦しんでいる人にとっては興味深いものになると思います。誰かが解決策を読んで提案するために時間を割いてくれたらとても感謝しています。
TL; DR:2問
要件
私のlayout.jadeは:
_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")
_
私のpage_full.jadeは:
_extends layout.jade
block content
h1 Hey Welcome !
_
私のpage_ajaxは:
_h1 Hey Welcome
_
そして最後にrouter.js(Express)で:
_app.get("/page",function(req,res,next){
if (req.xhr) res.render("page_ajax.jade");
else res.render("page_full.jade");
});
_
欠点:
layout.jadeは変更されません。
_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")
_
私のpage_full.jadeは今:
_extends layout.jade
block content
include page.jade
_
私のpage.jadeには、layout/block/extend/includeのない実際のコンテンツが含まれています。
_h1 Hey Welcome
_
そして今、私はrouter.js(Express)にあります:
_app.get("/page",function(req,res,next){
if (req.xhr) res.render("page.jade");
else res.render("page_full.jade");
});
_
利点:
欠点:
Alex Fordの手法 を使用すると、middleware.jsで独自のrender
関数を定義できます。
_app.use(function (req, res, next) {
res.renderView = function (viewName, opts) {
res.render(viewName + req.xhr ? null : '_full', opts);
next();
};
});
_
そしてrouter.js(Express)に変更します:
_app.get("/page",function(req,res,next){
res.renderView("/page");
});
_
他のファイルは変更せずに残します。
利点
欠点
renderView
メソッドを定義すると、少し汚い感じがします。結局のところ、テンプレートエンジン/フレームワークがこれを処理してくれることを期待しています。two ファイルを one ページに使用するのは好きではないので、Expressの代わりに何をレンダリングするかをJadeに決定させるとどうなりますか?一見したところ、テンプレートエンジンはロジックを処理する必要がありますnotと思うため、非常に不快に思われます。しかし、試してみましょう。
まず、Jadeに変数を渡して、それがどのような種類の要求であるかを伝える必要があります。
middleware.js(エクスプレス)
_app.use(function (req, res, next) {
res.locals.xhr = req.xhr;
});
_
したがって、今ではlayout.jadeは以前と同じになります。
_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")
_
そして、私のpage.jadeは次のようになります。
_if (!locals.xhr)
extends layout.jade
block content
h1 Hey Welcome !
_
すごい? Jadeでは conditional extends が不可能であるため、それは機能しません。したがって、テストをpage.jadeから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
_
およびpage.jadeは次のように戻ります。
_extends layout.jade
block content
h1 Hey Welcome !
_
利点:
req.xhr
_テストを繰り返す必要はありません欠点:
これらはすべて私が考え、試したテクニックですが、どれも私を本当に納得させませんでした。私は何か間違ったことをしていますか?よりクリーンなテクニックはありますか?または、別のテンプレートエンジン/フレームワークを使用する必要がありますか?
ビューに独自のJavaScriptファイルがある場合、(これらのソリューションのいずれかで)何が起こりますか?
たとえば、ソリューション#4を使用して、2つのページpage_a.jadeおよびpage_b.jadeがあり、両方に独自のクライアント側JavaScriptファイルがある場合js /page_a.jsおよびjs/page_b.js、AJAX?
最初に、layout.jadeでextraJS
ブロックを定義する必要があります。
_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
_
そしてpage_a.jadeは次のようになります。
_extends layout.jade
block content
h1 Hey Welcome !
block extraJS
script(src="js/page_a.js")
_
URLバー(非AJAXリクエスト)に_localhost/page_a
_と入力すると、次のコンパイル済みバージョンが取得されます。
_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")
_
それはよさそうだ。しかし、 AJAX navigation を使用して_page_b
_に移動した場合、どうなりますか?私のページは、次のコンパイル済みバージョンになります。
_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とjs/page_b.jsは両方とも同じページにロードされます。競合(同じ変数名など)がある場合はどうなりますか?さらに、AJAXを使用してlocalhost/page_aに戻ると、次のようになります。
_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")
_
同じJavaScriptファイル(page_a.js)が同じページに2回読み込まれます!競合、各イベントのダブルファイアリングが発生しますか?そうであるかどうかにかかわらず、私はそれがきれいなコードだとは思わない。
そのため、特定のJSファイルを_block content
_に入れて、別のページに移動したときに削除されるようにする必要があると言うかもしれません。したがって、私の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
block extraJS
// Shared JS files go here
script(src="js/jquery.min.js")
else
block content
// Specific JS files go there
block extraJS
_
正しい ?エラー.... _localhost/page_a
_にアクセスすると、次のコンパイル済みバージョンが取得されます。
_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")
_
お気づきかもしれませんが、js/page_a.jsは実際に] / jQueryの前にロードされるため、jQueryはまだ定義されていないため機能しません。 。だからこの問題をどうするか。クライアント側で(たとえば)jQuery.getScript()
を使用してスクリプトリクエストを処理することを考えましたが、クライアントはスクリプトのファイル名を知っている必要があります。クライアント側で行うべきではないと思います。
AJAX?別の戦略/テンプレートエンジンを使用するサーバー側?クライアント側?でロードされたJavaScriptファイルをどのように処理すればよいですか?] ==
ここまでやってきたなら、あなたは真のヒーローであり、私は感謝していますが、アドバイスをいただければさらに感謝しています:)
いい質問ですね。完璧な選択肢はありませんが、私が気に入っているソリューション#3のバリエーションを提供します。ソリューション#3と同じ考えですが、_fullファイルのヒスイテンプレートをコードに移動します。これはボイラープレートであり、javascriptはフルページに必要なときに生成できるためです。免責事項:テストされていませんが、私は謙虚に提案します:
app.use(function (req, res, next) {
var template = "extends layout.jade\n\nblock content\n include ";
res.renderView = function (viewName, opts) {
if (req.xhr) {
var renderResult = jade.compile(template + viewName + ".jade\n", opts);
res.send(renderResult);
} else {
res.render(viewName, opts);
}
next();
};
});
また、シナリオが複雑になるにつれて、このアイデアをより賢くすることができます。たとえば、ファイル名のプレースホルダーを使用してこのテンプレートをファイルに保存します。
もちろん、これはまだ完璧な解決策ではありません。ソリューション#3に対する元の反対と同じように、テンプレートエンジンで実際に処理する必要がある機能を実装しています。このために数十行以上のコードを書くことになった場合は、機能をJadeに適合させる方法を見つけて、それらにプルリクエストを送信してください。たとえば、jadeの「extends」キーワードが、xhrリクエストのレイアウトの拡張を無効にする可能性のある引数をとった場合...
2番目の問題については、テンプレートエンジンが役立つかどうかわかりません。 ajaxナビゲーションを使用している場合、ナビゲーションが何らかのバックエンドテンプレートマジックを介して行われたときにpage_a.jsを「アンロード」できません。このためには、従来のjavascript分離手法を使用する必要があると思います(クライアント側)。特定の懸念事項:クロージャーにページ固有のロジック(および変数)を最初に実装し、必要に応じてそれらの間で賢明な協力を行います。第二に、ダブルイベントハンドラーの接続についてあまり心配する必要はありません。メインコンテンツがajaxナビゲーションでクリアされ、それらが新しいハンドラーがDOMにロードされたときに(もちろん)get resetを呼び出すイベントハンドラーがアタッチされた要素であると仮定します。
本当に役立つかどうかはわかりませんが、Expressとejsテンプレートで同じ問題を解決しました。
私のフォルダ:
私のapp.js
app.get('/page', function(req, res) {
if (req.xhr) {
res.render('page',{ajax:1});
} else {
res.render('page',{ajax:0});
}
});
私のpage.ejs
<% if (ajax == 0) { %> <% include header %> <% } %>
content
<% if (ajax == 0) { %> <% include footer %><% } %>
非常にシンプルですが、完璧に動作します。
あなたが尋ねたことを行うにはいくつかの方法がありますが、それらはすべてアーキテクチャ上悪いです。気づかないかもしれませんが、時間の経過とともに悪化します。
この時点では奇妙に聞こえますが、本当にあなたはJS(AJAX経由でCSS)を送信したくない.
必要なのは、AJAXを介してマークアップを取得し、その後Javascriptを実行できるようにすることです。また、これを行う際に柔軟性と合理性が必要です。スケーラビリティも重要です。
一般的な方法は次のとおりです。
特定のページですべてのjs
ファイルをプリロードし、そのAJAX要求ハンドラーを使用できます。潜在的な名前が競合またはスクリプトを恐れる場合は browserify を使用します副作用:ユーザーを待たせないために、非同期に( script async )ロードします。
基本的にこれをクライアントで実行できるようにするアーキテクチャを開発します:loadPageAjax('page_a').then(...).catch(...)
。ポイントは、すべてのJSモジュールを事前にダウンロードしているため、AJAXページ関連のコードをリクエストではなく、リクエストのcallerで実行することですcallee。
したがって、あなたのSolution#4はほぼ良好です。スクリプトを使用せずにHTMLを取得するために使用します。
この手法は、あいまいなアーキテクチャを構築せずに目標を達成しますが、単一の堅牢な目標限定方法を使用します。
さらに質問に答えて、あなたがしようとしていることをしないようにあなたを説得して喜んで。