父子组件的双向数据绑定
我们先完成双向数据绑定,然后完成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