!!学习自官方教程->教程 | Vue.js (vuejs.org)
1.声明式渲染
<script setup>
import { reactive, ref } from 'vue'
const counter = reactive({ count: 0 })
const message = ref('Hello World!')
</script>
<template>
<h1>{{ message }}</h1>
<p>Count is: {{ counter.count }}</p>
</template>
你在编辑器中看到的是一个 Vue 单文件组件 (Single-File Component,缩写为 SFC)。SFC 是一种可复用的代码组织形式,它将从属于同一个组件的 HTML、CSS 和 JavaScript 封装在使用 .vue
后缀的文件中。
Vue 的核心功能是声明式渲染:通过扩展于标准 HTML 的模板语法,我们可以根据 JavaScript 的状态来描述 HTML 应该是什么样子的。当状态改变时,HTML 会自动更新。
能在改变时触发更新的状态被称作是响应式的。我们可以使用 Vue 的 reactive()
API 来声明响应式状态。由 reactive()
创建的对象都是 JavaScript Proxy,其行为与普通对象一样:
js
import { reactive } from 'vue'
const counter = reactive({
count: 0
})
console.log(counter.count) // 0
counter.count++
reactive()
只适用于对象 (包括数组和内置类型,如 Map
和 Set
)。而另一个 API ref()
则可以接受任何值类型。ref
会返回一个包裹对象,并在 .value
属性下暴露内部值。
js
import { ref } from 'vue'
const message = ref('Hello World!')
console.log(message.value) // "Hello World!"
message.value = 'Changed'
reactive()
和 ref()
的细节在指南 - 响应式基础一节中有进一步讨论。
在组件的 <script setup>
块中声明的响应式状态,可以直接在模板中使用。下面展示了我们如何使用双花括号语法,根据 counter
对象和 message
ref 的值渲染动态文本:
template
<h1>{{ message }}</h1>
<p>count is: {{ counter.count }}</p>
注意我们在模板中访问的 message
ref 时不需要使用 .value
:它会被自动解包,让使用更简单。
在双花括号中的内容并不只限于标识符或路径——我们可以使用任何有效的 JavaScript 表达式。
template
<h1>{{ message.split('').reverse().join('') }}</h1>
现在,试着自己创建一些响应式状态,用它来为模板中的 <h1>
渲染动态的文本内容。
2.Attribute 绑定
<script setup>
import { ref } from 'vue'
const titleClass = ref('title')
</script>
<template>
<h1 :class="titleClass">Make me red</h1>
</template>
<style>
.title {
color: red;
}
</style>
在 Vue 中,mustache 语法 (即双大括号) 只能用于文本插值。为了给 attribute 绑定一个动态值,需要使用 v-bind
指令:
template
<div v-bind:id="dynamicId"></div>
指令是由 v-
开头的一种特殊 attribute。它们是 Vue 模板语法的一部分。和文本插值类似,指令的值是可以访问组件状态的 JavaScript 表达式。关于 v-bind
和指令语法的完整细节请详阅指南 - 模板语法。
冒号后面的部分 (:id
) 是指令的“参数”。此处,元素的 id
attribute 将与组件状态里的 dynamicId
属性保持同步。
由于 v-bind
使用地非常频繁,它有一个专门的简写语法:
template
<div :id="dynamicId"></div>
3.事件监听 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>
可以使用 v-on
指令监听 DOM 事件:
template
<button v-on:click="increment">{{ count }}</button>
因为其经常使用,v-on
也有一个简写语法:
template
<button @click="increment">{{ count }}</button>
此处,increment
引用了一个在 <script setup>
中声明的函数:
vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
// 更新组件状态
count.value++
}
</script>
在函数中,我们可以通过修改 ref 来更新组件状态。
事件处理函数也可以使用内置表达式,并且可以使用修饰符简化常见任务。这些细节包含在指南 - 事件处理。
现在,尝试自行实现 increment
函数并通过使用 v-on
将其绑定到按钮上。
4.表单绑定
我们可以同时使用 v-bind
和 v-on
来在表单的输入元素上创建双向绑定:
template
<input :value="text" @input="onInput">
js
function onInput(e) {
// v-on 处理函数会接收原生 DOM 事件
// 作为其参数。
text.value = e.target.value
}
试着在文本框里输入——你会看到 <p>
里的文本也随着你的输入更新了。
为了简化双向绑定,Vue 提供了一个 v-model
指令,它实际上是上述操作的语法糖:
template
<input v-model="text">
v-model
会将被绑定的值与 <input>
的值自动同步,这样我们就不必再使用事件处理函数了。
v-model
不仅支持文本输入框,也支持诸如多选框、单选框、下拉框之类的输入类型。我们在指南 - 表单绑定中讨论了更多的细节。
5.条件渲染 v-if
<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-if
指令来有条件地渲染元素:
template
<h1 v-if="awesome">Vue is awesome!</h1>
这个 <h1>
标签只会在 awesome
的值为真值 (Truthy) 时渲染。若 awesome
更改为假值 (Falsy),它将被从 DOM 中移除。
我们也可以使用 v-else
和 v-else-if
来表示其他的条件分支:
template
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
现在,示例程序同时展示了两个 <h1>
标签,并且按钮不执行任何操作。尝试给它们添加 v-if
和 v-else
指令,并实现 toggle()
方法,让我们可以使用按钮在它们之间切换。
更多细节请查阅 v-if
:指南 - 条件渲染
6.列表渲染 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">
<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>
我们可以使用 v-for
指令来渲染一个基于源数组的列表:
template
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
这里的 todo
是一个局部变量,表示当前正在迭代的数组元素。它只能在 v-for
所绑定的元素上或是其内部访问,就像函数的作用域一样。
注意,我们还给每个 todo 对象设置了唯一的 id
,并且将它作为特殊的 key attribute 绑定到每个 <li>
。key
使得 Vue 能够精确的移动每个 <li>
,以匹配对应的对象在数组中的位置。
更新列表有两种方式:
-
在源数组上调用变更方法:
jstodos.value.push(newTodo)
-
使用新的数组替代原数组:
jstodos.value = todos.value.filter(/* ... */)
这里有一个简单的 todo 列表——试着实现一下 addTodo()
和 removeTodo()
这两个方法的逻辑,使列表能够正常工作!
关于 v-for
的更多细节:指南 - 列表渲染
7.计算属性 comouted
<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">
<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>
8.生命周期和模版引用 eg:onMounted
目前为止,Vue 为我们处理了所有的 DOM 更新,这要归功于响应性和声明式渲染。然而,有时我们也会不可避免地需要手动操作 DOM。
这时我们需要使用模板引用——也就是指向模板中一个 DOM 元素的 ref。我们需要通过这个特殊的 ref attribute 来实现模板引用:
template
<p ref="pElementRef">hello</p>
要访问该引用,我们需要声明一个同名的 ref:
js
const pElementRef = ref(null)
注意这个 ref 使用 null
值来初始化。这是因为当 <script setup>
执行时,DOM 元素还不存在。模板引用 ref 只能在组件挂载后访问。
要在挂载之后执行代码,我们可以使用 onMounted()
函数:
js
import { onMounted } from 'vue'
onMounted(() => {
// 此时组件已经挂载。
})
这被称为生命周期钩子——它允许我们注册一个在组件的特定生命周期调用的回调函数。还有一些其他的钩子如 onUpdated
和 onUnmounted
。更多细节请查阅生命周期图示。
现在,尝试添加一个 onMounted
钩子,然后通过 pElementRef.value
访问 <p>
,并直接对其执行一些 DOM 操作。(例如修改它的 textContent
)。