Вы чувствуете себя потерянным из-за всех изменений, связанных с активами и 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';

Спасибо за внимание!