Исходный код: https://github.com/ITmind/django-vue-template
Вторым по популярности реактивным фрамеворком является Vue. Синтаксис Composition API в версии Vue 3 очень напоминает синтаксис Svelte. Перепишем наш шаблон для Svelte на Vue, что бы посмотреть на отличия.
Копируем шаблон Svelte, удаляем папку svelte_app и создаем папку vue_app. В папке vue_app создаем приложение Vue на базе штатного шаблона из Vite. В папке /vue_app/src/components/ файл Notes.vue.
Код в Notes.vue почти идентичен коду из Svelte.vue. Область шаблона:
<template>
<h3>Заметки</h3>
<table>
<thead>
<tr>
<th>Дата</th>
<th>Название</th>
<th colspan="2">
<button class="btnadd" @click="add">Добавить</button>
</th>
</tr>
</thead>
<tbody>
<tr v-for="note in notes" @click="event => onSelectRow(note)">
<td v-if="note.edit"><input class="editable" type="date" v-model="note.date"></td>
<td v-else>{{ note.date }}</td>
<td v-if="note.edit"><input class="editable" v-model="note.title"></td>
<td v-else>{{ note.title }}</td>
<td>
<button @:click="event => onEditClick(event, note)">Edit</button>
</td>
<td><a href="notes/{note.slug}/">Details</a></td>
</tr>
</tbody>
</table>
<input v-if="currentNote.edit" class="note editable" v-model="currentNote.note">
<div v-else class="note noteditable">{{ currentNote.note }}</div>
</template>
Основное отличие от Svelte в том, что здесь условия и циклы не отдельные теги, а атрибуты обычных тегов. На мой взгляд это ухудшает чтение шаблона.
Область кода (script) практически повторят код из Svelte. В Vue переменные просто так не становятся “реактивными”, их нужно объявить особым образом . Для массива используем reactive() который создает прокси объекта с переопределенными методами get и set. Для текущей записи используем ref(). И перепишем функцию чтения данных, т.к. reactive() является не переопределяемым. (т.е. нужно менять объект по первоначальной ссылке)
import { ref, reactive, onMounted } from 'vue'
const notes = reactive([...Array(3).keys()].map(() => {
return {date: '', title: '1'}
}));
let currentNote = ref({note: ''});
onMounted(readTable)
async function readTable() {
let response = await fetch("/notes");
const temp = await response.json()
notes.length = 0
for(let el of temp){
notes.push(el)
}
notes.map((value) => value['edit'] = false);
}
reactive() делает все элементы массив тоже reactive и для сравнивать элемент массив с currentNote нам нужно следующим образом:
if (currentNote.value !== note) {
...
}
Value у ref() это то же reactive (прокси) и два прокси можно сравнить между собой.
И еще один момент связанный с отладкой. По умолчанию Vite делает build в режиме production и код js минифицирован. Это когнитивно усложняет отладку, поэтому в vite.config.js включим sourcemap:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
// параметр base определят директорию прямой ссылки на статические файлы (используется при разыменовывании относительных ссылок в скриптах)
base: "/static/spa",
//для отладки
mode: "development",
build: {
//Для отладки
sourcemap: true,
//сюда будем выкладывать артефакты
outDir: "../static/spa",
//сюда картинки и прочее midia
assetsDir: './assets',
rollupOptions: {
output: {
//название главного файла javascript
entryFileNames: '[name].js',
//Название остальных файлов. Если не прописать, то к имени будет дописывать случайный id
assetFileNames: '[name].[ext]',
},
},
}
})
После этого в в панели разработчика браузера появится дополнительная “папка” с нормальным кодом в котором мы можем ставить точки останова (Если не появилась, то установите расширение vue js devtool)