Сланец Shopify, особенно новая версия v1, - отличный инструмент для улучшения рабочего процесса разработки тем Shopify. Для меня одним из основных преимуществ является использование новых языковых функций и организация JS-кода по разным папкам с помощью импорта ES6.

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

Если вы похожи на меня, вы не собираетесь возвращаться к попыткам организовать свой код в виде JS-файлов с единой монолитной темой с использованием старых языковых функций. Но если вы просто вносите некоторые изменения в существующую тему и только хотите использовать новые языковые функции JS и импорт, тогда Slate может быть немного излишним… Введите Parcel.

Настройка

Parcel можно использовать как легкую альтернативу Slate, которую можно мгновенно настроить с нулевой конфигурацией для обработки пакетов JS-файлов вашей темы. Нам просто нужно установить посылку и настроить наши файлы локально, следуя инструкциям ниже.

1: Установить посылку

Предполагая, что у вас установлен Node JS, вы можете легко установить Parcel, выполнив следующую команду NPM:

npm install -g parcel-bundler

2: Установить набор тем

Нам также потребуется установить Набор тем, это то, что Slate использует под капотом для синхронизации обновлений между темой на Shopify и вашим локальным кодом. Интерфейс командной строки, который он предоставляет, столь же прост в использовании. Следуйте инструкциям по установке на сайте или, если вы работаете на Mac и ленивы, вы можете использовать следующую команду curl, чтобы установить его:

curl -s https://raw.githubusercontent.com/Shopify/themekit/master/scripts/install | sudo python

3: Загрузите тему

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

theme configure --password=[your-password] --store=[you-store.myshopify.com] --themeid=[your-theme-id]
theme download

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

Использование посылки

Теперь мы готовы начать использовать Parcel! Давайте начнем с простого примера, чтобы понять, как это будет работать. Создайте папку сценариев в корне локальной папки темы и добавьте в нее файл scripts.js, содержащий следующую строку JS:

console.log('Parcel!');

Теперь, чтобы наш код загружался в тему, нам нужно сделать пакет, объединяющий наш код и выгружающий его в папку с активами, затем нам понадобится набор тем, чтобы забрать изменения в папку с активами и отправить их в тему. К счастью, Parcel делает это намного проще, чем может показаться, всего лишь требует, чтобы несколько команд выполнялись в отдельных окнах терминала:

npm init -y # Creates a package.json file for NPM dependencies
theme watch # Auto sync file changes to our theme
# Then in a separate terminal window to bundle our JS upon changing files
parcel watch scripts/scripts.js --out-dir assets --out-file scripts.js

Последний шаг - включить в тему новый JS-файл. Где-нибудь в <head> вашего layout / theme.liquid добавьте следующую строку:

{{ 'scripts.js' | asset_url | script_tag }}

Откройте тему и проверьте консоль, она должна показать «Посылка!» что означает, что наш JS правильно упакован 🎉

Попробуйте изменить console.log текст на что-то другое, parcel автоматически повторно объединит JS при сохранении, что, в свою очередь, заставит набор тем подтолкнуть обновленный файл к теме.

Интеграция существующего кода темы

Итак, мы успешно получили пакет Parcel с кодом. Теперь давайте включим существующие файлы темы в наш пакет, чтобы в идеале мы получили только один JS-файл, который нужно загрузить нашей теме. Начните с создания папки в каталоге сценариев, чтобы добавить старые файлы тем, я собираюсь называть это «ядром» так /scripts/core.

Найдите JS-файлы старой темы в папке с ресурсами. Вы также можете определить их, проверив файл layout/theme.liquid, чтобы увидеть, какие скрипты включены. Переместите любой из найденных вами скриптов в /scripts/core и удалите включенные скрипты из /layout/theme.liquid.

Теперь импортируйте основные файлы в ваш scripts.js файл, например: import './core/core-file', убедитесь, что вы сохранили порядок файлов, в которые были ранее включены. Я использую «дебютную» тему, ваша тема, скорее всего, будет иметь другие файлы, но процесс все равно должен выглядеть нравится:

Если вы вернетесь в консоль, вы можете столкнуться с неопределенными ошибками переменных 😩, не паникуйте. Это будет связано с тем, что тема содержала некоторые библиотеки, которые экспортируют себя как модули, а не глобальные переменные, если библиотеки обнаружат, что вы теперь используете среду, поддерживающую экспорт. Есть несколько способов справиться с этим…

  1. «Правильный», но трудоемкий способ: разделить библиотеки и импортировать их по отдельности, возможно, переместив некоторые зависимости, такие как JQuery, для управления с помощью NPM.
  2. Быстрый, но рискованный способ: удалите условие кода экспорта из исходного кода библиотеки, чтобы библиотека была включена как глобальная.
  3. Быстрый и безопасный, но менее оптимизированный способ: оставьте файл с импортированными библиотеками в виде отдельного скрипта, включенного в layout / theme.liquid.

У вас, вероятно, сжатые сроки, и, кроме того, настройка посылки должна была быть быстрой и простой, не так ли? Даже если нам действительно нужно оставить сторонние библиотеки в виде отдельного сценария, пока это все же лучше, чем возвращаться к файлам JS монолитной темы с использованием старых языковых функций, поэтому давайте пойдем по пути № 3. В дебютной теме файл с именем vendor.js содержит все эти типы библиотек, поэтому я просто собираюсь переместить этот файл обратно в тему.

Теперь у нас есть Parcel, объединяющий старый код темы вместе с нашим новым console.log без ошибок, вы можете назвать это днем ​​здесь, если вы довольны изменениями и готовы к запуску в производство, вы можете остановить задачу просмотра, добавить живую среду в набор тем config.yml и используйте:

parcel build scripts/scripts.js --out-dir assets --out-file theme.js
theme replace -e=live assets/theme.js layout/theme.liquid

Команда parcel build минимизирует код в оптимизированный готовый пакет, который вы можете использовать в живой теме. parcel watch задача не включает эту минификацию, потому что она ускоряет объединение и отладку ошибок при разработке.

Улучшение рабочего процесса

Постоянное написание этих длинных команд посылки превращается в рутинную работу, давайте создадим ярлык, используя сценарии NPM в нашем package.json, чтобы обрабатывать просмотр и сборку пакета JS для нас:

"scripts": {
  "watch": "concurrently \"theme watch\" \"parcel watch scripts/scripts.js --out-dir assets --out-file scripts.js\"",
  "build": "parcel build scripts/scripts.js --out-dir assets --out-file scripts.js"
}

В команде watch используется одновременно, поэтому нам не нужно запускать одновременно theme watch и parcel watch по отдельности. Параллельно есть модуль NPM, поэтому нам нужно будет установить его как зависимость разработки, используя:

npm i -D concurrently

С этого момента мы можем начать следить за изменениями и автоматически загружать в нашу тему разработки, используя npm run watch:

Создайте лучший код

Отлично, теперь давайте посмотрим, как мы можем добавить в нашу тему что-то более сложное, чем console.log. В дебютной теме есть страница поиска "ОК" ... Но разве не было бы круче, если бы страницу не приходилось перезагружать каждый раз, когда выполнялся новый поиск?

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

Я собираюсь довольно быстро пройти через эту реализацию, больше для демонстрации преимуществ использования Parcel, чем для описания того, как создать саму функцию, но напишите комментарий на Medium, если у вас есть какие-либо вопросы по этому поводу.

Эта функция будет построена с использованием новых языковых функций JS, которые могут быть недоступны во всех браузерах, поэтому я собираюсь установить babel, чтобы скомпилировать JS обратно до версий, которые могут использовать все браузеры, во-первых, необходимо установить babel:

npm i -D babel-core babel-polyfill babel-preset-env

Затем в каталог тем был добавлен файл .babelrc, чтобы сообщить parcel, какой пресет babel использовать:

{
  "presets": ["env"]
}

Затем я собираюсь добавить import 'babel-polyfill'; в начало scripts.js, чтобы иметь возможность использовать новые языковые функции, такие как async / await. Parcel будет автоматически использовать babel, поэтому, просто выполнив эти несколько шагов, мы теперь можем безопасно использовать все новые функции языка JS, которые мы хотим 💅.

Затем я собираюсь добавить два файла /templates/search.json.liquid, чтобы указать URL-адрес, по которому мы можем получать результаты поиска в формате JSON:

{% layout none %}
{% paginate search.results by 99 %}
{% capture results %}
  {% for item in search.results %}
    {
      "title": {{ item.title | highlight: search.terms | json }},
      "url": {{ item.url | within: item.collections.last | json }},
      "price": {{ item.price | json }},
      "featured_image": {{ item.featured_image.src | json }}
    }
    {% unless forloop.last %},{% endunless %}
  {% endfor %}
{% endcapture %}
{
  "count": {{ search.results_count }},
  "results": [{{ results }}]
}
{% endpaginate %}

И /scripts/search.js для обработки обновления результатов поиска, пока покупатель вводит текст в поле поиска:

const getResults = query => $.getJSON(`/search?view=json&q=${query}`);
const generateItemHtml = item => `
  <li class="list-view-item">
    <a href="${item.url}" class="list-view-item__link">
      <div class="list-view-item__image-column">
        <div class="list-view-item__image-wrapper">
          <img class="list-view-item__image" src="${item.featured_image}">
        </div>
      </div>
      <div class="list-view-item__title-column">
        <div class="list-view-item__title">${item.title}</div>
      </div>
      <div class="list-view-item__price-column">
        <div class="product-price">
          <span class="product-price__price">${item.price}</span>
        </div>
      </div>
    </a>
  </li>
`;
const updateSearchText = (query, count) => $('h1').html(`
  <span class="visuallyhidden">Search result:</span>
  ${count} results for "${query}"
`);
const search = async e => {
  const query = e.target.value;
  const {results, count} = await getResults(query);
  const html = results.map(generateItemHtml).join('');
  $('.list-view-items').empty().append(html);
  updateSearchText(query, count);
}
export default () => $(() => $('#SearchInput').keyup(search));

Отлично, теперь осталось только включить новый модуль поиска в наш /scripts/scripts.js, чтобы убедиться, что пакет добавляет его в пакет JS:

import 'babel-polyfill';
import './core/lazysizes';
import './core/theme.js';
import search from './search';
if (window.location.pathname === '/search') {
  search();
}

Вот результат, намного лучше 😊

Задержать! И последнее ... Форматирование цены выглядит неуместным. Если подумать об этой проблеме, очень вероятно, что в какой-то момент нам понадобится использовать JS для форматирования цен в другом месте на сайте. Вместо того, чтобы создавать решение, специфичное для модуля поиска, давайте создадим повторно используемую служебную функцию для форматирования любых денежных сумм в /scripts/utils/format-money.js

const formatMoney = amount => {
  const roundNumber = (num, dec) => Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
  if (amount) {
    // Make sure "amount" is in cents
    if (String(amount).indexOf('.') > -1) {
      amount = Number(amount) * 100;
    }
    amount = (amount / 100).toFixed(2);
  } else {
    amount = 0.00;
  }
  return `$${amount}`;
}; 
export default formatMoney;

Теперь функцию formatMoney можно импортировать и использовать в любом месте нашего кода, import ее в /scripts/search.js и передать цену товара при генерации HTML следующим образом:

import formatMoney from './utils/format-money';
const getResults = query => $.getJSON(`/search?view=json&q=${query}`);
const generateItemHtml = item => `
  <li class="list-view-item">
    <a href="${item.url}" class="list-view-item__link">
      <div class="list-view-item__image-column">
        <div class="list-view-item__image-wrapper">
          <img class="list-view-item__image" src="${item.featured_image}">
        </div>
      </div>
      <div class="list-view-item__title-column">
        <div class="list-view-item__title">${item.title}</div>
      </div>
      <div class="list-view-item__price-column">
        <div class="product-price">
          <span class="product-price__price">
            ${formatMoney(item.price)}
          </span>
        </div>
      </div>
    </a>
  </li>
`;
const updateSearchText = (query, count) => $('h1').html(`
  <span class="visuallyhidden">Search result:</span>
  ${count} results for "${query}"
`);
const search = async e => {
  const query = e.target.value;
  const {results, count} = await getResults(query);
  const html = results.map(generateItemHtml).join('');
  $('.list-view-items').empty().append(html);
  updateSearchText(query, count);
} 
export default () => $(() => $('#SearchInput').keyup(search));

Наконец, теперь у нас есть динамически отображаемые результаты поиска с правильно отформатированными ценами 🎉

Заключение

Этот пост был посвящен объединению файлов JS тем Shopify с Parcel в качестве легкой альтернативы Slate, которая может быть интегрирована с существующими темами. Надеюсь, это может помочь улучшить ваш опыт разработки и эффективность при разработке тем Shopify, а также общее качество и организацию кода, минимизируя временные затраты на настройку.

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

Этот пост также доступен на danwebb.co, хотите принять участие в разработке Shopify или создании веб-сайта / приложения? Свяжитесь с нами [email protected].

Для получения обновлений о других подобных статьях и других вещах я делаю подписывайтесь на меня в твиттере или подпишитесь на мою рассылку.