client-seitiges Routing: Navigieren zwischen Ansichten, ohne die Anwendung zu verlassen
Möglichkeiten:
hash-basiertes Client-seitiges Routing, z.B.:
example.com/#/home
example.com/#/shop/cart
Client-seitiges Routing basierend auf dem History API, z.B.:
example.com/home
example.com/shop/cart
Für die zweite Option muss der Server zusätzlich konfiguriert werden
// router/index.js
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: About,
},
];
<router-link to="/">Home</router-link>
<router-view />
Wir können Daten an die Route übergebn:
<router-view :todos="todos" />
const routes = [
{
path: '/todos/:todoId',
component: TodoDetailView,
},
];
Zugriff auf die Parameter:
Details of todo: {{ $route.params.todoId }}
this.$router.push('/');
aktive Links erhalten die Klasse router-link-active
(dies gilt für Vue 3)
two-way binding für inputs:
<input v-model="newTitle" />
Two-way Binding in eigenen Komponenten:
<star-rating v-model="productRating" />
<todo-item
v-model:title="todo.title"
v-model:completed="todo.completed"
/>
Implementierung von star-rating
:
modelValue
update:modelValue
Implementierung von todo-item
:
title
, completed
update:title
, update:completed
<li>
{{ completed ? "DONE: " : "TODO: " }}
<input
:value="title"
@input="$emit('update:title', $event.target.value)"
:style="{ border: 'none' }"
/>
<button @click="$emit('update:completed', !completed)">
toggle
</button>
</li>
minimale Prop-Definition einer Komponente:
export default {
props: ['title', 'completed'],
};
export default {
props: {
title: String,
completed: Boolean,
},
};
export default {
props: {
title: {
type: String,
required: true,
},
completed: {
type: Boolean,
default: false,
},
},
};
Einrichtung eines Vue-TypeScript-Projekts:
während der Projekterstellung mit Vue-CLI, wähle "Manually select features"
<script lang="ts">
// ...
</script>
in Komponentendefnitionen:
export default {
name: 'TodoApp',
// ...
};
wird zu:
import { defineComponent } from 'vue';
export default defineComponent({
name: 'TodoApp',
// ...
});
Konfiguration von Vetur in VS Code:
Aktiviere die Option: Vetur > Experimental: Template Interpolation Service
Deklarieren der Struktur von Komponenten-State:
type ComponentState = {
todos: Array<Todo>;
};
export default defineComponent({
data(): ComponentState {
// ...
},
});
Definieren von Props mit TypeScript-Annotationen:
import { defineComponent, PropType } from 'vue';
// define interface / type "Todo" here
export default defineComponent({
props: {
type: [] as PropType<Array<Todo>>,
required: true,
},
});
nur Vue 3:
Definieren von Events und Event-Payloads (in Validierungsfunktionen):
export default defineComponent({
emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */
delete(id: number) {
return true;
},
toggle(id: number) {
return true;
},
/* eslint-enable @typescript-eslint/no-unused-vars */
},
});
In komplexeren Anwendungen oder Komponenten macht es Sinn, den Anwendungszustand (model) von der Ansicht (view) zu trennen.
Oft wird der gesamte Anwendungszustand durch ein Datenmodell repräsentiert. Jede Änderung am Anwendungszustand läuft über das Datenmodell.
eine Mutation beschreibt ein bestimmtes Ereignis in einer Anwendung (in anderen Tools: Action)
Eine Mutation hat einen type, und eventuell eine payload-Property
Beispiele für Mutationen, wie sie in den Devtools erscheinen:
{
"type": "addTodo",
"payload": {
"title": "learn Vue"
}
}
{
"type": "deleteCompletedTodos"
}
Manuelle Verwendung eines Vuex-Stores:
const store = new Vuex.Store({
// set up store here
});
store.commit('addTodo', { title: 'foo' });
store.commit('addTodo', { title: 'bar' });
store.commit('setTodoCompleted', {
id: 1,
completed: true,
});
store.commit('deleteTodo', { id: 2 });
console.log(JSON.stringify(store.state));
Konfiguration von Vuex:
const store = new Vuex.Store({
state: { todos: [] },
getters: {
numIncomplete(state) {
return state.todos.filter((t) => !t.completed).length;
},
},
mutations: {
addTodo(state, payload) {
// ...
},
deleteTodo(state, payload) {
// ...
},
},
actions: {
async loadFromApi(context) {
// ...
},
},
});
Verwenden eines Vuex-Stores in einer Vue-Anwendung:
new Vue({
// ...
store: store,
});
Der Store ist innerhalb von Komponenten als $store
verfügbar
Zugriff auf den Vuex-State aus Komponenten:
<todo-item v-for="todo in $store.state.todos" ... />
<p>Incomplete todos: {{ $store.getters.numIncomplete }}</p>
Auslösen von Mutationen aus Vue:
<todo-item
:todo="todo"
@toggle="$store.commit('toggleTodo', { id: todo.id })"
@delete="$store.commit('deleteTodo', { id: todo.id })"
/>
Auslösen einer Action aus Vue:
export default {
async created() {
this.$store.dispatch('loadFromApi');
},
};
Animation von Eingangs- oder Ausgangs- Transitions:
<transition name="fade">
<button>delete</button>
</transition>
Name der Transition: fade
Definition der fade-Transition:
während der Animation:
.fade-enter-active {
transition: opacity 5s;
}
.fade-leave-active {
transition: opacity 5s;
}
Styles für Anfang / Ende:
.fade-enter {
opacity: 0;
}
.fade-leave-to {
opacity 0;
}
Transitions für Gruppen von Elementen, bei denen einzelne Elemente erscheinen oder verschwinden können
<transition-group name="list-fade" tag="ul">
<li v-for="todo in todos" :key="todo.id">
{{ todo.title }}
</li>
</transition-group>
.list-fade-enter-active,
.list-fade-leave-active {
transition: opacity 0.5s;
}
.list-fade-enter,
.list-fade-leave-to {
opacity: 0;
}
Dokumentation: https://v3.vuejs.org/guide/transitions-overview.html
Konzept aus React: JSX - Kombination aus JavaScript und XML
Render-Funktionen können als Alternative zu Templates verwendet werden
export default {
name: 'Foo',
render() {
return (
<div>
foo
<button>bar</button>
</div>
);
},
};