首页 > 其他分享 >父子组件的v-modle双向数据绑定,ref和$refs,$nextTick,动态组件(component组件),自定义指令,插槽

父子组件的v-modle双向数据绑定,ref和$refs,$nextTick,动态组件(component组件),自定义指令,插槽

时间:2023-01-07 15:48:18浏览次数:48  
标签:nextTick 自定义 dom refs 获取 组件

父子组件的双向数据绑定

我们先完成双向数据绑定,然后完成v-model的双向数据绑定

父组件引入子组件,然后对子组件进行传值,动态显示出来名称

  <model :value=name ></model>
data() {
    return {
      name : `王路飞`
    }
  },

子组件进行接收并且渲染到页面

<div>
  {{ value }}
  <button>点击切换黑胡子</button>
</div>
  props : {
    value : {
      type : String
    }
  },

子组件创建点击事件,暴漏给父组件,父组件完成修改

  {{ value }}
  <button @click="input">点击切换黑胡子</button>
  methods : {
    input () {
      this.$emit(`input`,`黑胡子`)
    }
  }

父组件进行接收并且进行修改

  <model :value=name @input="input"></model>
  methods : {
    input (e) {
      this.name = e
    }
  }

 

v-model进行双向数据绑定

  <model v-model="name"></model>
//删除mester

总结v-model

场景:

1,父组件提供一个数据给子组件使用(父传子)

2,子组件又需要修改父组件传过来的这个数据,所以需要子传父把子组件的值传递给父组件

这种场景可以使用v-model进行简写

定义组件的时候,注意点子组件接收的值为value,子传父触发的事件叫input

v-model是语法糖,用于实现数据的双向绑定,v-model等价于(提供了):value和@input属性

如果自己写的话,需要提供动态属性:value监视动态属性,然后使用@input事件进行修改

v-model添加给组件有什么优势?可以少写父组件中的方法

v-model添加到组件上的时候,提供一个value属性,一个input事件,value用于传值,input用于修改用户数据

v-model组件上只能同时添加一个,添加多个的情况下会报错

传递下去的属性名是value,传递下去的事件名是input

ref和$refs,父子间的传值

ref和$refs可以用于获取dom元素,或者组件的事件

ref是做什么的?

ref添加到组件上获取的是组件实例,组件内部所有的属性和方法都可以任意调用,添加dom上获取的是dom元素

ref的核心作用:获取dom元素或者组件实例,想要获取谁的dom元素就给谁添加ref,然后使用this.$refs进行获取

$refs的作痛是什么?$refs获取dom元素

首先通过ref进行元素绑定,(给需要获取的dom元素或者组件添加ref属性),然后使用$refs获取dom元素(通过this.$refs获取,拿到组件。可以调用组件的方法)

父组件如何获取子组件中的方法?

ref添加到子组件上的时候,拿到的是子组件实例,可以获取到组件内部的所有属性

获取到input表单框dom,然后添加点击选中事件

<div>
  <input type="text" name="" id="" ref="inp">
  <button @click="aa">点击聚焦inpt表单框</button>
</div>

  methods : {
    aa () {
      this.$refs.inp.focus() //vue获取到深层次的dom节点,然后对深层次的节点进行修改
    }
  }

父组件获取到子组件上边的方法

首先在子组件上边创建一个方法并且进行传值

  methods : {
    dian (aa) { //子组件中创建一个点击事件,让父组件进行获取
      alert(aa)
    }
  }

父组件获取子组件事件,然后使用点击事件对子组件进行调用

  <two ref="alert"></two>
  <button @click="fudian">点击获取到子组件</button>
  methods : {
    fudian () {
      this.$refs.alert.dian(`获取到子组件的点击事件`)
    }
  },

$nextTick

$nextTick的作用是什么?

数据更新之后,想要获取到更新之后的最新dom节点的时候,需要用到$nextTick。

场景:当数据更新之后,想要拿到更新之后的最新dom结构的时候,使用到$nextTick

数据更新之后,为什么获取不到最新的dom结果?

因为数据发生变化之后,更新界面的操作并不是立即执行的,而是被注册成一个异步任务(底层也是使用$nextTick进行注册)

(1)数据更新之后,通知vue更新界面(虚拟dom进行对比,更新出真实dom),更新的操作注册成一个异步任务

(2)所以数据更新以后,并不能立即获取最新的dom结构

(3)我们想要获取dom的最新数据,需要使用$nextTick注册一个异步任务,获取最新更新数据

(4)同步代码走完以后,按照顺序清空异步任务,先注册的异步任务会被先清空

(5)更新界面的操作先被注册,会先被清空

(6)更新之后,界面就更新了,执行我们后续注册的$nextTick的时候,获取更新之后的最新结构(可以准确的拿到最新更新的dom结构)

$nextTick是一个微任务,但是我们不能确定他就是一个微任务或者是一个宏任务

我们如何确定$nextTick是一个什么样的任务?

根据宿主的环境进行判断,判断的先后顺序

promise.then(微任务) ===> mutationObserver(微任务) ===> setImmediate(宏任务,node环境支持) ===> setTimeOut(宏任务)

  <div>
    <input type="text" name="" id="" v-if="show" ref="inp">
    <button @click="aa">点击显示表单框,并且获取表单框焦点</button>
  </div>
  data() {
    return {
      show: false
    }
  },
  methods : {
    aa () {
      this.show = true
      this.$refs.inp.focus() //这里会报错,因为v-if还没有添加这个dom节点
    }
  }
}

解决这个问题使用$nextTick

methods : {
    aa () {
      this.show = true
      // this.$refs.inp.focus() //这里会报错,因为v-if还没有添加这个dom节点
      this.$nextTick(() => {
        this.$refs.inp.focus() //这里就不会报错了,因为我们注册了一个异步任务,紧跟着数据变化,当数据发生变化的时候,我们就可以实时获取数据
      })
    } 
  }

直观的获取到我们最新修改的dom数据内容,使用v-model进行双向数据绑定

首先添加v-model进行双向数据绑定,然后获取h1的dom节点,data中声明name属性值

    <input type="text" name="" id="" v-if="show" ref="inp" v-model="name">
    <button @click="aa">点击显示表单框,并且获取表单框焦点</button>
    <h1 ref="shi">{{ name }}</h1>
data() {
return {
show: false,
name : `学而时习之`
}
},
methods : {
aa () {
this.show = true
// this.$refs.inp.focus() //这里会报错,因为v-if还没有添加这个dom节点
this.$nextTick(() => {
this.$refs.inp.focus() //这里就不会报错了,因为我们注册了一个异步任务,紧跟着数据变化,当数据发生变化的时候,我们就可以实时获取数据
})
this.name = `时不我待` //我们给data中的name重新赋一个新值
// console.log(this.$refs.shi.innerHTML) //这里获取的是"学而时习之"并不是最新的"时不我待"
this.$nextTick(()=>{
console.log(this.$refs.shi.innerHTML) //这里获取的是"时不我待",也是dom节点更新的最新数据
})
}
}

动态组件(component组件)

通过动态切换is属性,实现组件的切换【is属性就是要渲染的组件的名称】

通过is展示的组件也要进行注册才可以,使用:is也可以进行动态的数据绑定

父组件进行动态绑定子组件

<div>
<!--  常规引入组件-->
<!--    <Nav></Nav>-->
<!--    <swipet></swipet>-->
<!--  使用新方式引入组件-->
<!--  <component is="Nav"></component>-->
<!--  <component is="swipet"></component>-->
<!--  使用动态方式绑定组件,创建动态组件,然后绑定动态组件-->
  <component :is="aa"></component>
<!--  点击的时候传值给data数据,data数据进行重新渲染-->
<!--  <button @click="aa = `Nav`">点击切换nav组件</button>-->
<!--  <button @click="aa = `swipet`">点击切换swipet组件</button>-->
<!--  把多个组件维护成一个数据,进行动态绑定-->
<button v-for="i in arr" :key="i.id" @click="aa = i.name">{{i.id}}</button>
</div>

data() {
return {
aa: `Nav`,
arr : [ //创建数组来维护动态切换按钮
{ id : `切换Nav`, name : `Nav` },
{ id : `切换swipet`, name : `swipet` },
]
}
},

自定义指令

以前我们使用的指令都是vue帮我们制定好的,现在我们可以写一些自己的自定义指令

当我们对dom元素进行底层操作的时候,我们就要使用到自定义指令了

局部自定义指令的注册方式

(1)局部注册指令

(2)全局注册指令,放在main文件当中

首先我们先自己写出来需求,当我们点击的时候,显示输入框,并且完成聚焦

<div>
<!--  (1)局部注册自定义事件,当我们点击button按钮,显示出来搜索框,并且完成聚焦-->
  <input type="text" name="" id="" v-if="show" ref="inp">
  <button @click="dian">点击事件</button>
</div>
  data() {
    return {
      show: false
    }
  },
  methods : {
    dian () {
      this.show = true
      this.$nextTick(()=>{
        this.$refs.inp.focus()
      })
    }
  }

然后我们写出来自己的自定义指令,进行页面的修改

directives:{},当我们添加vue指令的时候使用

然后我们在我们需要绑定元素的dom上添加我们自定义的名称,写在directives:{}里边,是一个对象的形式

inserted(el){}我们给元素添加aa自定义属性的时候会触发这个钩子,里边的el参数是我们拿到自定义属性,对应的dom元素,然后我们对dom元素进行底层的修改

<!--  (1)局部注册自定义事件,当我们点击button按钮,显示出来搜索框,并且完成聚焦-->
<!--  <input v-aa type="text" name="" id="" v-if="show" ref="inp">-->
  <input v-aa type="text" v-if="show">
  <button @click="dian">点击事件</button>
  data() {
    return {
      show: false
    }
  },
  methods : {
    dian () {
      this.show = true
      // this.$nextTick(()=>{
      //   this.$refs.inp.focus()
      // })
    }
  },
  directives : { //directives(指令)当我们添加新的vue指令的时候使用
    aa : { //我们添加的自定义指令的名称
      inserted (el) { //当我们给元素添加aa的时候就会触发inserted钩子
        console.log(el)//我们拿到的参数是,dom元素,然后我们对dom元素的底层进行修改
        el.focus()
      }
    }
  }

全局自定义指令的注册方式

全局自定义注册指令放在mian中,使用Vue.directive进行全局注册,Vue.directive里面放两个参数,一个是注册事件的名称,一个是注册事件

事件还是使用inserted进行注册事件,获取到对应的dom元素,然后对dom元素进行修改

在min中创建我们的事件名称

Vue.directive(`color`,{
  inserted (bb) { //aa获取的是当前的dom节点
    console.log(bb) //获取到我们想要的饿节点
    bb.style.color = `red`//对我们想要修改的节点进行修改
  }
})

在我们想要调用的组件中,使用v-color进行引用,修改指定的颜色

  <div v-color>全局注册事件</div>

自定义指令的传参,传参以后,让我们可以获取到子组件中data的属性,然后进行传参

inserted(参数1,参数2){} 参数1是当前的dom元素,参数2是dom元素的属性和样式

我们先用局部组件传递一个参数给mian

  <div v-color="color">全局注册事件</div>
  data() {
    return {
      color : `#de1c31` //这里是cc可以获取到的元素属性
    }
  },

然后我们使用mian中的inserted的第二个参数,进行参数接收

Vue.directive(`color`,{
  inserted (bb,cc) { //aa获取的是当前的dom节点
    // console.log(bb) //获取到我们想要的饿节点
    // bb.style.color = `red`//对我们想要修改的节点进行修改
    console.log(cc) //这里我们获取的是,dom的属性和样式
    console.log(cc.value) //我们获取到的颜色样式,赋值给dom的color样式
    bb.style.color = cc.value
  }
})

动态的修改传递过来的参数,我们需要使用新的钩子updata

updata作用:钩子所在的数据,发生了变化的时候就会重新发生变化,首次不会执行,触发以后才会执行

我们在main中创建新的钩子updata

Vue.directive(`color`,{
  // inserted (bb,cc) { //aa获取的是当前的dom节点
  //   // console.log(bb) //获取到我们想要的饿节点
  //   // bb.style.color = `red`//对我们想要修改的节点进行修改
  //   // console.log(cc) //这里我们获取的是,dom的属性和样式
  //   // console.log(cc.value) //我们获取到的颜色样式,赋值给dom的color样式
  //   bb.style.color = cc.value
  // },
  update(dd,ee){
    console.log(dd) //获取的是当前的元素dom
    console.log(ee) //获取的是当前的元素dom上的属性和样式
    console.log(ee.value) //获取到点击以后的新的样式属性
    dd.style.color = ee.value
  }
})

然后我们在自定义指令中添加点击事件,重新给变量color进行赋值,完成点击切换颜色

  <div v-color="color">全局注册事件</div>
  <button @click="color = `pink`">点击修改颜色</button>

插槽(Slot)

插槽的作用是什么?

封装一个组件的时候,如果组件的内部部分结构式不确定的,使用插槽占位

具体渲染的内容,调用组件的时候确定(组件标签中间的内容就是组件内部slot的地方渲染的内容)

slot放在子组件内部,需要插入div的地方,然后父组件对子组件进行插槽

首先我们在子组件中创建一个插槽

<div class="box">
  <div class="header">{{title}}</div>
  <div class="content">
    <slot></slot>
  </div>
  <div class="footer">
    <button>确定</button>
    <button>取消</button>
  </div>
</div>
props :{
title: String
}
.box {
margin: 10px;
height: 300px;
border: 1px solid red;
border-radius: 8px;
overflow: hidden;
}
.header {
height: 50px;
background: #0ac2f3;
text-align: center;
line-height: 50px;
}
.content {
height: 220px;
background: #9b9b9b;
text-align: center;
line-height: 220px;
}
.footer {
height: 30px;
background: #0ac2f3;
text-align: center;
line-height: 30px;
}
.footer button {
margin-left: 5px;
margin-right: 5px;
}

父组件对子组件插槽进行使用

  <aa title="时不我待">
    <p style="margin: 0;  ">学而时习之</p>
  </aa>
  <aa>
    <div>
      {{ msg }}
    </div>
  </aa>
components : {
aa
},
data() {
return {
msg: `路漫漫其修远兮,吾将上下而求索`
}
},

 

标签:nextTick,自定义,dom,refs,获取,组件
From: https://www.cnblogs.com/hgng/p/17031682.html

相关文章