互动教程(Vue3)
列表渲染
条件语句后是循环操作,也是基本操作
可以使用 v-for
指令来渲染一个基于源数组的列表
1 2 3 4 5
| <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} </li> </ul>
|
这里的 todo
是一个局部变量,表示当前正在迭代的数组元素。它只能在 v-for
所绑定的元素上或是其内部访问,就像函数的作用域一样。
注意,我们还给每个 todo
对象设置了唯一的 id
,并且将它作为特殊的 key attribute
绑定到每个 <li>
。key
使得 Vue
能够精确地移动每个
,以匹配对应的对象在数组中的位置。
更新列表
在源数组上调用变更方法
1
| this.todos.push(newTodo)
|
使用新的数组替代原数组
1
| this.todos = this.todos.filter()
|
示例代码:
这里有一个简单的 todo 列表——试着实现一下 addTodo() 和 removeTodo() 这两个方法的逻辑,使列表能够正常工作!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <script>
let id = 0
export default { data() { return { newTodo: '', todos: [ { id: id++, text: 'Learn HTML' }, { id: id++, text: 'Learn JavaScript' }, { id: id++, text: 'Learn Vue' } ] } }, methods: { addTodo() { this.todos.push({id:id++,text:this.newTodo}) this.newTodo = '' }, removeTodo(todo) { this.todos = this.todos.filter(item => item != todo) console.log(Object.prototype.toString.call(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>
|
小结:
- 可以用赋值/filter的方法生成数组,遍历操作: (变量) => 变量相关逻辑
form
表单可以使用@submit.prevent = "方法名"
来阻止默认的提交,并触发指定的方法
!==
执行严格的不相等比较,不会在检查不相等之前转换操作数的类型。这里是对象比较,比较的是相同
计算属性
这是通过给每一个todo
对象添加done
属性并将其绑定到复选框上来添加切换功能
如何基于状态渲染不同的列表项,这个要求有点类似上一篇的v-if-else
1 2 3 4
| <li v-for="todo in todos"> <input type="checkbox" v-model="todo.done"> ... </li>
|
可以使用 computed 选项声明一个响应式的属性,它的值由其他属性计算而来
示例代码:
试着添加 filteredTodos 计算属性并实现计算逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <script> let id = 0
export default { data() { return { newTodo: '', hideCompleted: false, todos: [ { id: id++, text: 'Learn HTML', done: true }, { id: id++, text: 'Learn JavaScript', done: true }, { id: id++, text: 'Learn Vue', done: false } ] } }, computed: { filteredTodos() { return this.hideCompleted ? this.todos.filter((t) => t.done !== true) : this.todos } }, methods: { addTodo() { this.todos.push({ id: id++, text: this.newTodo, done: false }) this.newTodo = '' }, removeTodo(todo) { this.todos = this.todos.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"> <!--<li v-for="todo in todos" :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>
|
显示所有的todos

隐藏已完成的todos

可以把右侧的show Error
关闭,弹出错误提示的时候影响输入
小结
- 计算属性的值依赖其其它的属性而来的,它会自动跟踪其计算中所使用的到的其他响应式状态
- 计算属性可以放在
computed:
选项里
生命周期和模板引用
Vue
为我们处理了所有的 DOM
更新,这要归功于响应性和声明式渲染。然而,有时我们也会不可避免地需要手动操作 DOM
。 – 类似C
要有指针去访问地址的概念;有自动挡还是要了解手动档。
这时我们需要使用模板引用——也就是指向模板中一个 DOM
元素的 ref
。我们需要通过这个特殊的 ref attribute
来实现模板引用:
1
| <p ref="pElementRef">hello</p>
|
此元素将作为 this.$refs.pElementRef
暴露在 this.$refs
上。然而,你只能在组件挂载之后访问它。
1 2 3 4 5 6 7 8 9 10 11 12
| <script> export default { mounted() { this.$refs.pElementRef.textContent = "World" } } </script>
<template> <p ref="pElementRef">Hello</p> </template>
|

这被称为生命周期钩子——它允许我们注册一个在组件的特定生命周期调用的回调函数。还有一些其他的钩子如 created
和 updated

小结
- 可以通过`ref
标签
在Vue
中操作DOM
元素
Vue
提供了若干生命周期的钩子函数,让开发者可以在不同的时间点操作操作元素
侦听器(Watch)
有时我们需要响应性地执行一些“副作用”——例如,当一个数字改变时将其输出到控制台。我们可以通过侦听器来实现它:
1 2 3 4 5 6 7 8 9 10 11 12 13
| export default { data() { return { count: 0 } }, watch: { count(newCount) { console.log(`new count is: ${newCount}`) } } }
|
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <script> export default { data() { return { todoId: 1, todoData: null } }, methods: { async fetchData() { this.todoData = null const res = await fetch( `https://jsonplaceholder.typicode.com/todos/${this.todoId}` ) this.todoData = await res.json() } }, mounted() { this.fetchData() }, watch: { todoId() { this.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
选项可以设置当某个值变化时触发指定的操作,有点像计算属性的更泛的表达
组件
真正的 Vue 应用往往是由嵌套组件创建的。
父组件可以在模板中渲染另一个组件作为子组件。要使用子组件,我们需要先导入它:
1 2 3 4 5 6 7
| import ChildComp from './ChildComp.vue'
export default { components: { ChildComp } }
|
使用模板
示例代码
1 2 3 4 5 6 7 8 9
| <script> export default { } </script>
<template> </template>
|


小结
- 使用
import
可以引入子组件
- 使用
components
选项可以注册子组件
Props
子组件可以通过 props
从父组件接受动态数据。首先,需要声明它所接受的 props
:
1 2 3 4 5 6
| export default { props: { msg: String } }
|
msg prop
就会暴露在 this
上,并可以在子组件的模板中使用
父组件可以像声明 HTML attributes
一样传递 props
。若要传递动态值,也可以使用 v-bind
语法:
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script> import ChildComp from './ChildComp.vue'
export default { components: { ChildComp }, data() { return { greeting: 'Hello from parent' } } } </script>
<template> <ChildComp :msg="greeting" /> <ChildComp msg="123"> </ChildComp> </template>
|

小结
- 子组件可以通过
prop
指定接收哪些外部状态 – 大同小异,差不多就是模型定义的公开属性
- 可以通过
html attributes
/ v-bind
将状态值从父组件传给子组件
Emits
除了接收 props
,子组件还可以向父组件触发事件
emit
: 发射
1 2 3 4 5 6 7 8
| export default { emits: ['response'], created() { this.$emit('response', 'hello from child') } }
|
this.$emit()
的第一个参数是事件的名称。其他所有参数都将传递给事件监听器。
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script> import ChildComp from './ChildComp.vue'
export default { components: { ChildComp }, data() { return { childMsg: 'No child msg yet' } } } </script>
<template> <ChildComp @response="(msg) => childMsg = msg" /> <p>{{ childMsg }}</p> </template>
|

小结
- 在子组件使用
emits
选项可以声明触发某些事件
- 父组件可以使用
v-on
在响应子组件的emits
的操作 – 数据如何传递
插槽(slots)
除了通过 props
传递数据外,父组件还可以通过插槽 (slots)
将模板片段传递给子组件:
1 2 3
| <ChildComp> This is some slot content! </ChildComp>
|
示例代码
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script> import ChildComp from './ChildComp.vue'
export default { components: { ChildComp }, data() { return { msg: 'from parent' } } } </script>
<template> <ChildComp>妖怪,还不现出原形</ChildComp> </template>
|
ChildComp.vue
1 2 3
| <template> <slot>Fallback content</slot> </template>
|
默认显示 Fallback content
, 如果父组件中有具体内容,则显示父组件中的内容

小结
- 父组件可以通过插槽
(slots)
将模板片段传递给子组件 – 可以理解为一种自定义的标签
总结
- 如何操作列表
- 通过
computed
选项可以关联其它的状态
- 了解了
Vue
模板的生命周期
- 通过
watch
选项在其它状态变化时触发指定操作
- 如何引入外部组件
- 多个组件间如何进行数据的传递:
props
(父组件传子组件),Emits
(子组件回传父组件),slot
(父组件传子组件)
参考
- Vue中常用的数组方法.filter()、.map()、.forEach()、.find()、.findIndex()、.some()、.every()
- vue中阻止表单自动提交
- 比较 JavaScript 对象的四种方式