Vue的视图更新,是同步的还是异步的
答案是异步的
如何更新是异步的,那么如何快速拿到dom里面的值呢?
nextTick
那么为什么是异步的?
下面的代码可以当成是vue的响应式更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button id="btn">count++</button>
<div id="el"></div>
</div>
<script>
const el = document.querySelector("#el")
const btn = document.querySelector('#btn')
function effect(fn) {
//保存当时的effect
activeEffect = fn
fn()
//执行完之后清空
activeEffect = null
}
//当前正在执行的effect
let activeEffect = null
//保存依赖
const set = new Set()
//响应式对象
const count = {
_value: 0,
get value() {
//收集依赖
activeEffect && set.add(activeEffect)
return this._value
},
set value(val) {
this._value = val
//触发更新
set.forEach((cb)=>cb())
}
}
effect(()=>{
console.log('effect')
el.innerText = count.value
})
btn.addEventListener('click',()=>{
count.value++
})
</script>
</body>
</html>
当点击按钮时重复更新counts数据
btn.addEventListener('click',()=>{
count.value++
count.value++
})
点击按钮时,直接加2了
如图:
image.png
由于是同步的其中effect执行了两次,点击按钮直接加2了,那么effect中+1的更新就没有必要,如何不触发呢
新增任务队列
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button id="btn">count++</button>
<div id="el"></div>
</div>
<script>
const el = document.querySelector("#el")
const btn = document.querySelector('#btn')
function effect(fn) {
//保存当时的effect
activeEffect = fn
fn()
//执行完之后清空
activeEffect = null
}
//当前正在执行的effect
let activeEffect = null
//保存依赖
const set = new Set()
//
const tasks = new Set()
function runTasks() {
//因为是异步更新
Promise.resolve().then(()=>{
tasks.forEach((task)=>task())
tasks.clear()
})
}
//响应式对象
const count = {
_value: 0,
get value() {
//收集依赖
activeEffect && set.add(activeEffect)
return this._value
},
set value(val) {
this._value = val
//触发更新
// set.forEach((cb)=>cb())
//在触发更新这里先不触发,把这次更新放入tasks中
set.forEach((cb)=>tasks.add(cb))
runTasks()
}
}
effect(()=>{
console.log('effect')
el.innerText = count.value
})
btn.addEventListener('click',()=>{
count.value++
count.value++
console.log(el.innerText)
})
</script>
</body>
</html>
点击按钮,effect只执行了一次
image.png
但是打印出来的el.innerText拿不到即时的值,那么就需要nextTick了,nextTick是如何实现的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button id="btn">count++</button>
<div id="el"></div>
</div>
<script>
const el = document.querySelector("#el")
const btn = document.querySelector('#btn')
function effect(fn) {
//保存当时的effect
activeEffect = fn
fn()
//执行完之后清空
activeEffect = null
}
//当前正在执行的effect
let activeEffect = null
//保存依赖
const set = new Set()
//
const tasks = new Set()
function runTasks() {
//因为是异步更新
Promise.resolve().then(()=>{
tasks.forEach((task)=>task())
tasks.clear()
})
}
//响应式对象
const count = {
_value: 0,
get value() {
//收集依赖
activeEffect && set.add(activeEffect)
return this._value
},
set value(val) {
this._value = val
//触发更新
// set.forEach((cb)=>cb())
//在触发更新这里先不触发,把这次更新放入tasks中
set.forEach((cb)=>tasks.add(cb))
runTasks()
}
}
effect(()=>{
console.log('effect')
el.innerText = count.value
})
function nextTick(cb) {
Promise.resolve().then(cb)
}
btn.addEventListener('click',()=>{
count.value++
count.value++
nextTick(()=>{
console.log(el.innerText)
})
})
</script>
</body>
</html>
如图,可以拿到正确的值
image.png
从上面可得,vue为什么要异步更新