$refs、$nextTick以及v-model的原理

228 阅读1分钟

1.$refs 的使用

  • 父组件使用 this.$refs.父组件ref名称,获取原生 DOM 对象
  • 父组件可以通过 this.$refs.子组件ref名称,获取到子组件对象,也可以访问子组件对象身上的属性和方法

App.vue

<template>
  <div>
    <h1>1.$refs是使用</h1>
    <!-- 给自己标签添加 ref 属性 -->
    <p ref="pRef" id="cuteP">我是可爱的p</p>
    <!-- 给子组件添加 ref 属性 -->
    <Demo ref="demoRef"></Demo>
  </div>
</template>

<script>
// 引入子组件
import Demo from "@/components/Demo.vue"
export default {
  // 注册子组件    
  components: {
    Demo
  },
  mounted(){
    const p=document.getElementById('cuteP')
    console.log(p,111)
    console.log(this.$refs.pRef,222)
    console.log(this.$refs.demoRef.msg,333)
    this.$refs.demoRef.fn()
    console.log(this.$refs.demoRef,444)
  }
}
</script>

src/components/Demo.vue(子组件)

<template>
  <div>我是 Demo 子组件</div>
</template>

<script>
export default {
  data() {
    return {
      msg: "我是 Demo 子组件中的 msg"
    }
  },

  mounted() {
    console.log(this,'我是 Demo 子组件中的 this')
  },

  methods: {
    fn() {
      console.log("我是 Demo 子组件中的 fn 方法")
    }
  }
}
</script>

image.png

2. $nextTick 的使用

App.vue

<template>
  <div>
    <h1>2. $nextTick 的使用</h1>
    <h4 ref="h4Ref">{{ msg }}</h4>
    <button @click="changeMsg">点我修改 h4 的内容</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "我是一个小小的 h4"
    }
  },
  methods: {
    changeMsg() {
      this.msg = "我是一个大大的 h4"
      console.log(this.msg, 111)
      console.log(this.$refs.h4Ref.innerHTML, 222)
      this.$nextTick(() => {
        console.log(this.$refs.h4Ref.innerHTML, 333)
      })
    }
  },
  updated() {
    console.log(this.$refs.h4Ref.innerHTML,444)
  }
}
</script>

image.png

  • 打印111,因为数据修改后更新 DOM 是异步操作(数据修改了)
  • 打印222,DOM 还未更新
  • 打印333,等待下一次 DOM 更新完毕后执行(this.$nextTick()
  • 打印444,会让业务逻辑产生割裂, 可读性不高

3. $nextTick 的应用场景

需求: 点击按钮, 让文本框显示并获取焦点 App.vue

<template>
  <div>
    <h1>3. $nextTick 的应用场景</h1>
    <input ref="inpRef" v-if="isShow" type="text">
    <button v-else @click="showSearch">点我显示搜索框</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
   isShow: false,
    }
  },
  methods: {
      async showSearch() {
      this.isShow = true
      //◆法1
      this.$nextTick(() => {
        this.$refs.inpRef.focus()
      })

      console.log(this.$nextTick())//Promise
      //◆法2
      // this.$nextTick().then(() => {
      //   this.$refs.inpRef.focus()
      // })
      
      //◆法3
      // await this.$nextTick()
      // this.$refs.inpRef.focus()
    }
  }
}
</script>

4、v-model的原理

  • 1.父向子传值,子组件收到值后,使用插值表达式渲染在页面上
  • 2.当子组件中的按钮被点击,触发点击事件,触发父组件的 input 事件,传递最新的值给父组件
  • 3.父组件收到最新的值后,重新给 count 赋值,count 修改了,传递给子组件的 value 也会更新

App.vue

<template>
  <div>
     <h1>4. v-model 的本质</h1>
    <AddBtn :value="count" @input="(val) => count = val"></AddBtn>
  </div>
</template>

<script>
//引入子组件
import AddBtn from '@/components/AddBtn.vue'
export default {
//注册子组件
  components:{
    AddBtn
  },
  data() {
    return {
      count: 10
    }
  }
}
</script>

src/components/AddBtn.vue(子组件)

<template>
  <div>
      <p>子组件库存: {{ value }}</p>
      <button @click="addFn">增加+1</button>
  </div>
</template>

<script>

export default {
  props: {
    value: {
      type: Number,
      default: 0
    }
  },
  methods: {
    addFn () {
      this.$emit('input', this.value + 1)
    }
  }
}
</script>

总结:

  • v-model 本质上是一个语法糖
  • 给元素绑定 value 属性
  • 给元素绑定 input 事件,在事件处理函数中修改绑定的数据