Уйти от jQuery к Svelte, без боли

Всем привет.

Я бэкэнд разработчик и фронтэнд задачи решаю как умею, то есть на jQuery, это работало в 2015, работает и сейчас. Но при наличии Vue и React это уже не камильфо. Из любви к особому пути я решил осваивать не проверенный миллионами разработчиков Angular/React/Vue, я решил попробовать Svelte.

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

По заданию надо было сделать просмотр списка задач и одной задачи из этого списка, CRUD не нужен.

Клиентская часть выполнена как SPA, и вся работа с DOM ведётся через jQuery, для замены jQuery на Svelte это отличный кандидат.

Ниже я расскажу о самых первых препятствиях на этом пути и конечно о том как их преодолеть.
Учебник по Svelte очень доступный и наглядный, но как внедрить Svelte в произвольный проект не очень понятно, ведь Svelte это не библиотека как jQuery, это компилятор, то есть код написанный с использованием директив Svelte надо каким то образом откомпилировать в нативный JS.

Другим камнем преткновения было использование

$

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


[!] (plugin svelte) ValidationError: $ is an illegal variable name

Использование `$`


Конечно может возникнуть вопрос какой такой `$`, если мы jQuery полностью меняем на Svelte?
Да вопрос резонный, но если подумать, то проблема станет понятна — не надо кушать слона целиком, опытные люди слона кушают по частям. В смысле переход от использования jQuery к использованию Svelte, будет серией атомарных рефакторингов, прогресс в работе будет видно всегда и «система» в целом всегда будет в работоспособном состоянии.

План такой: переписываем кусочек, тестируем, чиним, переписываем следующий кусочек и так пока не скушаем всего слона без остатка.

Проблема с `$` возникает только в коде который компилирует Svelte, у меня вся логика SPA вынесена в файл «business-process.js», там эту проблему решать не надо, но весь код из «business-process.js» должен перейти в App.svelte и другие связанные компоненты приложения.

После первого рефакторинга появился App.svelte с таким кодом:


<script>
    jQuery(function ($) {

        function loadStartScreen() {
        loadPaging(0, settings.capacity);
        loadPage(0, settings.capacity);
        }

        loadStartScreen();

        async function search(sample) {
            $('#paging').empty();
            $.ajax({
                type: "GET",
                url: `/api/v1/task/search/${sample}/`,
                dataType: "json",
                success: renderTasks,
                error: function (jqXHR, textStatus, errorThrown) {
                },
                timeout: 500,
            });
        }

        $("#search").submit(async function (event) {
            event.preventDefault();
            const sample = $("#sample").val();

            if (sample) {
                search(sample);
            }
            if (!sample) {
                loadStartScreen();
            }
        });
    });
</script>

<div class="container">
    <h1>Трекер рабочих заданий</h1>

    <form class="form-horizontal" id="search">
        <div class="form-group">
            <label class="control-label col-sm-2" for="sample">
            Поиск
            </label>
            <div class="col-sm-10">
                <input id="sample" class="form-control " type="search"
                 placeholder="Введите наименование задачи" 
                 autofocus autocomplete="on" />
            </div>
        </div>
    </form>
    <div id="taskList">
    </div>
    <div id="paging">
    </div>

Код рабочий, собственно Svelte ещё вообще не используется, на этом этапе Svelte только генерит HTML код который вставляется в

<body>

Переменная `$` изолирована в вызове

jQuery(function ($) {});

конфликта именований нет.

Свяжем переменную sample со значением value элемента input с id=«sample»:


                <input id="sample" class="form-control " type="search"
                 placeholder="Введите наименование задачи"
                 autofocus autocomplete="on"
                 bind:value={sample}
                 />

По событию submit элемента form с id=«search» выполняется код:


        $("#search").submit(async function (event) {
            event.preventDefault();

            if (sample) {
                search(sample);
            }
            if (!sample) {
                loadStartScreen();
            }
        });

Этот код должен выполняться по директиве Svelte, переписываем:


<script>
    async function svelteSearch(event) {
        event.preventDefault();
        if (sample) {
            search(sample);
        }
        if (!sample) {
            loadStartScreen();
        }
    }
</script>
<form class="form-horizontal" id="search"
on:submit="{svelteSearch}">
</form>

Код компилируется, но не работает, потому что функция search() определена в зоне видимости jQuery(function ($) {}); и в глобальном scope эту функцию не видно. Выносим search() в один scope с svelteSearch(event):


<script>
    function loadStartScreen() {
        loadPaging(0, settings.capacity);
        loadPage(0, settings.capacity);
    }

    let sample = '';

    jQuery(function ($) {
        loadStartScreen();
    });
    async function search(sample) {
        $('#paging').empty();
        $.ajax({
            type: "GET",
            url: `/api/v1/task/search/${sample}/`,
            dataType: "json",
            success: renderTasks,
            error: function (jqXHR, textStatus, errorThrown) {
            },
            timeout: 500,
        });
    }
    async function svelteSearch(event) {
        event.preventDefault();
        if (sample) {
            search(sample);
        }
        if (!sample) {
            loadStartScreen();
        }
    }
</script>

Такой код не компилируется:


[!] (plugin svelte) ValidationError: $ is an illegal variable name

Что делать? Гуглить! «svelte how to import jquery»: How do I use jQuery in Svelte
Ответ отмеченный как «правильный» мне не понравился, что бы импортировать стороннюю библиотеку (произвольный *.js), недостаточно написать import *, придётся в rollup.config.js прописывать external:


export default { external []};

Второй вариант с window.$ требует намного меньше телодвижений и поскольку мы планируем полностью отказаться от использования jQuery, то эта директива для импорта будет временной и знакомство с импортом можно отложить на потом.

Применяем копи-паст со стек-овер-флоу:


<script>
    function loadStartScreen() {
        loadPaging(0, settings.capacity);
        loadPage(0, settings.capacity);
    }
    jQuery(function ($) {
        loadStartScreen();
    });

    let sample = '';

    async function search(sample) {
        window.$('#paging').empty();
        window.$.ajax({
            type: "GET",
            url: `/api/v1/task/search/${sample}/`,
            dataType: "json",
            success: renderTasks,
            error: function (jqXHR, textStatus, errorThrown) {
            },
            timeout: 500,
        });
    }
    async function svelteSearch(event) {
        event.preventDefault();
        if (sample) {
            search(sample);
        }
        if (!sample) {
            loadStartScreen();
        }
    }
</script>

<div class="container">
    <h1>Трекер рабочих заданий</h1>
    <form class="form-horizontal" id="search"
    on:submit="{svelteSearch}">
        <div class="form-group">
            <label class="control-label col-sm-2" for="sample">
            Поиск
            </label>
            <div class="col-sm-10">
                <input id="sample" class="form-control " type="search"
                 placeholder="Введите наименование задачи"
                 autofocus autocomplete="on"
                 bind:value={sample}
                 />
            </div>
        </div>
    </form>
// ..skip..
</div>

Осталось избавиться от:


    jQuery(function ($) {
        loadStartScreen();
    });

Удаляем этот код и добавляем в разметку:


<svelte:window on:load = {loadStartScreen} />

Готово.

В документации Svelte, можно найти:
<svelte:document>
The <svelte:document> tag, just like <svelte:window>, gives you a convenient way to declaratively add event listeners to the document object. This is useful for listening to events that don't fire on window, such as mouseenter and mouseleave.

На практике имеем:


[!] (plugin svelte) ParseError: Valid <svelte:...> tag names are svelte:head, svelte:options, svelte:window, svelte:body, svelte:self or svelte:component

То есть тег выпилили из кода, но из документации удалить забыли, к моему изумлению Svelte разрабатывают не боги, а люди, которым тоже свойственно что то оставить на потом и забыть.

Как интегрировать jQuery в Svelte код малой кровью я рассказал.

Как компилировать директивы Svelte в JS код


Выкачиваем в отдельную папку пустой проект как описано в блоге Svelte (по пути устанавливаем Node.js). Теперь переносим скачанные файлы в директорию нашего проекта.

Файлы из «public» перекладываем туда где у нас лежат файлы для скачивания клиентом (браузером пользователя), у меня это «public/assets/Site», стили из «global.css» мне не нужны, этот файл я не перенёс.

Файл «public/index.html», я переложил туда где сервер будет брать шаблон для view: «view/Site/index.html».

Файлы из «src» складываем в исходники фронтэнда — «src/frontend».

Файлы описывающие исходники фронтэнда:

  • rollup.config.js
  • package-lock.json
  • package.json

я переложил в корень, там их подхватила IDE и обо всём позаботилась (установила зависимости).
Теперь необходимо изменить конфигурацию компиляции директив Svelte в JS.

Открываем rollup.config.js и меняем пути к файлам:


export default {
	input: 'src/frontend/main.js',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: 'public/assets/Site/bundle.js'
	},
	plugins: [
		svelte({
			dev: !production,
			css: css => {
				css.write('public/assets/Site/bundle.css');
			}
		}),

Вот и вся сложность с первоначальной интеграцией Svelte в существующий проект.

Устанавливаем зависимости и запускаем отслеживание изменений в исходниках фронтэнда для компиляции на лету:

npm install
npm run dev

Не забудьте


Отредактировать шаблон представления (в моём случае это «view/Site/index.html»):

  • Всё что внутри тега body переносим в App.svelte
  • Добавляем ссылки на необходимые файлы ресурсов

    
    <head>
      <link rel='stylesheet' href='/assets/Site/bundle.css'>
    </head>
    
  • После тега body добавляем «ссылку» на файл сгенерированной сборки:

    
    <body>
    </body>
    <script src='/assets/Site/bundle.js'></script>
    


Код целиком можно посмотреть в репозитории.

До окончательного отказа от jQuery в этом проекте ещё далеко, но статья была не об этом, эта статья о первых шагах по внедрению Svelte в ваш код, о простейших вещах.

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

ЗЫ
Каким получился переход, можно почитать в следующей статье "Как это было"

Only registered users can participate in poll. Log in, please.

Вам помогло?

  • 6.8%помогло3
  • 25.0%поможет потом11
  • 27.3%поможет, но не мне12
  • 0.0%не помогло0
  • 40.9%ни кому не поможет18
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 10

    0
    Насчет svelte:window — это синтаксис 2й версии, на документацию которой вы и ссылаетесь.
    А пишете вы на 3й версии. Они достаточно сильно отличаются

    Насчет $ в svelte файлах — раньше это было просто алиасом для Jquery, нельзя просто Jquery вместо $ использовать?
      0
      нельзя просто Jquery вместо $ использовать?

      Можно конечно, но не нужно, в качестве временного решения меня устраивает window.$

      То что документация по второй версии я это заметил по адресу страницы, поменял двойку на тройку — страница не найдена, поэтому я решил что это актуальная документация. Плюс на странице с документацией ни где не стоит ни версия ни дата.
      И если посмотреть в учебник, то там в примерах есть
      <svelte:window/>
      

      В телеграм канале русскоязычного сообщества, мне посоветовали onMount использовать, работает аналогично.
      0
      Но при наличии Vue и React это уже не камильфо.

      Странно звучит, что нужно использовать что-то просто потому, что это «комильфо». Всегда казалось, что нужно выбирать инструмент для оптимального решения задачи…

      Также хотелось бы увидеть, почему решили в принципе переезжать на Svelte, может это помогло бизнесу заработать n денег путем решения таких-то задач, а может просто время есть на рефакторинг и решили попробовать что-то новое…
        +1
        Из любви к особому пути я решил осваивать не проверенный миллионами разработчиков Angular/React/Vue, я решил попробовать Svelte.

        Что бы принять решение об использовании чего то надо знать плюсы и минусы, для этого надо с технологией познакомиться.
        Это учебный код.
          +1
          «Пет проджект» это всегда интересно и круто, возможно невнимательно читал, не увидел упоминания об этом в статье. Удачи в освоении Svelte :)
        0
        Переезд от jQuery к %любой_новый_модный_фреймворк% без боли невозможен потому, что на деле это означает, что нужно заново написать всё то, что писалось на jQuery десятилетиями, всю кашу из плагинов, которая писалось плохо и неправильно. Это долго и дорого, зачастую дешевле нанять команду JS-программистов, коих сейчас хоть отбавляй, и написать заново. Если конечно речь идёт не о сайтике, где из jQuery — $.ajax да галерея с каруселью (но тогда возникает вопрос, на кой там вообще нужен %любой_новый_модный_фреймворк%?).

        Представьте себе некий визуальный аналог 1С на jQuery, активно использующий, например, www.jeasyui.com/documentation/index.php поверх какого-нибудь backbone, а то и без него. Это катастрофа, его невозможно переписать «по чуть-чуть» на ходу, разные компоненты наследуются и переиспользуются друг в друге, каждый экран такой системы — этакое подобие ExtJS для бедных. В таких случаях единственная надежда — если автор предоставляет drop-in замену (в случае с EasyUI появились версии под Angular/React/Vue, однако они уже скорее напоминают простой UI kit и не могут толком заменить оригинал).
          0
          Статья не о том как проект переписать, статья о том как привычный инструмент jQuery заменить на другой инструмент — Svelte.
          на кой там вообще нужен %любой_новый_модный_фреймворк%?).

          мне например очень нравиться компонентный подход, мне нравиться идея библиотеки визуальных компонентов пользовательского интерфейса, хотя бы ради этого, что бы меньше копипастить и больше включать зависимостей в проект, что бы любой проект был набором «плагинов», а не набором файлов скопированных из предыдущего проекта.
          В коментах выше спрашивали в чём выгода смены отработанного инструмента на новый? у нового инструмента очень клёвая фишка — не надо знать магию CSS селекторов, в разметке не надо расставлять бесконечные id, с новым инструментом пишешь шаблон и в нужных местах появляется нужная информация, использование Svelte экономит время.
          0

          А чем svelte:window on:load лучше просто window.onload? Как он сработает, если до этого на onload уже что-то было?

            0
            А чем svelte:window on:load лучше просто window.onload?

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

            события в JS работают одинаково, всё что на событие повешаешь всё будет вызвано.
            Как я писал выше знающие люди советуют использовать метод onMount(), у в итоге через этот метод реализовано.
              0
              Тем же чем и вообще исползование svelte:window для любых других ивентов:

              • Декларативное описание обработчиков событий, что улучшает читаемость и поддерживаемость кода.
              • Не нужно беспокоиться о том, чтобы снять обработчик, если компонент демонтирован из DOM, что предотвращает неразумное использование памяти и даже утечки.

            Only users with full accounts can post comments. Log in, please.