Используя Rails 3.1, куда вы помещаете свой «специфичный для страницы» код JavaScript?


388

Насколько я понимаю, все ваши JavaScript объединены в один файл. Rails делает это по умолчанию, когда добавляет //= require_tree .в application.jsконец файла манифеста.

Это звучит как реальный спаситель жизни, но я немного обеспокоен специфичным для страницы кодом JavaScript. Этот код выполняется на каждой странице? Последнее, что я хочу, - чтобы все мои объекты создавались для каждой страницы, когда они нужны только на 1 странице.

Кроме того, не существует ли потенциал для кода, который конфликтует тоже?

Или вы помещаете небольшой scriptтег внизу страницы, который просто вызывает метод, который выполняет код JavaScript для этой страницы?

Вам больше не нужен require.js?

Спасибо

РЕДАКТИРОВАТЬ : Я ценю все ответы ... и я не думаю, что они действительно решают проблему. Некоторые из них касаются стиля и, похоже, не имеют отношения ... а другие просто упоминают javascript_include_tag... что я знаю, что существует (очевидно ...), но может показаться, что путь к Rails 3.1 - это завершение всего Ваш JavaScript в 1 файл вместо загрузки отдельного JavaScript внизу каждой страницы.

Лучшее решение, которое я могу придумать, - это обернуть некоторые функции в divтеги ids или classes. В коде JavaScript вы просто проверяете, присутствует ли idили classна странице, и, если это так, вы запускаете связанный с ним код JavaScript. Таким образом, если динамический элемент отсутствует на странице, код JavaScript не запускается, даже если он включен в большой application.jsфайл, упакованный Sprockets.

Мое вышеупомянутое решение имеет то преимущество, что если окно поиска включено на 8 из 100 страниц, оно будет работать только на этих 8 страницах. Вам также не придется включать этот код на 8 страницах сайта. На самом деле, вам больше никогда не придется добавлять теги сценариев вручную на ваш сайт.

Я думаю, что это фактический ответ на мой вопрос.


11
«Путь Rails 3.1 в будущем заключается в том, чтобы заключить весь ваш Javascript в 1 файл, а не загружать отдельный Javascript внизу каждой страницы». - Только потому, что основная команда Rails плохо знает, как управлять JavaScript. Небольшие файлы, как правило, лучше (см. Мои комментарии в другом месте). Когда дело доходит до JavaScript, путь Rails редко бывает правильным (за исключением конвейера ресурсов, который пинает задницу и поощряет CoffeeScript).
Марнен Лайбоу-Козер

Таким образом, вы будете включать свои js-файлы для каждой страницы на каждой странице? Я думаю, что это пустая трата времени, я больше согласен с ответом ClosureCowboy.
Gerky

1
Вы смотрели на принятый ответ на этот вопрос? stackoverflow.com/questions/6571753/…
rassom

1
@DutGRIFF Другими словами: нет, в этом случае лучше не делать то, что делает Rails (или, по крайней мере, не помещать все application.js), и фактически указанная вами ссылка указывает, почему это так: загрузка - это самая медленная часть процесса выполнения JS. Многие маленькие файлы более кешируются, чем один большой. Таким образом, люди Unholy Rails, похоже, не осознают, что их рекомендации не соответствуют принципам, которых они пытаются придерживаться, и поэтому к их рекомендациям не следует относиться серьезно.
Марнен Лайбоу-Козер

1
@DutGRIFF Нет, большой JS-файл обычно не годится, даже если он кешируется. Смотрите мои комментарии в другом месте на этой странице: маленькие файлы могут лучше нацеливаться на определенные страницы и могут кэшироваться с более высокой степенью детализации. Я не вижу ни одного хорошего варианта использования для одного большого файла, если нет кода для конкретной страницы вообще .
Марнен Лайбоу-Козер

Ответы:


157

Документы Asset Pipeline предлагают, как сделать JS для конкретного контроллера:

Например, если ProjectsControllerсгенерировано, будет новый файл в, app/assets/javascripts/projects.js.coffeeа другой в app/assets/stylesheets/projects.css.scss. Вы должны поместить любой JavaScript или CSS, уникальный для контроллера, в их соответствующие файлы ресурсов, так как эти файлы затем могут быть загружены только для этих контроллеров со строками, такими как <%= javascript_include_tag params[:controller] %>или <%= stylesheet_link_tag params[:controller] %>.

Ссылка на: asset_pipeline


50
Это самый элегантный способ сделать это. Но вам также нужно удалить строку // = require_tree. из application.js.coffee
zsljulius

2
Я полностью согласен с этим методом. Другие методы кажутся очень неуклюжими и все же заканчивают тем, что загружали гигантский файл js. Проект, над которым я работаю, содержит почти 2 МБ файлов / плагинов и т. Д. ПОСЛЕ объединения / минимизации.
Билл гарнизон

2
Я довольно новичок в Rails, но мне кажется, что это должно быть поведение по умолчанию.
Росс Хамбрик

12
Для контроля действий у меня есть это в моей компоновке, поскольку не каждое действие для каждого контроллера имеет определенный JS. page_specific_js = "#{params[:controller]}_#{params[:action]}"а потом; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Суджимичи

2
Определенные действия контроллера все еще уменьшены? Добавлены ли они в один файл js, созданный звездочками, или это приводит к многочисленным запросам файлов активов?
Джейсон

77

Для js для конкретной страницы вы можете использовать решение Garber-Irish .

Таким образом, ваша папка Rails javascripts может выглядеть так для двух контроллеров - автомобилей и пользователей:

javascripts/
├── application.js
├── init.js
├── markup_based_js_execution
├── cars
   ├── init .js
   ├── index.js
   └── ...
└── users
    └── ...

И javascripts будет выглядеть так:

// application.js

//= 
//= require init.js
//= require_tree cars
//= require_tree users

// init.js

SITENAME = new Object();
SITENAME.cars = new Object;
SITENAME.users = new Object;

SITENAME.common.init = function (){
  // Your js code for all pages here
}

// cars/init.js

SITENAME.cars.init = function (){
  // Your js code for the cars controller here
}

// cars/index.js

SITENAME.cars.index = function (){
  // Your js code for the index method of the cars controller
}

и markup_based_js_execution будет содержать код для объекта UTIL, а также для выполнения UTIL.init, готового к DOM.

И не забудьте поместить это в свой файл макета:

<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">

Я также думаю, что лучше использовать классы вместо data-*атрибутов, для лучшего CSS для конкретной страницы. Как упоминал Джейсон Гарбер: CSS-селекторы для конкретной страницы могут быть очень неудобными (когда вы используете data-*атрибуты)

Я надеюсь, что это поможет вам.


4
Что если вам нужна переменная, доступная для всех действий в контроллере пользователя, но недоступная в других контроллерах? Разве у этого метода нет проблем с областями видимости?
tybro0103

@ tybro0103, я думаю, что для реализации этого поведения вы хотели бы написать что-то вроде window.varForOneController='val'этой функции инициализации контроллера. Также здесь может помочь Gon Gem ( github.com/gazay/gon ). Могут быть и другие обходные пути.
welldan97

1
@ welldan97 Понижение не для вашего объяснения - что превосходно - а потому, что структура Garber-Irish является злой. Он загружает все ваши JS на каждой странице и зависит от классов и идентификаторов элемента <body>, чтобы разобраться. Это верный признак борьбы с DOM: в обычных условиях элемент <body> не должен нуждаться в классе или идентификаторе, поскольку в документе только один элемент. Правильный способ сделать это - просто удалить //= require_tree .и использовать специфичный для страницы JavaScript. Если вы активно пытаетесь не делать этого, тогда вы стремитесь к плохой практике.
Марнен Лайбоу-Козер

2
@ MarnenLaibow-Koser Лично я считаю, что загрузка всех js на каждой странице хороша для большинства проектов, когда вы объединяете все js в один файл и минимизируете его. Я считаю, что в целом это работает быстрее для пользователя. По крайней мере, это больше похоже на конфликт один файл JS против многих (то есть посмотрите на stackoverflow.com/questions/555696/… ). Также нет ничего плохого в использовании классов и идентификаторов в теле, если это делает код проще и работает для вас. Modernizr ( modernizr.com ) делает это, и некоторые другие библиотеки тоже.
welldan97

2
@ MarnenLaibow-Koser конвейер рельсовых активов, на мой взгляд, кажется хорошим кандидатом для сравнения с компиляцией. Программист пишет свой javascript в хороших отделенных модулях, а затем он объединяется, минимизируется и обслуживается. Как и в случае скомпилированных языков, всегда найдутся программисты, которые думают, что они на шаг впереди компилятора ... но я думаю, что это редко так.
Зигги

65

Я вижу, что вы ответили на свой вопрос, но вот еще один вариант:

По сути, вы делаете предположение, что

//= require_tree .

необходимо. Это не. Не стесняйтесь удалить это. В моем текущем приложении, честно говоря, первое, что я делаю с 3.1.x, я сделал три разных JS-файла верхнего уровня. мойapplication.js файл имеет только

//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin

Таким образом, я могу создавать подкаталоги с собственными JS-файлами верхнего уровня, которые содержат только то, что мне нужно.

Ключи:

  1. Вы можете удалить require_tree - Rails позволяет вам изменить предположения, которые он делает
  2. В этом имени нет ничего особенного application.js- любой файл в assets/javascriptподкаталоге может содержать директивы препроцессора с//=

Надеюсь, что это помогает и добавляет некоторые детали к ответу ClosureCowboy.

Sujal


8
+1 Это здорово знать для новичка, как я. Я бы дал +2, если бы мог.
jrhorn424

5
@sujal Точно. Основная команда Rails славится ужасным управлением JavaScript. Не стесняйтесь игнорировать их предложения и просто использовать хорошие части конвейера активов. :)
Marnen Laibow-Koser

1
Большое спасибо за этот совет. У меня нет нескольких JS-файлов «верхнего уровня», в зависимости от модуля моего приложения. Работает хорошо.
elsurudo

1
+1 Важным моментом здесь для меня является то, что вы можете заменить //= require_tree .на //= require_directory .так, чтобы вы могли хранить все существующие файлы там, где они есть, и создавать новые каталоги для файлов, специфичных для страницы.
zelanix

41

Другой вариант: для создания файлов, специфичных для страницы или модели, вы можете создавать каталоги внутри своей assets/javascripts/папки.

assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific

Ваш основной application.jsфайл манифеста может быть настроен для загрузки его файлов из global/. Определенные страницы или группы страниц могут иметь свои собственные манифесты, которые загружают файлы из их собственных определенных каталогов. Звездочки автоматически объединят загруженные файлы application.jsс файлами, специфичными для вашей страницы, что позволяет этому решению работать.

Эту технику можно использовать и для style_sheets/.


13
Вы заставили меня жаждать кексы сейчас .. Дангит!
Чак Бержерон

Мне очень нравится это решение. Единственная проблема, с которой я столкнулся, заключается в том, что эти дополнительные манифесты не сжимаются / не увеличиваются. Хотя они правильно скомпилированы. Есть решение или я что-то упустил?
clst

1
Означает ли это, что браузер загружает один файл js, то есть комбинацию файла global + page?
Лулалала

Не могли бы вы взглянуть на мой вопрос, если таковой имеется? stackoverflow.com/questions/17055213/…
Maximus S

1
@clst Я думаю, что это ответ, который вы ищете: guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho

23

Я ценю все ответы ... и я не думаю, что они действительно решают проблему. Некоторые из них касаются стиля и, кажется, не связаны ... а другие просто упоминаютjavascript_include_tag ... что я знаю, что существует (очевидно ...), но может показаться, что путь к Rails 3.1 - это завершение всего Ваш Javascript в 1 файл, а не загружать отдельный Javascript внизу каждой страницы.

Лучшее решение, которое я могу придумать, - это обернуть некоторые функции в divтеги ids или classes. В коде JavaScript. Тогда вы просто проверить , если idили classна странице, и если вы запустите код яваскрипта, связанный с ним. Таким образом, если динамический элемент отсутствует на странице, код javascript не запускается, даже если он включен в огромный application.jsфайл, упакованный Sprockets.

Мое вышеупомянутое решение имеет то преимущество, что если окно поиска включено на 8 из 100 страниц, оно будет работать только на этих 8 страницах. Вам также не придется включать этот код на 8 страницах сайта. На самом деле, вам больше никогда не придется включать теги сценариев вручную на ваш сайт, кроме как для предварительной загрузки данных.

Я думаю, что это фактический ответ на мой вопрос.


Но вы на самом деле хотите эти ручные <script>теги. Да, классы и идентификаторы являются частью ответа, но для пользователя нет смысла загружать JavaScript, который не требуется для этой конкретной страницы.
Марнен Лайбоу-Козер

4
@ MarnenLaibow-Koser причина, по которой вы не добавляете теги сценариев вручную на каждую уникальную страницу, заключается в том, что вы должны загружать содержимое сценария при каждом просмотре страницы. Если вы можете упаковать весь javascript в application.js с помощью конвейера ресурсов, то пользователь загружает эти сценарии только один раз и извлекает application.js из кэша на всех последующих загрузках страницы
jakeonrails

@jakeonrails "причина, по которой вы не добавляете теги сценариев вручную на каждую уникальную страницу, заключается в том, что вы должны загружать содержимое сценария при каждом просмотре страницы" - совершенно неправильно. Скрипт будет загружен один раз, а затем будет извлечен из кэша браузера при последующих запросах. «Если вы можете упаковать весь javascript в application.js с помощью конвейера ресурсов, то пользователь загружает эти сценарии только один раз» - правда, но за счет большого количества ненужного кода. Если вы можете структурировать свой JS во множество маленьких файлов вместо одного большого, вы получите преимущества кэширования без лишнего кода.
Марнен Лайбоу-Козер

1
@ MarnenLaibow-Koser Я думаю, что было бы лучше сказать, что если вы упаковываете все в один скрипт, ваш пользователь должен загрузить только 1 скрипт для любой страницы вашего сайта. Если у вас есть несколько сценариев для разных частей вашего приложения, то, очевидно, пользователь должен загрузить более одного сценария. Разумеется, оба метода будут кэшироваться, но в большинстве случаев (приложениях среднего размера) одноразовое обслуживание одного application.js будет более эффективным для загрузки. Разбор JS может быть другой историей, в зависимости от того, что вы обслуживаете.
jakeonrails

1
@Ziggy Кроме того, если маленький файл используется только на 8 из 100 страниц, почему код должен постоянно находиться в кэше пользователя? Лучше на самом деле бросить вещи, которые не нужны.
Марнен Лайбоу-Козер

16

Я понимаю, что прихожу на эту вечеринку немного поздно, но я хотел добавить решение, которое я использовал в последнее время. Тем не менее, позвольте мне сначала упомянуть ...

Rails 3.1 / 3.2 Way (Нет, сэр. Мне это не нравится.)

Смотрите: http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline

Для полноты изложения я включаю следующее и потому, что это не является нежизнеспособным решением ... хотя меня это не волнует.

«Rails Way» - это решение, ориентированное на контроллеры, а не ориентированное на просмотр, как просил первоначальный автор этого вопроса. Существуют специфичные для контроллера файлы JS, названные в честь соответствующих контроллеров. Все эти файлы помещаются в дерево папок, которое по умолчанию НЕ включено ни в одно из приложений. Js требуют директив.

Чтобы включить специфичный для контроллера код, в представление добавляется следующее.

<%= javascript_include_tag params[:controller] %>

Я ненавижу это решение, но оно есть и оно быстрое. Предположительно, вместо этого вы можете называть эти файлы чем-то вроде «people-index.js» и «people-show.js», а затем использовать что-то подобное, "#{params[:controller]}-index"чтобы получить ориентированное на представление решение. Опять же, быстрое решение, но мне это не подходит.

Мой путь к данным

Считай меня сумасшедшим, но я хочу, чтобы ВСЕ мои JS компилировались и минимизировались в application.js при развертывании. Я не хочу помнить, чтобы включить эти маленькие файлы бродяги повсюду.

Я загружаю все свои JS в один компактный файл, который скоро будет кэширован браузером. Если какой-то кусок моего application.js нужно запустить на странице, я позволяю HTML сказать мне, а не Rails.

Вместо того, чтобы привязывать мой JS к определенным идентификаторам элементов или засорять мой HTML классами маркеров, я использую пользовательский атрибут данных с именем data-jstags.

<input name="search" data-jstag="auto-suggest hint" />

На каждой странице я использую - вставьте предпочтительный метод библиотеки JS здесь - для запуска кода после завершения загрузки DOM. Этот код начальной загрузки выполняет следующие действия:

  1. Перебирать все элементы в DOM, отмеченные data-jstag
  2. Для каждого элемента разбейте значение атрибута на пробел, создав массив строк тегов.
  3. Для каждой строки тега выполните поиск в хэше для этого тега.
  4. Если соответствующий ключ найден, запустите связанную с ним функцию, передав элемент в качестве параметра.

Скажем, у меня в файле application.js определено следующее:

function my_autosuggest_init(element) {
  /* Add events to watch input and make suggestions... */
}

function my_hint_init(element) {
  /* Add events to show a hint on change/blur when blank... */
  /* Yes, I know HTML 5 can do this natively with attributes. */
}

var JSTags = {
  'auto-suggest': my_autosuggest_init,
  'hint': my_hint_init
};

Событие начальной загрузки будет применять функции my_autosuggest_initи my_hint_initк вводу поиска, превращая его во ввод, который отображает список предложений при вводе пользователем, а также предоставляет некоторую подсказку для ввода, когда ввод оставлен пустым и не сфокусированным.

Если какой-либо элемент не помечен data-jstag="auto-suggest", код автоматического предложения никогда не срабатывает. Тем не менее, он всегда присутствует, минимизируется и в конечном итоге кэшируется в моем application.js для тех случаев, когда он мне нужен на странице.

Если вам нужно передать дополнительные параметры вашим тегированным функциям JS, вам придется применить некоторые творческие возможности. Либо добавьте атрибуты data-paramter, придумайте некоторый синтаксис параметров или даже используйте гибридный подход.

Даже если у меня есть какой-то сложный рабочий процесс, который кажется специфичным для контроллера, я просто создам файл для него в своей папке lib, запакую его в application.js и добавлю к нему что-то вроде 'new-thing-wizard'. Когда мой загрузчик попадет в этот тег, будет создан и запущен мой милый, причудливый волшебник. Он работает для представлений этого контроллера, когда это необходимо, но никак не связан с контроллером. Фактически, если я правильно закодирую свой мастер, я смогу предоставить все данные конфигурации в представлениях и, следовательно, позже смогу повторно использовать мой мастер для любого другого контроллера, который нуждается в этом.

Как бы то ни было, именно так я уже давно внедряю JS для конкретных страниц, и это хорошо мне помогло как для простого дизайна сайта, так и для более сложных / насыщенных приложений. Надеемся, что одно из двух решений, которые я представил здесь, мой путь или путь Rails, будет полезно всем, кто сталкивается с этим вопросом в будущем.


6
Одна маленькая деталь: в вашем ответе есть понятие, что после того, как js будет кеширован браузером, это никак не повлияет. Это не совсем так. Браузер действительно предотвращает загрузку, если файл js должным образом кэшируется, но все равно компилирует код при каждом отображении страницы. Таким образом, вы должны сбалансировать компромиссы. Если у вас много JS в совокупности, но только одна используется на страницу, вы можете улучшить время страницы, разбив JS на части.
Суджал

Подробнее о практических эффектах этого этапа компиляции, о котором я говорю, см. Объяснение 37 сигналов о том, как pjax повлиял на Basecamp. Далее: 37signals.com/svn/posts/…
sujal

Это справедливо. После прочтения статьи и просмотра проектов, в которых я использовал вышеупомянутое решение, я понял, что написал по существу то же самое решение «отправить измененный HTML», о котором они упоминали в статье. Из-за этого частая перекомпиляция JS не была проблемой в моих проектах. Этап компиляции - это то, что я буду иметь в виду, когда работаю над сайтами, менее ориентированными на «настольные приложения».
Райан

2
Голосование за «Зовите меня сумасшедшим, но я хочу, чтобы ВСЕ мои JS компилировались и минимизировались в application.js при развертывании». Вы действительно не хотите этого, поскольку он заставляет пользователя загружать JavaScript, который ему не нужен, и заставляет ваши обработчики искать атрибуты, которых там даже не будет. Наличие всего в app.js заманчиво, и Rails, конечно, делает это легко, но правильное решение - лучше модульнить JavaScript.
Марнен Лайбоу-Козер

Вы имеете право на другое мнение ... и технически имеете право понизить мнение по сравнению с другим мнением. Тем не менее, было бы неплохо увидеть некоторое обоснование того, почему один большой и кэшированный файл уступает принуждению нескольких HTTP-запросов для захвата модульной JS. Кроме того, вы ошибаетесь в отношении процедуры поиска обработчика. Значения тега НЕ ищутся. Выполняется только один поиск, и он извлекает все элементы, имеющие атрибут data-jstag. Он не выполняет поиск по имени тега, он просто находит все элементы, которые имеют теги, а затем создает экземпляры только необходимых объектов.
Райан

7

На это уже давно был дан ответ, но я придумал собственное решение, основанное на некоторых из этих ответов и моем опыте работы с Rails 3+.

Трубопровод активов сладок. Используй это.

Сначала в вашем application.jsфайле удалите//= require_tree.

Затем application_controller.rbсоздайте вспомогательный метод:

helper_method :javascript_include_view_js //Or something similar

def javascript_include_view_js
    if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
        return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
    end
end

Затем в application.html.erbфайле макета добавьте нового помощника среди существующих включений javascript с префиксом rawпомощника:

<head>
    <title>Your Application</title>
    <%= stylesheet_link_tag "application", :media => "all" %>
    <%= javascript_include_tag "application" %>
    <%= raw javascript_include_view_js %>
</head>

Вуаля, теперь вы можете легко создавать javascript для конкретного вида, используя ту же файловую структуру, что и везде в rails. Просто вставьте свои файлы app/assets/:namespace/:controller/action.js.erb!

Надеюсь, что это помогает кому-то еще!


1
Разве это не вызывает проблемы после того, как ресурсы предварительно скомпилированы и во время выполнения <%= raw ... %>вернет 404?
Nishant

Я думаю, что конвейер ресурсов неприятен, так как он создает кучу файлов, которые часто не должны использоваться. Поэтому для меня опора на конвейер активов создает зависимость от неэффективной системы.
Дебора

1
@DeborahSpeece Когда конвейер ресурсов создает файлы, которые не должны использоваться? Вы путаете конвейер активов (хороший) с require_tree /(плохим)?
Марнен Лайбоу-Козер

6

Вы можете добавить эту строку в свой файл макета (например, application.html.erb), чтобы автоматически загрузить специфичный для контроллера файл javascript (тот, который был создан при создании контроллера):

<%= javascript_include_tag params[:controller] %>

Вы также можете добавить строку для автоматической загрузки файла сценария для каждого действия.

<%= javascript_include_tag params[:controller] + "/" + params[:action] %>

Просто поместите скрипты вашей страницы в подкаталог с именем контроллера. В эти файлы вы можете включить другие скрипты, используя = require. Было бы неплохо создать помощника для включения файла, только если он существует, чтобы избежать сбоя 404 в браузере.


6
<%= javascript_include_tag params[:controller] %>

2
Похоже, что он мог бы ответить на вопрос. Не могли бы вы добавить больше к ответу, чтобы уточнить это?


5

LoadJS камень другой вариант:

LoadJS предоставляет способ загрузки специфичного для страницы кода Javascript в приложении Rails без потери волшебства, предоставляемого Sprockets. Весь ваш код Javascript будет продолжен в одном файле Javascript, но некоторые его части будут выполняться только для определенных страниц.

https://github.com/guidomb/loadjs


3

Ответ Филиппа довольно хороший. Вот код, чтобы заставить это работать:

В application.html.erb:

<body class="<%=params[:controller].parameterize%>">

Предполагая, что ваш контроллер называется Projects, он сгенерирует:

<body class="projects">

Тогда в projects.js.coffee:

jQuery ->
  if $('body.projects').length > 0  
     $('h1').click ->
       alert 'you clicked on an h1 in Projects'

Downvoting: любое решение , которое ставит класс на <body>это в силу самого факта неверно. Смотрите мои комментарии в другом месте на этой странице.
Марнен Лайбоу-Козер

Не делай этого. Эта проблема заключается в том, что каждый раз, когда вы добавляете один из них, вы добавляете другой фрагмент js, который должен выполняться при загрузке страницы. Определенно может привести к некоторому снижению производительности по мере роста вашего проекта.
ifightcrime

2

JavaScripts объединяются только тогда, когда вы говорите Rails (скорее Sprockets) объединить их.


Конечно. Наверное, я спрашиваю, потому что по умолчанию в Rails есть все в папке ... что означает, что Дэвид собирается сделать это. Но, как я сказал в другом комментарии к @rubyprince, я не уверен насчет выполнения, когда это делается таким образом. Я думаю, что я должен отключить //= require_tree .?
Эмблема Огня

@FireEmblem Да. require_tree .обычно плохая идея.
Марнен Лайбоу-Козер

2

Вот как я решил проблему стайлинга: (извините, Хэмл)

%div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
    = yield

Таким образом, я запускаю все специфичные для страницы файлы .css.sass с:

#post
  /* Controller specific code here */
  &#index
    /* View specific code here */
  &#new
  &#edit
  &#show

Таким образом, вы можете легко избежать любых столкновений. Когда дело доходит до файлов .js.coffee, вы можете просто инициализировать такие элементы, как;

$('#post > #edit') ->
  $('form > h1').css('float', 'right')

Надеюсь, это помогло некоторым.


1
Прочитайте последний бит еще раз, пожалуйста, для javascript вы можете воспользоваться той же структурой, которая используется для таблиц стилей для инициализации определенных функций представления.
Zeeraw

Филипп, $('#post > #edit') ->кажется, недействительным. Как вы используете JQuery для работы в области?
Рамон Тайаг

2
Недавно я начал загружать все специфичные для контроллера java-скрипты и таблицы стилей, вызвав это в application.html.haml; = javascript_include_tag "application"и = javascript_include_tag params[:controller]таким образом я могу держать код JavaScript без необходимости указывать область внутри файла.
zeeraw


2

Я согласен с вашим ответом, чтобы проверить, есть ли этот селектор, используйте:

if ($(selector).length) {
    // Put the function that does not need to be executed every page
}

(никто не видел, чтобы добавить фактическое решение)


2

Я не вижу ответа, который действительно объединяет все это и выкладывает это для вас. Таким образом, я попытаюсь поставить meleyal , sujal (а - ля ClosureCowboy ), первая часть Райана ответа, и даже Галь смелое заявление о Backbone.js ... все вместе в пути , который коротко и ясно. И, кто знает, я мог бы даже удовлетворить требования Марнена Лайбоу-Козера .

Пример редактирования

assets / javascripts / application.js

//= require jquery
//= require jquery_ujs
//= require lodash.underscore.min
...


views / layouts / application.html.erb

  ...
  </footer>

  <!-- Javascripts ================================================== -->
  <!-- Placed at the end of the document so the pages load faster -->
  <%= javascript_include_tag "application" %>
  <%= yield :javascript %>

</body>
</html>


views / foo / index.html.erb

...
<% content_for :javascript do %>
  <%= javascript_include_tag params[:controller] %>
<% end %>


assets / javascripts / foo.js

//= require moment
//= require_tree ./foostuff


assets / javascripts / foostuff / foothis.js.coffee

alert "Hello world!"


Краткое описание

  • Удалите //= require_tree .из application.js и перечислите только тот JS, который разделяет каждая страница.

  • Две строки, показанные выше в application.html.erb, указывают странице, куда следует включить application.js и ваш JS для конкретной страницы.

  • Три строки, показанные выше в index.html.erb, говорят вашему представлению о поиске некоторого JS для конкретной страницы и включении его в именованную область выхода, называемую ": javascript" (или как вы хотите ее назвать). В этом примере контроллером является «foo», поэтому Rails попытается включить «foo.js» в область выхода: javascript в макете приложения.

  • Перечислите свой JS для конкретной страницы в файле foo.js (или как называется контроллер). Список общих библиотек, дерево, каталоги, что угодно.

  • Держите свой JS для конкретной страницы где-нибудь, где вы можете легко ссылаться на него отдельно от вашего другого JS. В этом примере для foo.js требуется дерево foostuff, поэтому поместите туда свой пользовательский JS, например foothis.js.coffee .

  • Здесь нет жестких правил. Не стесняйтесь перемещать вещи и, возможно, даже создавать несколько областей доходности с различными именами в различных макетах, если это необходимо. Это только показывает один возможный первый шаг вперед. (Я делаю это не совсем так, учитывая наше использование Backbone.js. Я мог бы также захотеть поместить foo.js в папку с именем foo вместо foostuff, но пока не решил этого.)

Ноты

Вы можете делать подобные вещи с помощью CSS, <%= stylesheet_link_tag params[:controller] %>но это выходит за рамки вопроса.

Если я пропустил яркую лучшую практику здесь, пришли мне записку, и я придумаю адаптацию. Rails довольно нов для меня, и, честно говоря, я до сих пор не очень впечатлен хаосом, который по умолчанию вносит в развитие предприятия и весь трафик, который генерирует средняя программа Rails.


это похоже на путь, я собираюсь посмотреть, смогу ли я реализовать его в своем собственном приложении, спасибо за подробный ответ.
martincarlin87

1

У меня есть другое решение, которое, хотя примитив отлично работает для меня и не нуждается в каких-либо причудливых выборочных стратегиях загрузки. Поместите в свою обычную функцию готовности документа, но затем проверьте текущее местоположение окон, чтобы увидеть, предназначена ли это страница, для которой предназначен ваш javascript:

$(document).ready(function() {
   if(window.location.pathname.indexOf('/yourpage') != -1) {
          // the javascript you want to execute
   }
}

Это по-прежнему позволяет загружать все js с помощью rails 3.x в одном небольшом пакете, но не создает больших накладных расходов или конфликтов со страницами, для которых js не предназначен.


1

Ответ ryguy является хорошим ответом, даже несмотря на то, что его оценили как негативные.

Особенно, если вы используете что-то вроде Backbone JS - каждая страница имеет свой собственный вид Backbone. Тогда у файла erb есть единственная строка встроенного javascript, который запускает правый базовый класс представления. Я считаю, что это единственная строка «склеивающего кода» и, следовательно, тот факт, что с ней все в порядке. Преимущество заключается в том, что вы можете сохранить ваше «require_tree», которое позволяет браузеру кэшировать весь javascript.

в show.html.erb у вас будет что-то вроде:

<% provide :javascript do %>
  <%= javascript_include_tag do %>
    (new app.views.ProjectsView({el: 'body'})).render();
  <% end %>
<% end do %>

и в вашем файле макета вам понадобится:

<%= yield :javascript %>

Downvoting. Встроенный JavaScript никогда не бывает хорошей идеей. Даже если это клейкий код, он должен быть во внешнем файле.
Марнен Лайбоу-Козер

1

Переместите все ваши JS-файлы commom в подпапку, например «app / assets / javascript / global», затем в application.js измените //= require_tree .строку на //= require_tree ./global.

Теперь вы можете поместить свой JS, специфичный для контроллера, в корень «app / assets / javascript /», и они не будут включены в скомпилированный JS, а используются только тогда, когда вы вызываете их через = javascript_include_tagконтроллер / представление.


Нет, это дерьмо из JavaScript для загрузки на одну страницу. Даже не имеет значения, если он кешируется.
Jackyalcine

1

Хотя у вас есть несколько ответов здесь, я думаю, что ваша редакция, вероятно, лучшая ставка. Шаблон проектирования, который мы используем в нашей команде, который мы получили от Gitlab, - это шаблон Dispatcher. Он делает нечто похожее на то, о чем вы говорите, однако имя страницы задается в теге body с помощью rails. Например, в вашем файле макета просто включите что-то вроде (в HAML):

%body{'data-page' => "#{controller}:#{action}" }

Тогда в вашем dispatcher.js.coffeeфайле javascripts есть только одно замыкание и инструкция switch в вашем файле:

$ ->
  new Dispatcher()

class Dispatcher
  constructor: ->
    page = $('body').attr('data-page')
    switch page
      when 'products:index'
        new Products() 
      when 'users:login'
        new Login()

Все, что вам нужно сделать в отдельных файлах (скажем products.js.coffeeили, login.js.coffeeнапример), это заключить их в класс и затем глобализировать этот символ класса, чтобы вы могли получить к нему доступ в диспетчере:

class Products
  constructor: ->
    #do stuff
@Products = Products

У Gitlab есть несколько примеров этого, с которыми вы можете поэкспериментировать, если вам интересно :)


1

Проект Paloma предлагает интересный подход к управлению javascript-кодом для конкретной страницы.

Пример использования из их документов:

var UsersController = Paloma.controller('Users');

// Executes when Rails User#new is executed.
UsersController.prototype.new = function(){
   alert('Hello Sexy User!' );
};

1

Шаг 1. удалить require_tree. в вашем application.js и application.css.

Шаг 2. Отредактируйте файл application.html.erb (по умолчанию на rails) в папке макета. Добавьте «params [: controller]» в следующие теги.

<%= stylesheet_link_tag    'application', params[:controller], media: 'all', 'data-turbolinks-track' => true %>

<%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track' => true %>

Шаг 3. Добавьте файл в config / initializers / assets.rb

%w( controller_one controller_two controller_three ).each do |controller|
  Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.js.coffee", "#{controller}.css", "#{controller}.scss"]
end

ссылки: http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/


Хотя это может теоретически ответить на вопрос, было бы предпочтительным включить здесь основные части ответа и предоставить ссылку для справки.
Бхаргав Рао

0

Я не пробовал это, но похоже, что верно следующее:

  • если у вас есть content_for, который является javascript (например, с реальным javascript внутри него), sprockets не будет знать об этом, и, следовательно, это будет работать так же, как и сейчас.

  • если вы хотите исключить файл из большого пакета javascript, вы должны зайти в файл config / sprockets.yml и соответственно изменить исходные файлы. Затем вы просто включите любые файлы, которые вы исключили, где это необходимо.


Является ли исключение файлов или использование собственного javascript на самой странице "правильным способом"? Это то, как Давид хотел, чтобы люди использовали это?
Эмблема Огня

@FireEmblem Мне неважно, что задумал Дэвид, потому что я не думаю, что Дэвид понимает, как правильно организовать JavaScript.
Марнен Лайбоу-Козер


0

Я объединил некоторые ответы в:

Помощник по применению:

module ApplicationHelper
  def js_page_specific_include
    page_specific_js = params[:controller] + '_' + params[:action]
    if Rails.application.assets.find_asset(page_specific_js).nil?
      javascript_include_tag 'application', 'data-turbolinks-track' => true
    else
      javascript_include_tag 'application', page_specific_js, 'data-turbolinks-track' => true
    end
  end
end

макеты / application.html.haml:

 <!DOCTYPE html>
%html{lang: 'uk'}
  %head   
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
   bla-bla-bla
    = js_page_specific_include   
   bla-bla-bla  

0

Первый: удалить \\=require_treeиз application.js. Второй: весь ваш код JS должен быть размещен в, /app/assets/javascritptа весь ваш код CSS - в/app/assets/stylesheets


-2

Следуя указаниям Райана, вот что я сделал:

application.js.coffee

$ ->
    view_method_name = $("body").data("view") + "_onload"
    eval("#{view_method_name}()") if eval("typeof #{view_method_name} == 'function'")
    view_action_method_name = $("body").data("view") + "_"+$("body").data("action")+"_onload"
    eval("#{view_action_method_name}()") if eval("typeof #{view_action_method_name} == 'function'")

users.js.coffee (специфический для контроллера coffeescript, например controller: users, action: dashboard)

window.users_dashboard_onload = () ->
    alert("controller action called")
window.users_onload = () ->
    alert("controller called")

application.html.haml

%body{:data=>{:view=>controller.controller_name, :action=>controller.action_name}}

Downvoting. Это смехотворно запутанно - не говоря уже о небезопасности (из-за eval), если ваш HTML-код скомпрометирован взломанным сервером или вредоносным скриптом пользователя.
Марнен Лайбоу-Козер

-3

Вот как это сделать, особенно если вам не нужно запускать тонны библиотек для конкретной страницы, а выполнять более или менее несколько сотен строк JS.

Поскольку встраивать код Javascript в HTML совершенно нормально, просто создайте его в каталоге app / views shared.js и поместите туда свой код для страниц / страниц в my_cool_partial.html.erb.

<script type="text/javascript"> 
<!--
  var your_code_goes_here = 0;
  function etc() {
     ...
  }
-->
</script>

Так что теперь, откуда вы хотите, вы просто делаете:

  = render :partial => 'shared.js/my_cool_partial'

И это все, к?


2
Downvoting. Встроенный JavaScript никогда не рекомендуется. HTML должен содержать только разметку. JS и CSS должны быть в отдельных, повторно используемых файлах.
Марнен Лайбоу-Козер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.