欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > vue组件($refs对象,动态组件,插槽,自定义指令)

vue组件($refs对象,动态组件,插槽,自定义指令)

2024/10/25 11:19:21 来源:https://blog.csdn.net/2301_79568779/article/details/142366771  浏览:    关键词:vue组件($refs对象,动态组件,插槽,自定义指令)

一、ref

1.ref引用

每个vue组件实例上,都包含一个$refs对象,里面存储着对应dom元素或组件的引用。默认情况下,组件的$refs指向一个空对象。

2.使用ref获取dom元素的引用
<template><h3 ref="myh3">ref组件</h3><button @click="getRef">getRef</button>
</template>
<script>
export default{methods:{getRef(){console.log('this',this.$refs);this.$refs.myh3.style.color='red'}}
}
</script>
3.使用ref引用组件实例
<template><MyIndex ref="myIndexRef"></MyIndex><button @click="getRef">getRef</button>
</template>
<script>
import MyIndex from './index copy.vue';
export default{components:{MyIndex},methods:{getRef(){console.log('this',this.$refs);this.$refs.myIndexRef.reset()}}
}
</script><template><div>{{count}}<button @click="this.count+=1">按钮</button></div>
</template>
<script>
export default{data(){return{count:1}},methods:{reset(){this.count=0}}
}
</script>
4.控制文本框和按钮的按需切换
<template><input type="text" v-if="inputVisible" ref="ipt"><button v-else @click="showInput">getRef</button>
</template>
<script>
export default{data(){return{inputVisible:false}},methods:{showInput(){this.inputVisible=true;//获取文本框的引用对象,访问的ipt是undefind,dom更新是异步的,拿不到最新domthis.$nextTick(()=>{this.$refs.ipt.focus();})}}
}
</script>
5.组件是异步执行dom更新的
6.this.$nextTick(cb)

组件的$nextTick(cb)方法,会把cb回调推迟到下一个dom更新周期之后执行。简单理解:等组件的dom异步地重新渲染完成后,再执行cb回调函数。从而能保证cb回调函数可以操作到最新的dom元素。

二、动态组件

1.什么是动态组件

动态组件时动态切换组建的显示与隐藏。vue提供了一个<component>组件,专门用来实现组件的动态渲染。

a.<component>是组件的占位符

b.通过is属性动态指定要渲染的组件名称

c.<component is="要渲染的组件名称"></component>

2.如何实现动态组件渲染
<template><button @click="comName='Home'">首页</button><button @click="comName='Movie'">电影</button><hr><component :is='comName'></component>
</template>
<script>
import Home from './home.vue'
import Movie from './movie.vue'
export default{components:{Home,Movie},data(){return{comName:'Home'}}
}
</script>
3.使用keep-alive保持组件的状态

默认情况下,动态切换组件时无法保持组件的状态。可以使用vue内置的<keep-alive>组件保持动态组件的状态。

<template><button @click="comName='Home'">首页</button><button @click="comName='Movie'">电影</button><hr><keep-alive><component :is='comName'></component></keep-alive>
</template>
<script>
import Home from './home.vue'
import Movie from './movie.vue'
export default{components:{Home,Movie},data(){return{comName:'Home'}}
}
</script><template><div>Home组件{{ count }}<button @click="count+=1">+1</button></div>
</template>
<script>
export default{data(){return{count:1}},created(){console.log('created');},unmounted(){console.log('unmounted');},
}
</script>

三、插槽

1.什么是插槽

插槽是vue组件为组件的封装者提供的能力。允许开发者在封装组件时,把不确定、希望由用户指定的部分定义为插槽。

可以把插槽认为是组件封装期间,为用户预留的内容占位符。

没有预留插槽的内容会被丢弃

<template><Home><p>-------------</p></Home>
</template>
<script>
import Home from './home.vue'
export default{components:{Home},
}
</script><template><div>Home组件<p>p1p1p1p1p1p1p1p1p1p1p1p1p1</p><slot></slot><p>p2p2p2p2p2p2p2p2p2p2p2p2p2</p></div>
</template>
2.后备内容(默认内容)

封装组件时,可以为预留的<slot>插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。

<template><Home><!-- <p>-------------</p> --></Home>
</template>
<script>
import Home from './home.vue'
export default{components:{Home},
}
</script><template><div>Home组件<p>p1p1p1p1p1p1p1p1p1p1p1p1p1</p><slot>默认xxxxxxxx</slot><p>p2p2p2p2p2p2p2p2p2p2p2p2p2</p></div>
</template>
3.具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的name名称。这种带有具体名称的插槽叫做“具名插槽”。

v-slot:可以简写为字符#

<template><Home><!-- v-slot:可简写为# --><template v-slot:header>头部</template><p>-------------</p><template #footer>底部</template></Home>
</template>
<script>
import Home from './home.vue'
export default{components:{Home},
}
</script><template><div>Home组件<slot name="header"></slot><slot>默认xxxxxxxx</slot><slot name="footer"></slot></div>
</template>
4.作用域插槽

在封装组件的过程中,可以为预留的<slot>插槽绑定props数据,这种带有props数据的<slot>插槽叫做作用域插槽。

<template><Home><template v-slot:default="scope">{{ scope }}{{ scope.info.name }}</template></Home>
</template>
<script>
import Home from './home.vue'
export default{components:{Home},
}
</script><template><div>Home组件<slot :info="infomation" :msg="msg"></slot></div>
</template>
<script>
export default{data(){return{infomation:{name:'zs',age:18},msg:'123'}}
}
</script>

解构作用域插槽

<template><Home><template #default="{info,msg}">{{ info.name }}{{ info.age }}{{ msg }}</template></Home>
</template>
<script>
import Home from './home.vue'
export default{components:{Home},
}
</script>

作用域插槽的使用场景

<template><Home><template #default="{user}"><td>{{user.name}}</td><td>{{ user.age }}</td><td><input type="checkbox" :checked="user.state"></td></template></Home>
</template>
<script>
import Home from './home.vue'
export default{components:{Home},
}
</script><template><div>Home组件<table><tr><td>名字</td><td>年龄</td><td>状态</td></tr><tr v-for="item in list" :key="item"><slot :user="item"></slot></tr></table></div>
</template>
<script>
export default{data(){return{list:[{name:'zs1',age:18,state:true},{name:'zs2',age:17,state:false},{name:'zs3',age:15,state:true}],}}
}
</script> 

四、自定义指令

vue 中自定义指令分为两类:私有自定义指令,全局自定义指令。

在每个vue组件中,可以在directives节点下声明私有自定义指令。

1.私有自定义指令
<template><input type="text" v-focus>
</template>
<script>
export default{directives:{focus:{//当被绑定的元素插入到dom中时,会自定触发mounted函数mounted(el){el.focus()//让被绑定的元素自动获取焦点}}}
}
</script>
2.全局自定义指令

在main.js文件中声明自定义指令

const app = createApp(App)
app.directive('focus',{mounted(el){el.focus()}
})
3.updated函数

mounted函数只在元素第一次插入dom时被调用,当dom更新时mounted函数不会被触发。updated函数会在每次dom更新后被调用。

<template><input type="text" v-focus>{{ count }}<button @click="count+=1">+1</button>
</template>
<script>
export default{data(){return{count:1}}
}
</script>//main.jsmain.js
const app = createApp(App)
app.directive('focus',{mounted(el){el.focus()},updated(el){el.focus()}
})

在vue2的项目中使用自定义指令时,mounted->bind,updated->update

如果mounted,updated函数中的逻辑完全相同,可以简写

const app = createApp(App)
app.directive('focus',(el)=>{el.focus()
})

4.指令的参数值

在绑定指令时,可以通过等号的形式为指令绑定具体的参数值。binding.value

<template><input type="text" v-model.number="count" v-focus v-color="'red'"><p v-color="'green'">{{ count }}</p><button @click="count+=1">+1</button>
</template>
<script>
import Home from './home.vue'
export default{components:{Home},data(){return{count:1}}
}
</script>//main.js
const app = createApp(App)
app.directive('focus',(el)=>{el.focus()
})
app.directive('color',(el,binding)=>{el.style.color=binding.value
})

五、Table案例

1.搭建项目基本结构
npm create vite
cd vite-project
npm install
2.请求商品列表数据

npm i axios

//main.js
import axios from 'axios'
//配置请求的根路径
axios.defaults.baseURL='http://localhost:3000'
//将axios挂载为全局的$http自定义属性
app.config.globalProperties.$http=axios
3.封装MyTable组件

a.通过props属性,为MyTable.vue组件指定数据源

b.在MyTable.vue组件中,预留header的具名插槽

c.在MyTable.vue组件中,预留body的作用域插槽

<template><table><thead><slot name="header"></slot></thead><tbody><tr v-for="(item,index) in data" :key="item"><slot name="body" :row="item" :index="index"></slot></tr></tbody></table>
</template>
<script>
export default{props:{data:{type:Array,default:[],require:true}}
}
</script>
4.实现删除功能
5.实现添加标签的功能

a.实现input和button的按需展示(v-if,v-else)

b.让input自动获取焦点(自定义指令)

c.文本框失去焦点自动隐藏

d.文本框的enter,esc按键事件

把用户在文本框中输入的值,预先转存到常量val中,清空文本的值,v-if='false'隐藏文本框

判断val的值是否为空,如果为空,则不进行添加,

判断val的值是否已经存在于数组中,如果存在,则不进行添加,return

将用户输入的内容,作为新标签push到当前tag数组中

<template><MyTable :data="goodsList"><template #header><td>#</td><td>商品名称</td><td>价格</td><td>标签</td><td>操作</td></template>     <template #body="{row,index}"><td>{{index+1}}</td><td>{{ row.title }}</td><td>¥{{ row.price }}</td><td><inputv-if="row.inputVisible"ref="InputRef"v-model.trim="row.inputValue"class="ml-1 w-20"@keyup.enter="handleInputConfirm(row)"@blur="handleInputConfirm(row)"@keyup.esc="row.inputValue=''"v-focus/><el-button v-else class="button-new-tag ml-1" size="small" @click="row.inputVisible = true">+Tag</el-button><el-tag class="ml-2" type="success" v-for="item in row.tag" :key="item">{{ item }}</el-tag></td><td><el-button type="danger" @click="deleteFn(row.id)">删除</el-button></td></template></MyTable>
</template>
<script>
import MyTable from './MyTable.vue'
export default{components:{MyTable},data(){return{goodsList:[]}},methods:{async getGoodsList(){const {data:res}=await this.$http.get('/goodsList')console.log('res',res);this.goodsList = res.map(item=>{return{...item,inputVisible:false,inputValue:''}})},deleteFn(id){this.goodsList = this.goodsList.filter(item=>item.id!==id)},handleInputConfirm(row){const val = row.inputValue;row.inputValue = '';row.inputVisible = false;if(!val||row.tag.indexOf(val) !==-1)returnrow.tag.push(val)},},created(){this.getGoodsList()},directives:{focus(el){el.focus()}}
}
</script>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com