Вы чувствуете себя потерянным из-за всех изменений, связанных с активами и Javascript? Npm, Babel, ES6, Yarn, Webpack, Webpacker, Sprockets - все они кажутся вам совершенно незнакомыми?
Если вам нужно быстро и легко понять, как вся эта экосистема Javascript работает в приложении Rails 6, эта статья - то, что вам нужно.
Я закончу эту статью пошаговым разделом, объясняющим, как добавить Bootstrap 4 и FontAwesome 5 в проект Rails 6.
НПМ
NPM - менеджер пакетов Javascript (точнее, модули NodeJS). Это Rubygems мира Javascript.
npm install <package>
Например, если вы хотите установить бутстрап:
npm install bootstrap
NPM хранит загруженные пакеты в ./node_modules
и хранит список этих пакетов в ./package.json
.
На данный момент я не рисую никакой связи между NPM и Rails, продолжайте читать, чтобы понять, почему.
Пряжа
Yarn - это более поздний менеджер пакетов для Javascript. Он извлекает пакеты из репозитория NPM, но делает больше. Он позволяет заблокировать нужные версии ваших пакетов NPM в yarn.lock
автоматически созданном файле (аналогично Gemfile.lock
), это намного быстрее, чем NPM и т. Д.
В приложении Rails 6, когда вам нужна библиотека Javascript, вы:
- использовался для добавления драгоценного камня, который его предоставляет, затем он вам потребовался в
app/assets/application.js
(который был скомпилирован Sprockets) - теперь нужно добавить его через Yarn (https://yarnpkg.com):
yarn add <package>
, тогда он вам понадобится (мы увидим, как позже).
Примечание. С тех пор NPM также добавила функцию блокировки через package-lock.json
ES6
ES6 - это новый стандарт Javascript (если хотите, новая версия Javascript). Он поставляется с новыми супер-удобными функциями, такими как определение класса, деструктуризация, стрелочные функции и т. Д.
Прощай, кофе, я всегда тебя ненавидел.
Вавилон
Поскольку все веб-браузеры еще не поддерживают ES6, вам понадобится инструмент, который считывает ваш код ES6 Javascript и переводит его в старый Javascript ES5, чтобы он работал во всех браузерах. Babel - компилятор, выполняющий этот перевод.
Webpack
Есть Babel, есть Yarn и их файлы конфигурации, и есть необходимость автоматизировать компиляцию ваших ресурсов, управление средами и так далее.
Поскольку вы хотите сосредоточиться на написании кода и автоматизации предварительной компиляции ресурсов, вы будете использовать Webpack, который возьмет на себя роль контролера. Он берет ваши активы и передает каждый из них нужным плагинам. Затем плагины заставляют правильный инструмент обрабатывать входной файл и выдавать ожидаемый результат.
Например, Webpack может:
- возьмите свой код ES6 Javascript,
- используйте плагин
babel-loader
, чтобы Babel скомпилировал ES6 в код ES5 Javascript, - затем выведите полученный пакет в файл, который можно включить в свою HTML DOM (
<script type="text/javascript" src="path-to-es5-javascript-pack.js"></script>
).
Webpacker
Webpacker - это драгоценный камень, который прекрасно включает Webpack в ваше приложение Rails. Он поставляется с некоторыми начальными (и достаточными для начала) файлами конфигурации, так что вы можете начать с написания фактического кода, не беспокоясь о конфигурации.
В конфигурации Webpacker по умолчанию указано следующее:
app/javascript/packs/
должен содержать ваши пакеты Javascript (например:application.js
)- Вы можете включить пакет Javascript в свои представления, используя
javascript_pack_tag '<pack_name>'
(например:<%= javascript_pack_tag 'my_app' %>
будет включатьapp/javascript/packs/my_app.js
)
Я дам очень наглядный пример того, как все это работает, в конце этой статьи, мне просто нужно сначала немного поговорить о звездочках.
Примечание: другая конфигурация по умолчанию - extract_css: false
(config/webpacker.yml
), что означает, что хотя Webpack знает, как обслуживать пакеты CSS с stylesheet_pack_tag
, вы говорите ему этого не делать. Эта статья посвящена Javascript, поэтому я не собираюсь больше говорить об этом, просто имейте в виду, что он отключен по умолчанию, поэтому вы не будете тратить время на отладку того, что является поведением по умолчанию, а не ошибкой.
Еще одно замечание: когда вы запускаете rails assets:precompile
, вы можете подумать, что Rails прекомпилирует только то, что находится в app/assets/
. Rails фактически прекомпилирует как ресурсы Webpack app/javascript/
, так и ресурсы Sprockets app/assets/
.
Звездочки 4
Как и Webpack, Sprockets представляет собой конвейер ресурсов, что означает, что он принимает файлы ресурсов в качестве входных данных (Javascript, CSS, изображения и т. Д.) И обрабатывает их для создания вывода в желаемом формате.
Начиная с Rails 6, Webpack (er) заменяет Sprockets в качестве нового стандарта для написания Javascript в ваших приложениях Rails. Тем не менее, Sprockets по-прежнему является способом добавления CSS в ваши приложения по умолчанию.
Со звездочками вы:
- используется для вывода списка доступных ресурсов в
config.assets.precompile
(Sprockets 3, Rails 5) - теперь нужно сделать это в файле манифеста
app/assets/config/manifest.js
(Sprockets 4, Rails 6)
Если вы хотите включить актив из конвейера Sprockets, вы должны:
- Напишите свой CSS (например:
app/assets/stylesheets/my_makeup.css
) - Убедитесь, что
app/assets/config/manifest.js
делает его доступным дляstylesheet_link_tag
посредством оператораlink_tree
,link_directory
илиlink
(например:link my_makeup.css
) - Включите его в свое представление, используя
stylesheet_link_tag
(<%= stylesheet_link_tag 'my_makeup' %>
)
Не пытайтесь использовать Webpack, как Sprockets!
Если вы не хотите тратить бесчисленные часы, гребя против течения, очень важно понимать следующее. В идеале вам следует потратить некоторое время на изучение ES6, но пока что я могу по крайней мере сказать следующее:
Webpack отличается от Sprockets тем, что он компилирует модули.
Модули ES6, если быть точным (в случае Rails 6 с конфигурацией по умолчанию). Что это значит? Что ж, это означает, что все, что вы объявляете в модуле, является своего рода пространством имен, потому что оно не предназначено для доступа из глобальной области видимости, а скорее импортировано, а затем используется. Позвольте привести пример.
С помощью звездочек можно делать следующее:
app/assets/javascripts/hello.js
:
function hello(name) { console.log("Hello " + name + "!"); }
app/assets/javascripts/user_greeting.js
:
function greet_user(last_name, first_name) { hello(last_name + " " + first_name); }
app/views/my_controller/index.html.erb
:
<%= javascript_link_tag 'hello' %> <%= javascript_link_tag 'user_greeting' %> <button onclick="greet_user('Dire', 'Straits')">Hey!</button>
Довольно просто понять. Как насчет Webpacker сейчас?
Если вы думали, что просто переместите эти JS-файлы в app/javascript/packs
, включите их с помощью javascript_pack_tag
и готово, позвольте мне остановить вас прямо сейчас: это не сработает.
Почему? Поскольку hello()
будет скомпилирован как находящийся в модуле ES6 (аналогично для user_greeting()
), это означает, что, что касается функции user_greeting()
, даже после того, как оба файла JS включены в представление, функция hello()
не существует.
Итак, как получить тот же результат с Webpack:
app/javascript/packs/hello.js
:
export function hello(name) { console.log("Hello " + name + "!"); }
app/javascript/packs/user_greeting.js
:
import { hello } from './hello'; function greet_user(last_name, first_name) { hello(last_name + " " + first_name); }
app/views/my_controller/index.html.erb
:
<%= javascript_pack_tag 'user_greeting' %> <button onclick="greet_user('Dire', 'Straits')">Hey!</button>
Это сработает? Нет почему? Опять же, по той же причине: greet_user
недоступен из представления, потому что он скрыт внутри модуля после его компиляции.
Наконец, мы подошли к самому важному моменту этого раздела:
- С помощью Sprockets: представления могут взаимодействовать с тем, что открывают ваши JS-файлы (используйте переменную, вызовите функцию, ..)
- С Webpack: представления НЕ имеют доступа к тому, что содержат ваши JS-пакеты.
Так как же заставить кнопку запускать действие JS? Из пакета вы добавляете поведение к элементу HTML. Вы можете сделать это, используя ванильный JS, JQuery, StimulusJS, как угодно.
Вот пример использования JQuery:
import $ from 'jquery'; import { hello } from './hello'; function greet_user(last_name, first_name) { hello(last_name + " " + first_name); } $(document).ready(function() { $('button#greet-user-button').on( 'click', function() { greet_user('Dire', 'Strait'); } ); }); /* Or the ES6 version for this: */ $(() => $('button#greet-user-button').on('click', () => greet_user('Dire', 'Strait')) );
app/views/my_controller/index.html.erb
:
<%= javascript_pack_tag 'user_greeting' %> <button id="greet-user-button">Hey!</button>
Практическое правило: с помощью Webpack вы настраиваете желаемое поведение в пакетах, а не в представлениях.
Позвольте мне повторить еще один пример:
Если вам нужно использовать библиотеку (например, select2 или jQuery), можно ли импортировать ее в пакет и использовать в представлении? Нет. Вы либо импортируете его в пакете и используете в нем, либо читаете следующий раздел этой статьи.
Если вы хотите узнать, как использовать StimulusJS для структурирования вашего JS-кода и прикрепления поведения к вашим HTML-элементам, я советую вам прочитать StimulusJS on Rails 101.
Для тех, кто хочет понять, как работает это «все скрыто / в пространстве имен»: когда модуль ES6 компилируется в код ES5, содержимое модуля упаковывается внутри анонимной функции, так что за пределами этой функции вы не можете получить доступ к любой переменная / функция, объявленная в модуле.
Вы все еще можете использовать Sprockets для кода Javascript
В документации Webpacker указано следующее:
[…] Основная цель webpack - это JavaScript, подобный приложениям, а не изображения, CSS или даже JavaScript Sprinkles (которые продолжают жить в app / assets).
Это означает, что если вам нужно или вы хотите сделать некоторые материалы Javascript доступными для представлений, вы все равно можете использовать Sprockets.
- Создайте каталог
app/assets/javascripts
(обратите внимание, что здесь javascripts во множественном числе) - Обновите
app/assets/config/manifest.js
соответственно (//= link_directory ../javascripts .js
) - Включите файлы Javascript Sprockets в ваши представления, используя
javascript_include_tag
(обратите внимание на разницу:javascript_include_tag
для Sprockets,javascript_pack_tag
для Webpacker) - Делай свое дело.
Я лично стараюсь по возможности избегать этого, но об этом стоит знать.
Примечание: вы можете спросить, почему существуют и файл manifest.js
, и массив config.assets.precompile
, которые служат одной и той же цели - раскрытию целей верхнего уровня для компиляции. Это сделано для обратной совместимости. Инструкции по обновлению не рекомендуют вам использовать последнее.
Как добавить bootstrap 4 и fontawesome 5 в приложение Rails 6
Чтобы помочь вам лучше понять, я советую вам подавать заявку по мере чтения. Это очень поможет в восприятии этих новинок.
1. Создайте новое приложение Rails 6
rails new bloggy
Я хочу, чтобы вы взглянули на следующие файлы. Цель состоит не в том, чтобы вы понимали все, что они держат, а в том, чтобы знать, что они существуют, и иметь смутное мысленное представление о том, что они содержат, чтобы вы могли легко вернуться к ним позже, если потребуется.
Пряжа:
- package.json
Webpacker:
- config / webpacker.yml
- приложение / JavaScript / пакеты / application.js
- приложение / просмотров / макеты / application.html.erb
Звездочки:
- приложение / активы / конфигурация / manifest.json
2. Добавьте корневую страницу.
rails generate controller welcome index
И добавьте root to: 'welcome#index'
в config/routes.rb
.
Запустите rails server
и убедитесь, что пока все в порядке.
3. Добавьте необходимые пакеты пряжи.
Мы хотим добавить bootstrap 4 (для которого требуются jquery и popper.js) и font-awesome 5.
Взгляните на поисковую систему Yarn и попытайтесь найти необходимые пакеты самостоятельно (обратите внимание на количество загрузок для каждого пакета), затем продолжайте это руководство.
yarn add bootstrap jquery popper.js @fortawesome/fontawesome-free
Пряжа теперь кэширует их в ./bloggy/node_modules/
и обновляет package.json
. Однако эти пакеты все еще не используются в нашем приложении. Давай исправим это. Мы начнем с включения части JS и займемся частью CSS позже.
4. Включите JS-часть начальной загрузки и fontawesome.
В вашем макете уже есть javascript_pack_tag 'application'
, что означает, что вы просите Webpack скомпилировать app/javascript/packs/application.js
и включить вывод в этот макет. Чтобы добавить бутстрап, мы можем либо создать другой пакет исключительно для включения бутстрапа, либо использовать пакет application.js
. Давайте сделаем последнее, поскольку мы не создаем настоящее приложение.
Добавьте в app/javascript/packs/application.js
следующее:
require("bootstrap"); require("@fortawesome/fontawesome-free");
Обратите внимание, мне нужен «bootstrap», а не «bootstrap / dist / js / bootstrap.min». Это связано с тем, что если я не укажу путь к файлу, package.json
(bloggy/node_modules/bootstrap/package.json
) модуля предоставит необходимую информацию о том, какой файл включить. Я мог бы потребовать 'bootstrap / dist / js / bootstrap.min', и это сработало бы нормально.
Вернемся к настройке bootstrap и fontawesome в нашем приложении. Если вы запустите сервер Rails и посмотрите на консоль Javascript, вы увидите, что она работает правильно, хотя нам не требовался jQuery в application.js
.
Если вы ранее просматривали другие руководства, объясняющие, как включить загрузку через Webpacker, вы, вероятно, заметили, что для большинства из них сначала требуется jQuery, а затем требуется загрузка. На самом деле это бесполезно.
Почему? Поскольку, поскольку мы установили jQuery через Yarn, для начальной загрузки может потребоваться jQuery. Нам нет смысла требовать его в application.js
, потому что он сделает его доступным в application.js
, а не внутри модуля начальной загрузки. Поэтому, если вам действительно не нужно использовать jQuery непосредственно в application.js
, в этом нет необходимости.
5. Включите часть (S) CSS в bootstrap и fontawesome.
Мне нравится работать с SCSS, поэтому, прежде чем включать bootstrap и font-awesome, давайте переименуем application.css
в application.scss
и очистим его от всех комментариев и других инструкций Sprockets.
Теперь вставьте в него следующий код:
$fa-font-path: '@fortawesome/fontawesome-free/webfonts'; @import '@fortawesome/fontawesome-free/scss/fontawesome'; @import '@fortawesome/fontawesome-free/scss/regular'; @import '@fortawesome/fontawesome-free/scss/solid'; @import '@fortawesome/fontawesome-free/scss/brands'; @import 'bootstrap/scss/bootstrap';
Примечание. В отличие от Webpack, Sprockets не читает package.json
файлы модулей npm, чтобы определить, какой файл включить, поэтому вы не можете импортировать модуль только по его имени. Вы должны указать путь к фактическим файлам, которые вы хотите импортировать (хотя расширение файла необязательно).
Все готово!
Давайте добавим кнопку и значок, чтобы убедиться, что все работает правильно:
Добавьте <a href="#" class="btn btn-primary">Yeah <i class="far fa-thumbs-up"></i></a>
в app/views/welcome/index.html.erb
, запустите сервер rails и убедитесь, что основная кнопка и значок отображаются правильно.
Сделать jQuery доступным во всех пакетах
Если вам нужно использовать jQuery (или любую зависимость) в большинстве ваших пакетов, требовать его в каждом пакете обременительно. Решение, которое мне нравится, - сделать его доступным для всех пакетов через конфигурацию (опять же, он не будет доступен в представлениях, только в пакетах).
Для этого скопируйте / вставьте в config/webpack/environment.js
следующее:
const { environment } = require('@rails/webpacker') var webpack = require('webpack'); environment.plugins.append( 'Provide', new webpack.ProvidePlugin({ $: 'jquery', }) ) module.exports = environment
Этот фрагмент заставляет Webpack «предоставлять» модуль jQuery всем пакетам через имя $
. Это эквивалентно добавлению следующего в начале каждого пакета:
import $ from 'jquery';
Спасибо за внимание!