父子组件的v-modle双向数据绑定,ref和$refs,$nextTick,动态组件(component组件),自定义指令,插槽
Published on 2023-01-07 15:35 in 分类: vue2 with 帅气丶汪星人
分类: vue2

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

父子组件的双向数据绑定

我们先完成双向数据绑定,然后完成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双向数据绑定的时候进行修改value和input名称

我们使用model进行名字的修改,修改之后也要修改接收参数的名称,和传递给父组件的事件名称

  model : {
    prop : `name`, //属性名
    event : `dianji` //修改事件名
  }
复制代码
<template>
  <div>
    <div >{{ name }}</div>
    <button @click="input">点击切换黑胡子</button>
  </div>
</template>
<script>
export default {
  name: "model(8)",
  props : {
    name : String
  },
  methods : {
    input () {
      this.$emit(`dianji`,`黑胡子`)
    }
  },
  model : {
    prop : `name`,
    event : `dianji`
  }
}
</script>
复制代码

v-model双向数据绑定的时候,传递给子组件一个对象数据类型如何修改

父组件进行传值

复制代码
<template>
<div>
  <div>
    {{obj.username}}
  </div>
  <duixiang v-model="obj"></duixiang>
</div>
</template>

<script>
import duixiang from "@/components/duixiang";
export default {
  name: "04-v-model进行对象修改",
  components : {
    duixiang
  },
  data() {
    return {
      obj : {
        username : `路飞`
      }
    }
  },
}
</script>
复制代码

子组件接收到value值之后使用点出来的方法进行修改

复制代码
<template>
<div>
  <button @click="info">点击修改黑胡子</button>
</div>
</template>

<script>
export default {
  name: "duixiang",
  props : {
    value : {
      type : Object
    }
  },
  methods : {
    info () {
      // 对对象中数据进行修改
      this.value.username = `黑胡子`
      // 这样对对象中的数值进行修改
      // this.$emit(`input`,{username:`黑胡子`})
    }
  }
}
</script>
复制代码

总结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

v-model通过子组件进行修改传递过来的固定名称

  model : {  //子组件添加model
    prop : `name`, //属性名称
    event : `dianji` //修改事件名
  }

 

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: `路漫漫其修远兮,吾将上下而求索`
}
},
复制代码

 

posted @   帅气丶汪星人  阅读(1152)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示