响应式,就是 script 中值变化之后,页面能自动更新。
<!-- 页面初始渲染内容为:“姓名:张伟,等级:3”,这也是与下面 script 中的内容对应的。 -->
<!-- 1秒钟后,因为登录时间够了,用户等级变了,现在内容为:“姓名:张伟,等级:4” -->
<div> 姓名:{{ name }} ,等级:{{ level }}</div>
<script>
let name = '张伟';
let level = 3;
setTimeout(()=>{
// 1秒后执行下面的逻辑,比如模拟用户登录时间够了,更新等级。
level = 4;
},1000);
</script>
上面的代码就是一个响应式的体现,当修改了 level 的值后,页面内容也跟着实时更新了,而不需要我们做其它操作。如果是原生的话,那会如何呢?
<div>
姓名:<span class="name">{{ name }}</span>,
等级:<span class="level">{{ level }}</span>
</div>
<script>
let name = '张伟';
let level = 3;
setTimeout(()=>{
// 1秒后执行下面的逻辑,比如模拟用户登录时间够了,更新等级。
level = 4;
// 第1步,先获取等级元素
const levelEle = document.querySelector('span.level');
// 第2步,更新值
levelEle.innerText = level;
},1000);
</script>
这就是非响应式,当一个变量 level 变化之后,如果要想页面也跟着更新,必须手动操作 dom 完成同步。而且为了能选中对应的内容,我们的 HTML 也要有相应的变化,增加了复杂度。当页面复杂后,操作的 dom 也会变多,这时候极容易出 bug。
本节就是学习 Vue 的响应式基本用法。
声明响应式状态
大家应该对 ref 包装后的对象取值要带“.value”感觉疑惑,文档中有说明,你可以理解为对象才有 getter/setter 方法,而 Vue 是利用这2个方法做监听实现的。基本类型,比如数字 0,它就是一个值,没有相应的 getter/setter 方法。为了让 0 响应式,就把这包装成一个对象了。这个对象大概这样:{ value: 0 }。
作者在知乎也有专门的讨论,感兴趣的可以看看。地址:www.zhihu.com/question/49…
状态的更新是异步的,对某些利用 dom 的第三方库,在使用时,要放在 nextTick()中。
// 假设 name 的原值为:张三
name = '李四'
// 获取 name 的值,预期是“李四”,但获取到的是旧值“张三”。这就是异步更新导致的。
document.querySelector('.name').textContent
nextTick(()=>{
// 在 nextTick 中获取,这时候就获取到“李四”了,你可以把 nextTick 看成是 Vue 更新完页面后再执行的方法。
document.querySelector('.name').textContent
})
reactive()
官方代码:
const proxy = reactive({})
const raw = {}
// 将一个普通对象 raw 赋值给一个响应式对象的属性时,
// Vue 会将 raw 对象进行代理,并返回一个代理对象,而不是直接返回原始对象。
proxy.nested = raw
console.log(proxy.nested === raw) // false
额外的 ref 解包细节
const count = ref(0)
const state = reactive({
count
})
// 因为 state 是被 reactive 包装后的对象,所以 state.count 这里不需要再添加 ".value"
// 你可以试试去掉 reactive
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1