互动教程(Vue3)
列表渲染
条件语句后是循环操作,也是基本操作
可以使用 v-for
指令来渲染一个基于源数组的列表
js
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
这里的 todo
是一个局部变量,表示当前正在迭代的数组元素。它只能在 v-for
所绑定的元素上或是其内部访问,就像函数的作用域一样。
注意,我们还给每个 todo
对象设置了唯一的 id
,并且将它作为特殊的 key attribute
绑定到每个 <li>
。key
使得 Vue
能够精确地移动每个
-
,以匹配对应的对象在数组中的位置。
更新列表
在源数组上调用变更方法
jsthis.todos.push(newTodo)
使用新的数组替代原数组
jsthis.todos = this.todos.filter(/* ... */)
示例代码:
这里有一个简单的 todo 列表------试着实现一下 addTodo() 和 removeTodo() 这两个方法的逻辑,使列表能够正常工作!
js<script> // 给每个 todo 对象一个唯一的 id 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: { // @submit.prevent属性可以实现当我们点击提交按钮时,表单并不会提交,而是会触发绑定的函数 // 所以addTodo被触发 addTodo() { // 因为"newTodo" 用v-model进行修饰,所以有双向关联,newTodo会得到用户输入的内容 // 可以添加到todos列表中 this.todos.push({id:id++,text:this.newTodo}) // 常规操作 this.newTodo = '' }, removeTodo(todo) { // 遍历this.todos,如果其中的元素item != 当前删除的这个才放到this.todos这个数组 this.todos = this.todos.filter(item => item != todo) // 参考答案 // 这里用的是 !== // this.todos = this.todos.filter((t) => t !== 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
js<li v-for="todo in todos"> <input type="checkbox" v-model="todo.done"> ... </li>
可以使用 computed 选项声明一个响应式的属性,它的值由其他属性计算而来
示例代码:
试着添加 filteredTodos 计算属性并实现计算逻辑
js<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: { // ② 根据 `this.hideCompleted` 返回过滤后的 todo 项目 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
来实现模板引用:js<p ref="pElementRef">hello</p>
此元素将作为
this.$refs.pElementRef
暴露在this.$refs
上。然而,你只能在组件挂载之后访问它。js<script> export default { mounted() { // 重新设置引用的pElementRef元素的textContent this.$refs.pElementRef.textContent = "World" } } </script> <template> <p ref="pElementRef">Hello</p> </template>
这被称为生命周期钩子------它允许我们注册一个在组件的特定生命周期调用的回调函数。还有一些其他的钩子如
created
和updated
小结
- 可以通过`ref
标签
在Vue
中操作DOM
元素 Vue
提供了若干生命周期的钩子函数,让开发者可以在不同的时间点操作操作元素
侦听器(Watch)
有时我们需要响应性地执行一些"副作用"------例如,当一个数字改变时将其输出到控制台。我们可以通过侦听器来实现它:
jsexport default { data() { return { count: 0 } }, watch: { count(newCount) { // 没错,console.log() 是一个副作用 console.log(`new count is: ${newCount}`) } } }
示例代码
js<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状态,每次点击 Fetch next dodo按钮时 // 因为@click="todoId++",所以todoId的状态值会自增1 // 然后触发watch里的todoId方法,执行fetchData,获取下一条数据 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 应用往往是由嵌套组件创建的。
父组件可以在模板中渲染另一个组件作为子组件。要使用子组件,我们需要先导入它:
jsimport ChildComp from './ChildComp.vue' export default { components: { ChildComp } }
使用模板
js<ChildComp />
示例代码
js<script> export default { // 注册子组件 } </script> <template> <!-- render child component --> </template>
小结
- 使用
import
可以引入子组件 - 使用
components
选项可以注册子组件
Props
子组件可以通过
props
从父组件接受动态数据。首先,需要声明它所接受的props
:js// 在子组件中 export default { props: { msg: String } }
msg prop
就会暴露在this
上,并可以在子组件的模板中使用父组件可以像声明
HTML attributes
一样传递props
。若要传递动态值,也可以使用v-bind
语法:示例代码
js<script> import ChildComp from './ChildComp.vue' export default { components: { ChildComp }, data() { return { greeting: 'Hello from parent' } } } </script> <template> <!-- 父组件的greeting状态值传递给子组件用prop声明接收的msg --> <ChildComp :msg="greeting" /> <!-- 使用HTML attributes 传递给子组件 --> <ChildComp msg="123"> </ChildComp> </template>
小结
- 子组件可以通过
prop
指定接收哪些外部状态 -- 大同小异,差不多就是模型定义的公开属性 - 可以通过
html attributes
/v-bind
将状态值从父组件传给子组件
Emits
除了接收
props
,子组件还可以向父组件触发事件emit
: 发射jsexport default { // 声明触发的事件 emits: ['response'], created() { // 带参数触发 this.$emit('response', 'hello from child') } }
this.$emit()
的第一个参数是事件的名称。其他所有参数都将传递给事件监听器。示例代码
js<script> import ChildComp from './ChildComp.vue' export default { components: { ChildComp }, data() { return { childMsg: 'No child msg yet' } } } </script> <template> <!-- 父组件可以使用 ``v-on`` 监听子组件触发的事件------这里的处理函数接收了子组件触发事件时的额外参数并将它赋值给了本地状态 --> <ChildComp @response="(msg) => childMsg = msg" /> <p>{{ childMsg }}</p> </template>
小结
- 在子组件使用
emits
选项可以声明触发某些事件 - 父组件可以使用
v-on
在响应子组件的emits
的操作 -- 数据如何传递
插槽(slots)
除了通过
props
传递数据外,父组件还可以通过插槽(slots)
将模板片段传递给子组件:js<ChildComp> This is some slot content! </ChildComp>
示例代码
App.vue
js<script> import ChildComp from './ChildComp.vue' export default { components: { ChildComp }, data() { return { msg: 'from parent' } } } </script> <template> <ChildComp>妖怪,还不现出原形</ChildComp> </template>
ChildComp.vue
js<template> <slot>Fallback content</slot> </template>
默认显示
Fallback content
, 如果父组件中有具体内容,则显示父组件中的内容小结
- 父组件可以通过插槽
(slots)
将模板片段传递给子组件 -- 可以理解为一种自定义的标签
总结
- 如何操作列表
- 通过
computed
选项可以关联其它的状态 - 了解了
Vue
模板的生命周期 - 通过
watch
选项在其它状态变化时触发指定操作 - 如何引入外部组件
- 多个组件间如何进行数据的传递:
props
(父组件传子组件),Emits
(子组件回传父组件),slot
(父组件传子组件)
参考