Skip to content

Latest commit

 

History

History
1544 lines (1272 loc) · 24.5 KB

slides.md

File metadata and controls

1544 lines (1272 loc) · 24.5 KB
theme title lineNumbers transition
apple-basic
Vue.js - Nutzung, Unterschiede und sein Ecosystem
true
fade-out

Vue.js

Nutzung, Eigenheiten und sein Ökosystem

<style> .slidev-layout { --uno: h-full flex flex-col justify-center } h1 { --uno: text-6xl font-700 leading-20 } h1 + p { --uno: font-700 -mt-4 text-2xl; } </style>

transition: fade-out layout: center

Disclaimer


transition: fade-out

Wie sieht Vue aus?

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <button class="button" @click="count++">Count is: {{ count }}</button>
</template>

<style scoped>
.button {
  background-color: grey;
  padding: 0.25rem 1rem;
  border-radius: 0.25rem;
}
</style>

transition: fade layout: quote

API Style - Options API

"Go with Options API if you are not using build tools, or plan to use Vue primarily in low-complexity scenarios, e.g. progressive enhancement."


transition: fade layout: quote

API Style - Composition API

"Go with Composition API + Single-File Components if you plan to build full applications with Vue."


transition: fade-out

Single File Components

```vue
<script>
</script>

<template>
</template>

<style>
</style>
```

```vue
<script>
</script>

<template>
</template>

<style>
</style>

<i18n>
</i18n>
```

```vue
<script>
</script>

<template>
</template>

<style lang="scss">
</style>

<i18n>
</i18n>
```

```vue
<script setup>
  const { t } = useI18n({ ... })
</script>

<template>
<p>{{ t('hello') }}</p>
</template>

<style lang="scss">
</style>

<i18n lang="json">
{
  "en": {
    "hello": "Hello world!"
  },
  "de": {
    "hello": "Hallo Welt!"
  }
}
</i18n>
```

transition: fade-out

Vue ohne Build Tool

```html {all|5|15|17-22|8-12}
<!doctype html>
<html>
<head>
  <title>Vue ohne Build Tool</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
  <div id="app">
    <button @click="count++">
      Count is: {{ count }}
    </button>
  </div>
</body>
<script>
const { createApp, ref } = Vue

createApp({
  setup() {
    const count = ref(0)
    return { count }
  }
}).mount('#app')
</script>
</html>
```
```html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Vue ohne Build Tool</title>
  <script src="https://unpkg.com/petite-vue" defer init></script>
</head>
<body>
  <div v-scope="{ count: 0 }">
    <button @click="count++">
      Count is: {{ count }}
    </button>
  </div>
</body>
</html>
```

transition: fade

Reactive State

ref()

```vue
<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>
```

```vue
<script setup>
import { ref } from 'vue'

const count = ref(0)

console.log(count.value) // 0
</script>
```

```vue
<script setup>
import { ref } from 'vue'

const count = ref(0)

console.log(count.value) // 0

count.value++
console.log(count.value) // 1
</script>
```

```vue
<script setup>
import { ref } from 'vue'

const count = ref(0)

console.log(count.value) // 0

count.value++
console.log(count.value) // 1
</script>

<template>
  <!-- Automatisch Unrefed -->
  <div>{{ count }}</div>
</template>
```

```vue
<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)

    console.log(count.value) // 0

    count.value++
    console.log(count.value) // 1

    return { count }
  }
}

</script>

<template>
  <!-- Automatisch Unrefed -->
  <div>{{ count }}</div>
</template>
```

transition: fade-out

Reactive State

reactive()

const state = reactive({ count: 0 })

console.log(state.count) // 0

state.count++
console.log(state.count) // 1

Limitationen

```js
const state = reactive({ count: 0 })

state = { count: 1 }
console.log(state.count) // 0

let { count } = state
count++
console.log(state.count) // 0

// reactivity verloren
callSomeFunction(state.count)
```
```js
const state = ref({ count: 0 })

state.value = { count: 1 }
console.log(state.value.count) // 1

let { count } = toRefs(state.value)
count.value++
console.log(state.value.count) // 2

// reactivity bleibt
callSomeFunction(state.value.count)
```

transition: fade-out

Computed State

```js
const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed(() => {
  return firstName.value + ' ' + lastName.value
})
```
```js
const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // Nicht empfohlen
  set(newValue) {
    names = newValue.split(' ')
    firstName.value = names[0]
    lastName.value = names[1]
  }
})
```

transition: fade-out

Data Binding

<!-- Data binding "Mustache" syntax -->
<span>Count is: {{ count }}</span>
```vue-html
<!-- Attribut binding -->
<div v-bind:id="id"></div>
```

```vue-html
<!-- Shorthand Attribut binding -->
<div :id="id"></div>
```

```vue-html
<!-- Shorthand Attribut binding (ab v3.4) -->
<div :id></div>
```
<!-- Multiple Attribut binding -->
<script setup>
const objectOfAttrs = {
  id: 'container',
  class: 'wrapper',
  style: 'background-color:green'
}
</script>
<template>
  <div v-bind="objectOfAttrs"></div>
</template>
<!-- JavaScript Expressions -->
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<button :disabled="age < 18">Registrieren</button>

transition: fade

Directives

<!-- Conditional rendering -->
<p v-if="age < 13">Du bist noch ein Kind</p>
<p v-else-if="age < 18">Du bist ein Teenager</p>
<p v-else>Du bist erwachsen</p>
<!-- Conditional visibility -->
<p v-show="seen">Ich werde angezeigt wenn 'seen' truthy ist</p>
```vue
<!-- Lists -->
<div v-for="item in items">
  {{ item.text }}
</div>
```
```vue
<!-- Lists -->
<div v-for="item in items" :key="item.id">
  {{ item.text }}
</div>
```
<!-- List Range -->
<span v-for="n in 10">{{ n }}</span>

transition: fade

Directives

```vue
<!-- Events -->
<button v-on:[eventName]="count++"> ... </button>
```
```vue
<!-- Events -->
<button v-on:click="count++"> ... </button>
```
```vue
<!-- Events Shorthand -->
<button @click="count++"> ... </button>
```
<!-- Events Method Handler -->
<button @click="onButtonClick"> ... </button>
<!-- Modifier -->
<form @submit.prevent="submit"> ... </form>
<!-- Key Modifier -->
<input @keyup.enter="submit" />
<!-- Andere Integrierte Directives -->
v-text, v-html, v-model, v-slot, v-pre, v-once, v-memo, v-cloak

<!-- Andere Integrierte Modifier -->
.stop, .prevent, .self, .capture, .once, .passive, ...

<!-- Andere Integrierte Key Modifier -->
.enter, .tab, .delete, .esc, .space, .up, .down, .left, .right, ...

transition: fade-out

Directives

alt text


transition: fade-out

Form Inputs

```vue
<template>
  <input :value="text" @input="event => text = event.target.value">

  <input type="checkbox" :checked="accepted" @change="_ => accepted = !accepted">

  <input type="radio" :checked="accepted" @change="_ => accepted = !accepted">

  <select :value="selected" @change="event => selected = event.target.value">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </selected>
</template>
```
```vue
<template>
  <input v-model="text">
  
  <input type="checkbox" v-model="accepted">

  <input type="radio" v-model="accepted">

  <select v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </selected>
</template>
```

transition: fade

Form Input Checkbox

<script setup>
  const checkedNames = ref([])
</script>

<template>
  <div>Checked names: {{ checkedNames }}</div>

  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
  <label for="jack">Jack</label>

  <input type="checkbox" id="john" value="John" v-model="checkedNames" />
  <label for="john">John</label>

  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
  <label for="mike">Mike</label>
</template>

transition: fade-out

Form Input Checkbox

```vue-html
<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no" />
```
```vue-html
<input
  type="checkbox"
  v-model="toggle"
  :true-value="dynamicTrueValue"
  :false-value="dynamicFalseValue" />
```

transition: fade-out

Form Input Select

```vue
<script setup>
  const selected = ref("A")
</script>

<template>
  <div>Selected: {{ selected }}</div>

  <select v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
</template>
```
```vue
<script setup>
  const selected = ref([])
</script>

<template>
  <div>Selected: {{ selected }}</div>

  <select multiple v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
</template>
```
```vue
<script setup>
  const selected = ref([])
</script>

<template>
  <div>Selected: {{ selected }}</div>

  <select multiple v-model="selected">
    <option :value="{ code: 'A' }">A</option>
    <option :value="{ code: 'B' }">B</option>
    <option :value="{ code: 'C' }">C</option>
  </select>
</template>
```

transition: fade-out

Form Input Modifier

<!-- Nutzt @change anstatt von @input -->
<input v-model.lazy="msg" />
<!-- Casted mit parseFloat -->
<input v-model.number="age" />
<!-- Entfernt whitespace -->
<input v-model.trim="msg" />

transition: fade

Side Effects

```vue
<script setup>
const question = ref('')
const answer = ref('')
const loading = ref(false)

watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      answer.value = (await (await fetch('https://yesno.wtf/api')).json()).answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    } finally {
      loading.value = false
    }
  }
})
</script>

<template>
  <p>Stelle eine Ja oder Nein Frage: <input v-model="question" :disabled="loading" /></p>
  <p>{{ answer }}</p>
</template>
```
```vue
<script setup>
const question = ref('')
const answer = ref('')
const loading = ref(false)

watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      answer.value = await (await fetch('https://yesno.wtf/api')).json().answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    } finally {
      loading.value = false
    }
  }
})
</script>
```

transition: fade-out

Side Effects

```js
const itemId = ref(1)
const data = ref(null)

watch(
  itemId,
  async () => {
    const response = await fetch(`https://server:port/items/${itemId.value}`)
    data.value = await response.json()
  }
)
```
```js
const itemId = ref(1)
const data = ref(null)

watch(
  itemId,
  async () => {
    const response = await fetch(`https://server:port/items/${itemId.value}`)
    data.value = await response.json()
  },
  { immediate: true }
)
```
```js
const itemId = ref(1)
const data = ref(null)

watchEffect(async () => {
  const response = await fetch(`https://server:port/items/${itemId.value}`)
  data.value = await response.json()
})
```

transition: fade-out

Composables

```vue
<script setup>
const question = ref('')
const answer = ref('')
const loading = ref(false)

watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      answer.value = await (await fetch('https://yesno.wtf/api')).json().answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    } finally {
      loading.value = false
    }
  }
})
</script>
```
```js
// api.ts
export function useAskYesNoQuestion() {
  const question = ref('')
  const answer = ref('')
  const loading = ref(false)
  
  watch(question, async (newQuestion, oldQuestion) => {
    if (newQuestion.includes('?')) {
      loading.value = true
      answer.value = 'Thinking...'
      try {
        answer.value = await (await fetch('https://yesno.wtf/api')).json().answer
      } catch (error) {
        answer.value = 'Error! Could not reach the API. ' + error
      } finally {
        loading.value = false
      }
    }
  })

  return { question, answer, loading }
}
```
```vue
<script setup>
import { useAskYesNoQuestion } from './api.ts'

const { question, answer, loading } = useAskYesNoQuestion()
</script>
```

transition: fade

Components

```vue
<!-- ButtonCounter.vue -->
<script setup>
  import { ref } from 'vue'

  const count = ref(0)
</script>

<template>
  <button @click="count++">Count is: {{ count }}</button>
</template>
```
```vue
<!-- ButtonCounter.vue -->
<script setup lang="ts">
  import { ref, defineProps } from 'vue'

  const count = ref(0)
  const props = defineProps<{ steps: number }>()
</script>

<template>
  <button @click="count++">Count is: {{ count * props.steps }}</button>
</template>
```
```vue
<!-- ButtonCounter.vue -->
<script setup lang="ts">
  import { ref, defineProps, withDefaults } from 'vue'

  const count = ref(0)
  const props = withDefaults(defineProps<{ steps: number }>(), {
    steps: 1
  })
</script>

<template>
  <button @click="count++">Count is: {{ count * props.steps }}</button>
</template>
```
```vue
<!-- ButtonCounter.vue -->
<script setup lang="ts">
  import { ref, defineProps } from 'vue'

  const count = ref(0)
  // Ab v3.5 mit Reactive Props Destructure
  const { steps = 1 } = defineProps<{ steps: number }>()
</script>

<template>
  <button @click="count++">Count is: {{ count * steps }}</button>
</template>
```
```vue
<!-- ButtonCounter.vue -->
<script setup lang="ts">
  import { ref, defineProps } from 'vue'

  const count = ref(0)
  const { steps = 1 } = defineProps<{ steps: number }>()
  const emit = defineEmits<{
    (e: 'reset'): void
  }>()
</script>

<template>
  <button @click="count++">Count is: {{ count * steps }}</button>
  <button @click="emit("reset")" />
</template>
```
```vue
<!-- ButtonCounter.vue -->
<script setup lang="ts">
  import { ref, defineProps } from 'vue'

  const count = ref(0)
  const { steps = 1 } = defineProps<{ steps: number }>()
  const emit = defineEmits<{
    (e: 'reset', newCount: number): void
  }>()
</script>

<template>
  <button @click="count++">Count is: {{ count * steps }}</button>
  <button @click="emit("reset", 10)" />
</template>
```
```vue
<!-- ButtonCounter.vue -->
<script setup lang="ts">
  import { ref, defineProps } from 'vue'

  const count = ref(0)
  const { steps = 1 } = defineProps<{ steps: number }>()
  const emit = defineEmits<{
    reset: [newCount: number] // Ab v3.3
  }>()
</script>

<template>
  <button @click="count++">Count is: {{ count * steps }}</button>
  <button @click="emit("reset", 10)" />
</template>
```
```vue
<!-- ButtonCounter.vue -->
<script setup lang="ts">
  import { ref, defineProps } from 'vue'

  const count = ref(0)
  const { steps = 1 } = defineProps<{ steps: number }>()
  const emit = defineEmits<{
    reset: [newCount: number]
  }>()
</script>

<template>
  <button v-bind="$attrs" @click="count++">Count is: {{ count * steps }}</button>
  <button @click="emit("reset", 10)" />
</template>
```
```vue
<!-- OtherComponent.vue -->
<script setup>
  import ButtonCounter from './ButtonCounter.vue'
</script>

<template>
  <ButtonCounter />
</template>
```
```vue
<!-- OtherComponent.vue -->
<script setup>
  import ButtonCounter from './ButtonCounter.vue'
</script>

<template>
  <ButtonCounter class="red-button" />
</template>
```

transition: fade

Component Slots

```vue
<!-- Card.vue -->
<script setup lang="ts">
  const props = defineProps<{ title: string, body: string }>()
</script>

<template>
  <div>
    <h3>{{ props.title }}</h3>
    <p>{{ props.body }}</p>
  </div>
</template>
```
```vue
<!-- Card.vue -->
<script setup lang="ts">
  const props = defineProps<{ title: string }>()
</script>

<template>
  <div>
    <h3>{{ props.title }}</h3>
    <slot />
  </div>
</template>
```
```vue
<!-- Card.vue -->
<script setup lang="ts">
</script>

<template>
  <div>
    <slot name="title" />
    <slot>Keine Informationen</slot>
  </div>
</template>
```
```vue
<script setup>
  import Card from './Card.vue'
</script>

<template>
  <Card 
    title="Vue.js" 
    body="An approachable, performant and versatile framework for building web user interfaces."
  />
</template>
```
```vue
<script setup>
  import Card from './Card.vue'
</script>

<template>
  <Card title="Vue.js">
    An approachable, performant and versatile framework for building web user interfaces.
  </Card>
</template>
```
```vue
<script setup>
  import Card from './Card.vue'
</script>

<template>
  <Card title="Vue.js">
    <template v-slot:title>
      <h2>Vue.js</h2>
    </template>
    An approachable, performant and versatile framework for building web user interfaces.
  </Card>
</template>
```
```vue
<script setup>
  import Card from './Card.vue'
</script>

<template>
  <Card title="Vue.js">
    <template #title>
      <h2>Vue.js</h2>
    </template>
    An approachable, performant and versatile framework for building web user interfaces.
  </Card>
</template>
```

transition: fade

Component Slots

```vue
<template>
  <div class="card">
    <div class="card-header">
      <slot name="title" />
    </div>
    <slot>Keine Informationen</slot>
  </div>
</template>
```
```vue
<template>
  <div class="card">
    <div v-if="$slots.header" class="card-header">
      <slot name="title" />
    </div>
    <slot>Keine Informationen</slot>
  </div>
</template>
```

transition: fade-out

Component Slots

```vue
<!-- List.vue -->
<template>
  <ul>
    <li v-for="item in items">
      <slot name="item" />
    </li>
  </ul>
</template>
```
```vue
<!-- List.vue -->
<template>
  <ul>
    <li v-for="item in items">
      <slot name="item" :name="item.name" :rating="item.rating" />
    </li>
  </ul>
</template>
```
```vue
<!-- List.vue -->
<template>
  <ul>
    <li v-for="item in items">
      <slot name="item" v-bind="item" />
    </li>
  </ul>
</template>
```
```vue
<!-- Main.vue -->
<template>
  <List>
    <template #item>
      Das ist nicht so sinnvoll
    </template>
  </List>
</template>
```
```vue
<!-- Main.vue -->
<template>
  <List>
    <template #item="{ name, rating }">
      {{ name }} - {{ rating }} Bewertung
    </template>
  </List>
</template>
```

transition: fade-out

Dynamic Components

```vue
<script setup>
import Home from './Home.vue'
import Posts from './Posts.vue'
import Archive from './Archive.vue'
import { ref } from 'vue'
 
const currentTab = ref('Home')
const tabs = ['Home', 'Posts', 'Archive']
</script>

<template>
  <button
    v-for="tab in tabs"
    :key="tab"
    @click="currentTab = tab"
   >
    {{ tab }}
  </button>
  <Home v-if="currentTab === 'Home'" />
  <Posts v-if="currentTab === 'Posts'" />
  <Archive v-if="currentTab === 'Archive'" />
</template>
```
```vue
<script setup>
// imports...
 
const currentTab = ref('Home')
const tabs = { Home, Posts, Archive }
</script>

<template>
  <button
    v-for="(_, tab) in tabs"
    :key="tab"
    @click="currentTab = tab"
   >
    {{ tab }}
  </button>
  <component :is="tabs[currentTab]" />
</template>
```

transition: fade-out

Styling Block

```vue
<style scoped>
.example {
  color: red;
}
</style>

<template>
  <div class="example">hi</div>
</template>
```
```vue
<style>
.example[data-v-f3f3eg9] {
  color: red;
}
</style>

<template>
  <div class="example" data-v-f3f3eg9>hi</div>
</template>
```
```vue
<style module>
.example {
  color: red;
}
</style>

<template>
  <div :class="$style.red">hi</div>
</template>
```
```vue
<style scoped>
div {
  /* Wird eine CSS Variable */
  color: v-bind('theme.color');
}
</style>

<template>
  <div>hi</div>
</template>

<script setup>
  import { ref } from 'vue'
  const theme = ref({
      color: 'red',
  })
</script>
```

transition: fade-out

CSS Classes

```vue
<template>
  <div :class="(isActive ? 'active' : '') + (hasError ? ' text-danger' : '') + ' static'" />
</template>
```
```vue
<template>
  <div 
    class="static"
    :class="{ 'active': isActive, 'text-danger': hasError }" />
</template>
```
```vue
<template>
  <div 
    class="static"
    :class="[activeClasses, errorClasses]" />
</template>
```
```vue
<template>
  <div 
    class="static"
    :class="[{ [activeClasses]: isActive }, errorClasses]" />
</template>
```

transition: fade-out

Ecosystem and Tools

  • Vite - Build Tool
  • Vitest - Testing Framework
  • Nuxt - Metaframework für SSR, SSG, ESG, ISR oder Hybrid
  • Vueuse - Composition Utilities
  • Tanstack Query - Asynchronous state management
  • radix-vue - Unstyled, accessible Components

transition: fade-out

Die Zukunft von Vue: Vapor Mode

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <div>
    <button @click="count++">
      {{ count }}
    </button>
  </div>
</template>
// Vereinfachter Code

const t0 = template("<div><button></button></div>")
delegateEvents("click")

export default {
  ...
  setup(props, { expose }) {
    expose();
    const count = ref(0)
    return { count, ref }
  }
  render(ctx) {
    const div = t0()
    const b = div.firstChild
    delegate(b, "click", () => () => (ctx.count++))
    renderEffect(() => setText(b, ctx.count))

    return div
  }
}

transition: fade-out

Ausblick

  • KeepAlive
  • Transition/-Group
  • Teleport
  • Suspense and Async Components
  • Provide/Inject
  • Template Refs
  • Eigene Plugins oder Directives
  • ...

transition: fade-out layout: center class: 'text-center pb-5 :'

Danke!

Die Folien gibt es hier github.com/mello-r/vue-introduction