
TodoList 案例
下面这个组件把响应式的几个基本用法都用上了,后面的原理都围绕它展开。
<script setup lang="ts">import { ref } from 'vue'
interface Todo { id: number content: string completed: boolean}
// 输入框绑定的新任务内容const newTodo = ref('')const todos = ref<Todo[]>([])
function addTodo() { const value = newTodo.value.trim() if (!value) return
todos.value.push({ id: Date.now(), content: value, completed: false, })
newTodo.value = ''}
function handleChange(id: number) { // find 的谓词返回布尔真值;find 本身返回匹配到的元素,找不到则返回 undefined const item = todos.value.find((todo) => todo.id === id)
if (item) { item.completed = !item.completed }}</script>
<template> <div class="todo-app"> <h1>Todolist</h1>
<label> <input type="text" v-model="newTodo" /> <button @click="addTodo()">添加任务</button> </label>
<h2>待办清单</h2> <ul> <li v-for="todo in todos" :key="todo.id"> <span :class="{ completed: todo.completed }">{{ todo.content }}</span> <input type="checkbox" :checked="todo.completed" @change="handleChange(todo.id)" /> </li> </ul> </div></template>
<style scoped>.completed { text-decoration: line-through;}
.todo-app { width: 200px; height: 200px; margin: 100px auto; text-align: center;}
button { margin-top: 10px;}</style>响应式要解决的问题
响应式指数据变化时,依赖这份数据的代码自动重新执行。
用一句话说:你改 count.value,模板里的 {{ count }} 自己就变了,不需要手动操作 DOM。
Vue 3 的实现由三部分组成:Proxy、依赖收集和触发更新。
track 与 trigger
track(收集依赖):读取数据时,记下这次读取发生在哪个副作用里。组件渲染函数、computed 和 watch 都算副作用。
trigger(触发更新):修改数据时,找出所有依赖这份数据的副作用,让它们重新执行。
两者合起来就是「数据变 → 视图自动更新」。
ref:包装任意值
ref() 接收一个值,返回一个带 .value 属性的响应式对象。
const count = ref(0)
count.value // 0count.value = 1 // 触发更新内部实现是一个 class RefImpl,用 get value() 和 set value() 两个存取器拦截读写:读取时调用 track 收集依赖,写入时调用 trigger 触发更新。
ref 能装的值不限类型。放进去的是对象时,ref 会把它交给 reactive() 做深度转换。
reactive:代理对象与数组
reactive() 接收一个对象,返回它的 Proxy 代理。
const state = reactive({ count: 1 })
state.count++ // 触发更新,不需要 .value代理会拦截 get、set、deleteProperty 等操作:读的时候收集依赖,改和删的时候触发更新。
接受的入参包括普通对象、数组,以及 Map、Set、WeakMap、WeakSet 这些集合类型。
为什么还需要 ref
reactive() 只对对象生效。原始类型不是对象,Proxy 代理不了。
ref 的思路是把值包进一个对象,再用存取器监听这个对象的读写,绕开了这条限制。
reactive 的两个边界
第一,深度是默认的。 reactive() 是深度响应式,访问嵌套对象时会递归把它也转成 Proxy,所以改 state.friend.name 同样能触发更新。
第二,不能整体重新赋值。
let state = reactive({ count: 1 })
state = reactive({ count: 2 }) // 丢失响应式Vue 3 学习笔记
连载中分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
最后更新于 ,距今已过 219 天
部分内容可能已过时