Я бэкэнд разработчик и фронтэнд задачи решаю как умею, то есть на 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 в ваш код, о простейших вещах.
Спасибо за внимание.
Th0r_N13
Насчет svelte:window — это синтаксис 2й версии, на документацию которой вы и ссылаетесь.
А пишете вы на 3й версии. Они достаточно сильно отличаются
Насчет $ в svelte файлах — раньше это было просто алиасом для Jquery, нельзя просто Jquery вместо $ использовать?
SbWereWolf Автор
Можно конечно, но не нужно, в качестве временного решения меня устраивает window.$
То что документация по второй версии я это заметил по адресу страницы, поменял двойку на тройку — страница не найдена, поэтому я решил что это актуальная документация. Плюс на странице с документацией ни где не стоит ни версия ни дата.
И если посмотреть в учебник, то там в примерах есть
В телеграм канале русскоязычного сообщества, мне посоветовали onMount использовать, работает аналогично.