Резюме
Я использую Express + Jade для своего веб-приложения, и я изо всех сил пытаюсь отобразить частичные представления для моей навигации AJAX.
У меня есть два разных вопроса, но они полностью связаны, поэтому я включил их в один пост. Я думаю, это будет длинный пост, но я гарантирую, что это интересно, если вы уже боролись с теми же проблемами. Я был бы очень признателен, если бы кто-то нашел время, чтобы прочитать и предложить решение.
TL; DR: 2 вопроса
- Что такое самый чистый и самый быстрый способ визуализации фрагментов просмотров для навигации AJAX с помощью Express + Jade?
- Как загружать файлы JavaScript по отношению к каждому представлению?
Требования
- Мое веб-приложение должно быть совместимо с пользователями, отключенными
JavaScript - Если JavaScript включен, только собственный контент страницы (а не весь макет) должны быть отправлены с сервера на клиент
- Приложение должно быть быстрым и загружать как можно меньше байтов
Проблема №1: что я пробовал
Решение 1: наличие разных файлов для запросов AJAX & non-AJAX
Мой 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 (Экспресс):
app.get("/page",function(req,res,next){
if (req.xhr) res.render("page_ajax.jade");
else res.render("page_full.jade");
});
Недостатки:
- Как вы, наверное, догадались, мне приходится каждый раз редактировать свои взгляды каждый раз, когда я нужно что-то изменить. Довольно неприятно.
Решение 2: тот же метод с `include`
Мой 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
My page.jade содержит фактическое содержимое без какого-либо макета/блока/расширения/включения:
h1 Hey Welcome
И теперь у меня есть router.js (Экспресс):
app.get("/page",function(req,res,next){
if (req.xhr) res.render("page.jade");
else res.render("page_full.jade");
});
Преимущества:
- Теперь мой контент определяется только один раз, что лучше.
Недостатки:
- Мне еще нужны два файла для одной страницы.
- Я должен использовать ту же технику в Express на каждом маршруте. я просто переместил мою проблему повторения кода из Jade в Express. Привязать.
Решение 3: то же, что и решение 2, но исправление проблемы повторения кода.
Используя технику Alex Ford, я мог бы определить свою собственную функцию render
в middleware.js:
app.use(function (req, res, next) {
res.renderView = function (viewName, opts) {
res.render(viewName + req.xhr ? null : '_full', opts);
next();
};
});
И затем измените router.js (Экспресс) на:
app.get("/page",function(req,res,next){
res.renderView("/page");
});
оставление остальных файлов без изменений.
<сильные > Преимущества
- Он решил проблему повторения кода
Недостатки
- Мне еще нужны два файла для одной страницы.
- Определение моего собственного метода
renderView
кажется грязным. В конце концов, я ожидайте, что мой механизм шаблонов/фреймворк обработает это для меня.
Решение 4: Перемещение логики в Jade
Мне не нравится использовать два файла для одной страницы, так что, если я позволю Джейд решить, что делать вместо Express? На первый взгляд мне кажется очень неудобно, потому что я думаю, что механизм шаблонов должен не обрабатывать любую логику вообще. Но попробуй.
Во-первых, мне нужно передать переменную 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 !
Великий, да? За исключением того, что это не сработает, потому что условные расширения невозможны в Джейд. Поэтому я могу перенести тест с 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
на каждом маршруте или в каждом вид
Недостатки:
- В моем шаблоне есть логика. Не хорошо
Резюме
Это все методы, о которых я думал и пытался, но никто из них не убедил меня. Я делаю что-то неправильно? Существуют ли более чистые методы? Или я должен использовать другой механизм/структуру шаблона?
Проблема № 2
Что происходит (с любым из этих решений), если представление имеет свои собственные файлы JavaScript?
Например, используя решение # 4, если у меня есть две страницы, page_a.jade и page_b.jade, у которых есть свои собственные клиентские файлы JavaScript js/page_a.js и js/page_b.js, что происходит с ними при загрузке страниц в AJAX?
Во-первых, мне нужно определить блок extraJS
в 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
а затем page_a.jade будет:
extends layout.jade
block content
h1 Hey Welcome !
block extraJS
script(src="js/page_a.js")
Если я набрал localhost/page_a
в моей строке URL (не-AJAX-запрос), я бы получил скомпилированную версию:
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")
Это выглядит хорошо. Но что произойдет, если я перейду к page_b
с помощью моей навигации AJAX? Моя страница будет скомпилированной версией:
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 загружаются на одну страницу. Что произойдет, если есть конфликт (такое же имя переменной и т.д.)? Кроме того, если я вернусь к localhost/page_a с помощью AJAX, у меня будет следующее:
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) загружается дважды на одной странице! Будет ли это причиной конфликтов, двойного обжига каждого события? Независимо от того, действительно ли это дело, я не считаю его чистым кодом.
Итак, вы можете сказать, что определенные 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
Правильно? Err.... Если я перейду к 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 еще не определен...
Поэтому я не знаю , что делать для этой проблемы. Я думал о обработке запросов script на стороне клиента, используя (например) jQuery.getScript()
, но клиенту нужно было бы узнать имя файла скриптов, посмотреть, загружены ли они, возможно, удалить их. Я не думаю, что это должно быть сделано на стороне клиента.
Как мне обрабатывать файлы JavaScript, загружаемые через AJAX? На стороне сервера используется другой механизм стратегии/шаблона? Клиентская сторона?
Если вы сделали это так далеко, вы настоящий герой, и я благодарен, но я был бы еще более благодарен, если бы вы могли дать мне несколько советов:)