Attribute v-bind
<script setup>
import { ref } from 'vue'
const titleClass = ref('title')
</script>
<template>
<h1 :class="titleClass">Make me red</h1> <!-- 此處新增一個動態 class 繫結 -->
</template>
<style>
.title {
color: red;
}
</style>
titleClass是一個類,它指向物件title。把這個類繫結到h1標籤上,在最後為title的顏色屬性賦值。繫結用的是v-bind,簡寫為:
class是規定好的HTML標籤屬性
事件監聽 v-on
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment(){
count.value--
}
</script>
<template>
<!-- 使此按鈕生效 -->
<button @click="increment">Count is: {{ count }}</button>
</template>
把button用v-on:click監聽,簡寫為@
,監聽click事件.
表單繫結 v-model
把函式和展示繫結在一起
<script setup>
import { ref } from 'vue'
const text = ref('')
function onInput(e) {
text.value = e.target.value
}
</script>
<template>
<input :value="text" @input="onInput" placeholder="Type here">
<p>{{ text }}</p>
</template>
把onInput
函式和text
物件繫結在一起
<script setup>
import { ref } from 'vue'
const text = ref('')
</script>
<template>
<input v-model="text" placeholder="Type here">
<p>{{ text }}</p>
</template>
條件渲染v-if,v-else
給元件加上邏輯關係來渲染
<script setup>
import { ref } from 'vue'
const awesome = ref(true)
function toggle() {
awesome.value = !awesome.value
}
</script>
<template>
<button @click="toggle">Toggle</button>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
</template>
這樣實現有條件的渲染元件
迴圈渲染v-for
<script setup>
import { ref } from 'vue'
// 給每個 todo 物件一個唯一的 id
let id = 0
const newTodo = ref('')
const todos = ref([
{ id: id++, text: 'Learn HTML' },
{ id: id++, text: 'Learn JavaScript' },
{ id: id++, text: 'Learn Vue' }
])
function addTodo() {
todos.value.push({ id: id++, text: newTodo.value })
newTodo.value = ''
}
function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" required placeholder="new todo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
</template>
對todos進行迴圈渲染,並且把標籤<li>的key繫結到id上
計算屬性computed()
<script setup>
import { ref, computed } from 'vue'
let id = 0
const newTodo = ref('')
const hideCompleted = ref(false)
const todos = ref([
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
])
const filteredTodos = computed(() => {
return hideCompleted.value
? todos.value.filter((t) => !t.done)
: todos.value
})
function addTodo() {
todos.value.push({ id: id++, text: newTodo.value, done: false })
newTodo.value = ''
}
function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" required placeholder="new todo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
</template>
<style>
.done {
text-decoration: line-through;
}
</style>
我個人的理解是,本來要隱藏這個done的任務,是需要一個函式來計算是不是完成了,比如
function filteredTodos() { return hideCompleted.value ? todos.value.filter((t) => !t.done) : todos.value }
這樣的話,在每次我點選隱藏按鈕的時候,都要計算一遍過濾的陣列
其實這個過濾器filter,並沒有刪掉陣列中的元素,只是返回了一個經過過濾的子陣列
如果我採用computed(),這實質上是一個屬性,也就是一種vue提供的資料格式。
const filteredTodos = computed(() => {
return hideCompleted.value
? todos.value.filter((t) => !t.done)
: todos.value
})
用computed()的話,來定義一個量,其括號裡面放的其實還是函式內的語句,但問題在於
- 這個不是函式,而是一個量,像const int b;中的b
- 當這其中的值不變化,不需要重新呼叫computed中的函式
What that means? 意思就是如果我採用一個函式,每次我點選隱藏按鈕的時候,都要計算一遍過濾的陣列。而採用computed就不需要了,如果我沒有改變任務是否完成(即todos陣列的值),我點選按鈕得到的值是上一次快取下來的
模板引用
<p ref="pElementRef">hello</p>
宣告一個指向DOM元素的ref,這是一種特殊的ref,要想訪問它,我們需要宣告一個同名的ref
const pElementRef = ref(null)
使用null進行初始化,是因為<script setup>
執行的時候,後面模板中的DOM還未渲染。因此要採用函式來使這部分程式碼在元件掛載之後再執行
生命週期
使用onMounted()
來實現在元件掛載之後再執行其內部的程式碼
<script setup>
import { ref, onMounted } from 'vue'
const pElementRef = ref(null)
onMounted(() => {
pElementRef.value.textContent = 'mounted!'
})
</script>
<template>
<p ref="pElementRef">Hello</p>
</template>
在這裡,textContent
是一種DOM屬性,是規定好的,其他規定好的https://www.cnblogs.com/Ch1ldKing/p/18293034
偵聽器watch()
<script setup>
import { ref, watch } from 'vue'
const todoId = ref(1)
const todoData = ref(null)
async function fetchData() {
todoData.value = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
todoData.value = await res.json()
}
fetchData()
watch(todoId, fetchData)
</script>
<template>
<p>Todo id: {{ todoId }}</p>
<button @click="todoId++" :disabled="!todoData">Fetch next todo</button>
<p v-if="!todoData">Loading...</p>
<pre v-else>{{ todoData }}</pre>
</template>
對於這段程式碼,watch()
在todoId變化時呼叫了fetchData()
,實現監聽。
此處,fetchData()
的作用是每次清空todoData的值,並且抓取這段傳輸的json資料顯示出來,並賦值給todoData
子元件
vue的nb之處之一在於巢狀元件
比如我有一個檔案ChildComp.vue,是我寫好的一個元件
我想在App.vue中呼叫它作為一個小元件
<!-- ChildComp.vue -->
<template>
<h2>A Child Component!</h2>
</template>
<!-- App.vue -->
<script setup>
import ChildComp from './ChildComp.vue'
</script>
<template>
<ChildComp />
</template>
渲染App.vue的效果:
子元件的引數Props
和其他元件(button,form)等一樣,vue的子元件也支援attributes,可以用v-bind進行繫結。不同的是,子元件有何attributes需要自己定義。
attributes是指,比如button有type,img有src,是規定好的HTML標籤屬性
<!-- ChildComp.vue -->
<script setup>
const props = defineProps({
msg: String
})
</script>
<template>
<h2>{{ msg || 'No props passed yet' }}</h2>
</template>
props中就是定義好的屬性,此處有一個msg
屬性是對於呼叫這個子元件的父元件而言的。在父元件眼裡,這個msg是一個屬性。而對於子元件內部,msg是一個引數(變數)
<!-- App.vue -->
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'
const greeting = ref('Hello from parent')
</script>
<template>
<ChildComp :msg="greeting"/>
</template>
此處透過:(即v-bind)
把msg屬性賦值為greeting。如果子元件中沒有msg,就會報錯
子元件定義事件Emit
和其他元件(button,form)等一樣,vue的子元件也支援事件,可以用v-on進行繫結。不同的是,子元件有何事件需要自己定義。
事件是指,比如button有click,form有submit,是規定好的HTML響應事件
<!-- ChildComp.vue -->
<script setup>
const emit = defineEmits(['response'])
emit('response', 'hello from child')
</script>
<template>
<h2>Child component</h2>
</template>
defineEmits
是規定的函式,注意到其中的引數其實是一個陣列,意味著你可以定義多個事件,like this
<!-- ChildComp.vue -->
<script setup>
const emit = defineEmits(['response','update'])
emit('response', 'hello from child')
emit('update', 'hello')
</script>
<template>
<h2>Child component</h2>
</template>
然後在父元件中監聽事件(不是watch,是v-on)
<!-- App.vue -->
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'
const childMsg = ref('No child msg yet')
</script>
<template>
<ChildComp @update="(msg) => childMsg = msg" />
<p>{{ childMsg }}</p>
</template>
插槽slot
在父元件中,呼叫子元件的時候插入內容
<!-- App.vue -->
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'
const msg = ref('from parent')
</script>
<template>
<ChildComp>Message: {{ msg }}</ChildComp>
</template>
這時候,其實如同<p>text</p>
這種,在中間顯示一些值。但我們自己寫的元件需要在子元件中進行定義。
<!-- ChildComp.vue -->
<template>
<slot>Fallback content</slot>
</template>