nextTick()
是Vue.js中一个非常重要的方法,它的作用是延迟到下一个DOM更新周期后再执行,执行nextTick
里的回调函数。在修改数据后,你立即使用这个方法,获取更新后的DOM。
当你在Vue组件中更改数据时,视图不会立刻更新,而是等到本轮事件循环结束时进行批量更新。如果你需要对更新后的视图做出操作的话,nextTick
回调将会是你的绝佳方案。
打个比方,你放学回家后想吃鸡腿,在自己脑中写下了吃鸡腿的代码,但是妈妈还没将鸡腿蒸熟,如果你不使用nextTick
回调吃鸡腿的代码,那么你就没有鸡腿可吃了,因为等鸡腿蒸熟后,你吃鸡腿的代码早已经跑过一遍了,大脑默认为你已经吃过了。👉🤣
下面再给出我在项目中碰到的问题:
- 问题描述:我要实现一个类似
markdown
编辑器的效果,当从preview
切换到write
视图后,会自动聚焦到输入框中,如下图所示。
但是我尝试在点击write后执行我的toggleWriteBox函数,并不会自动聚焦至textarea中
………………
const writeBoxRef = ref(null)
function toggleWriteBox() {
showWriteBox.value = true
showPreviewBox.value = false
if (writeBoxRef.value) {
writeBoxRef.value.focus()
}
}
………………
<textarea ref="writeBoxRef">
这是因为在点击write时,在DOM还未更新就执行了focus的代码,浏览器没有地方focus,于是使用nextTick()更改代码如下:
function toggleWriteBox() {
showWriteBox.value = true
showPreviewBox.value = false
nextTick(() => {
if (writeBoxRef.value) {
writeBoxRef.value.focus()
}
})
}
随后,需求就完成了。
还有一个小细节,就是在浏览器更新时也应该自动聚焦,我们使用onMounted函数实现自动聚焦
onMounted(() => {
if (showWriteBox.value) {
writeBoxRef.value.focus()
}
})
全部代码如下所示,可以自己试着玩玩(写的稍显笨拙,见谅):
<script setup>
import { marked } from 'marked'
import { ref, onMounted, nextTick } from 'vue'
const showWriteBox = ref(true)
const showPreviewBox = ref(false)
const writeBoxRef = ref(null)
function toggleWriteBox() {
showWriteBox.value = true
showPreviewBox.value = false
nextTick(() => {
if (writeBoxRef.value) {
writeBoxRef.value.focus()
}
})
}
function togglePreviewBox() {
showWriteBox.value = false
showPreviewBox.value = true
}
onMounted(() => {
if (showWriteBox.value) {
writeBoxRef.value.focus()
}
})
</script>
<template>
<div class="container">
<div class="header">
<div class="ll-box"></div>
<div class="left-box" :class="{ 'selected-box': showWriteBox, 'unselected-box': showPreviewBox }">
<button class="left-button" @click="toggleWriteBox"
:class="{ 'selected-font': showPreviewBox }">write</button>
</div>
<div class="right-box" :class="{ 'selected-box': showPreviewBox, 'unselected-box': showWriteBox }">
<button class="right-button" @click="togglePreviewBox"
:class="{ 'selected-font': showWriteBox }">preview</button>
</div>
<div class="rr-box"></div>
</div>
<div class="main">
<textarea ref="writeBoxRef" v-if="showWriteBox" placeholder="Leave a comment" class="google-font write-box"
cols="90" rows="20"></textarea>
<div v-if="showPreviewBox" class="preview-box">preview-box</div>
</div>
<div class="footer google-font">Remember, contributions to this repository should follow our <a href="#">Github
Community Guidelines.</a></div>
</div>
</template>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: space-between;
background-color: #ffffff;
border: 1px solid #d0d7de;
border-radius: 6px;
padding: 1rem;
margin: 1rem;
/* gap: 1rem; */
}
.header {
width: 100%;
height: 50px;
display: flex;
padding-bottom: 1rem;
}
.left-box {
flex: 20%;
background-color: #ffffff;
}
.right-box {
flex: 20%;
background-color: #ffffff;
}
.selected-font {
color: #999999;
}
.selected-box {
border: 1px solid #d0d7de;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom: none;
}
.unselected-box {
border-bottom: 1px solid #d0d7de;
}
.write-box {
outline: none;
border: 1px solid #d0d7de;
}
.write-box:focus {
border: 3px solid #218bff;
border-radius: 6px;
}
.write-box:not(:focus) {
background-color: #f6f8fa;
}
.preview-box {
width: 500px;
height: 200px;
background-color: #fff;
}
.ll-box {
flex: 2%;
border-bottom: 1px solid #d0d7de;
}
.rr-box {
flex: 60%;
border-bottom: 1px solid #d0d7de;
}
.footer {
font-size: small;
}
.google-font {
font-family: Roboto, sans-serif;
}
button {
border: none;
background-color: transparent;
outline: none;
}
button:hover {
color: black;
}</style>
``