一.组件及组件化

1.组件化的作用

由于之前的代码全写在一个App.vue这个文件里面,会到导致一个文件代码过于多而且不易复用,所以有组件化的思想。

2.组件的使用

①创建

创建一个.vue文件,使用setup的简写方式会自动导出.vue文件

②导入
import 组件对象 from '相对路径'// eg
import MyPanel from './components/MyPanel.vue'
③使用

把组件当做⾃定义标签使⽤(单双标签均可)

<组件名></组件名>
<组件名 />// eg
<!-- ⼤驼峰标 双标签 -->
<MyPanel></MyPanel>
<!-- ⼤驼峰 ⾃闭合的单标签 -->
<MyPanel />
<!-- 烤串法 双标签 -->
<my-panel></my-panel>
<!-- 烤串法 ⾃闭合的单标签 -->
<my-panel />

3.组件的全局注册

①特点

全局注册的组件,在项⽬的任何组件中都能使⽤

②步骤

1. 创建.vue组件(三个组成部分)

2. main.js 中进⾏全局注册

③使用方式

当成HTML标签直接使⽤:

• 双标签: <组件名></组件名>

• ⾃闭合的单标签: <组件名 />

④在main.js中注册的写法
import MyPanel from './components/MyPanel.vue'
const app = createApp(App)
// 注册全局组件
// app.component('组件名', 组件对象)eg:
// ⼤驼峰组件名
app.component('MyPanel', MyPanel)
// 烤串法组件名
app.component('my-panel', MyPanel)

二.组件生命周期

1.生命周期的介绍

组件的生命周期的四个阶段:

1. 创建阶段:创建响应式数据

2. 挂载阶段:渲染模板
3. 更新阶段:修改数据,更新视图

4. 卸载阶段:卸载组件

2.组件生命周期的钩子

介绍:

       每个 Vue 组件实例在创建时都需要经历⼀系列的初始化步骤,⽐如设置好数据监听,编译模板,挂载实例到真实 DOM 树上,以及在数据改变时更新 DOM。在此过程中,会 ⾃动运⾏⼀些函数,这些函数被称为【Vue⽣命周期钩⼦】
钩子的本质就是函数,只不过这些函数比较特殊,无需程序员调用,而是有vue3内部的执行机制自己调用。钩子函数存在的意义就是给了程序员在特定的时机添加自己的代码的机会,比如组件创建完毕了,就想发送ajax请求,那么可以在创建后的钩子函数中编写相关代码;还比如页面渲染完毕后,立刻让输入框自动聚焦,那么可以在挂载后的钩子函数中编写相关代码

生命周期的八个钩子:

分别是:
1. 创建阶段: 1. beforeCreate (): 创建前 2. created (): 创建后
2. 挂载阶段: 1. beforeMount (): 挂载前 2. mounted (): 挂载后
3. 更新阶段: 1. beforeUpdate (): 更新前 2. updated (): 更新后
4. 卸载阶段: 1. beforeUnmount (): 卸载前 2. unmounted (): 卸载后 

vue生命周期与钩子的关系图:

3.选项式API生命周期的钩子写法(vue2的写法)

代码示例:

App.vue 文件代码

<script setup>
import LifeCycle from './components/LifeCycle.vue'
import { ref } from 'vue'
const isLive = ref(true)
</script>
<template><LifeCycle v-if="isLive" />
</template>
<style scoped></style>

LifeCycle.vue 文件代码

<script>
export default {// 提供响应式数据data() {return {count: 0,}},// 提供⽅法/函数methods: {fn() {console.log('fn 函数执⾏了')},},setup() {console.log('0-setup')},// 创建阶段(第⼀阶段):Vue组件创建/出⽣阶段:// 创建前:此时⽆法访问 data 数据,也⽆法调⽤ methods ⽅法beforeCreate() {console.log('1-beforeCreate')// console.log(this.count) // undefined// console.log(this.fn) // undefined},// 创建后:此时可以访问 data 数据,也可以调⽤ methods ⽅法created() {console.log('2-created')// console.log(this.count) // 0// console.log(this.fn)// 访问到函数// this.fn()// 开启定时器// 给当前组件实例新增了⼀个 timerId 属性,保存了当前定时器的 id 值// this.timerId = setInterval(() => {//     console.log(this.count)//     }, 1000)// },},// 挂载阶段(第⼆阶段):模版渲染阶段// 挂载前:此时写在 template 下的标签还没有变成真实DOM,故⽽⽆法获取DOMbeforeMount() {console.log('3-beforeMount')// console.log(document.querySelector('p')) // null},// 挂载后:此时写在 template 下的标签已经变成了真实DOM,故⽽可以获取DOM(是最早可以操作DOM的时机)mounted() {console.log('4-mounted')// console.log(document.querySelector('p')) // <p>0</p>// document.querySelector('p').style.color = 'red'},// 更新阶段(第三阶段):数据变了,组件重新渲染的过程// 更新前beforeUpdate() {console.log('5-beforeUpdate')// console.log(this.count)// console.log(document.querySelector('p').innerText) // 旧内容(以前的内容)},// 更新后updated() {console.log('6-updated')// console.log(this.count)// console.log(document.querySelector('p').innerText) // 新内容},// 卸载阶段(第四阶段):组件移除阶段beforeUnmount() {console.log('7-beforeUnmount')},unmounted() {console.log('8-mounted')//关闭定时器// clearInterval(this.timerId)},
}
</script>
<template><div><p>{{ count }}</p><button @click="count++">+1</button></div>
</template><style scoped></style>

比较实用的应该就只有:created,mounted,unmounted 这三个钩子。

4.组合式API生命周期钩子

创建阶段挂载阶段更新阶段销毁阶段
Vue2beforeCreate createdbeforeMount mountedbeforeUpdate updatedbeforeUnmount unmounted
Vue3setup (网络请求)onBeforeMount onMounted (操作 DOM)onBeforeUpdate onUpdatedonBeforeUnmount onUnmounted (清理工作)
代码示例:

App.vue 文件代码

<script setup>
import LifeCycle from './components/LifeCycle.vue'
import { ref } from 'vue'
const isLive = ref(true)
</script>
<template><LifeCycle v-if="isLive" />
</template>
<style scoped></style>

LifeCycle.vue 文件代码

<script setup>
import { onMounted, ref } from 'vue'
onMounted(() => {console.log('渲染之后')document.querySelector('p').style.color = 'green'
})
let count = ref(0)
</script><template><div><p>{{ count }}</p><button @click="count++">+1</button></div>
</template><style scoped></style>

三.scoped

1.scoped作用

写在组件中的样式会 全局⽣效, 因此很容易造成多个组件之间的样式冲突问题。

1. 全局样式: 默认组件中的样式会作⽤到全局,任何⼀个组件中都会受到此样式的影响

2. 局部样式: 可以给组件加上scoped 属性,可以让样式只作⽤于当前组件的标签

2.scoped原理

1. 组件内所有标签都被添加data-v-hash值 的⾃定义属性

2. css选择器都被添加 [data-v-hash值] 的属性选择器

最终效果: 必须是当前组件的元素, 才会有这个⾃定义属性, 从⽽保证了样式只能作⽤到当前组件。

四.组件通信

1.介绍

作用:

之前把代码写在⼀起的,数据直接使⽤即可;现在是组件化开发,通过代码拆分和组合的⽅式进⾏开发,这种情况下,还要达到和不拆分之前⼀样的效果,这时组件之间难免需要数据传递,这就需要组件之间进⾏通信。

组件之间的关系:

1、⽗⼦关系:谁被使⽤, 谁就是⼦组件, 当前组件就是⽗组件

2、⾮⽗⼦关系

2.父传子(defineProps)

传数据步骤:

1. ⼦组件通过 defineProps 接收数据(⼦接)

2. ⽗组件通过 ⾃定义属性 传递数据 (⽗传)

流程图:

代码案例:

App.vue 文件代码

<script setup>
import MyGoods from './components/MyGoods.vue'
// 商品列表
const goodsList = [{id: '4001172',name: '称⼼如意⼿摇咖啡磨⾖机咖啡⾖研磨机',price: 289,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001594',name: '⽇式⿊陶功夫茶组双侧把茶具礼盒装',price: 288,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001009',name: '⽵制⼲泡茶盘正⽅形沥⽔茶台品茶盘',price: 109,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001874',name: '古法温酒汝瓷酒具套装⽩酒杯莲花温酒器',price: 488,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001649',name: '⼤师监制⻰泉⻘瓷茶叶罐',price: 139,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3997185',name: '与众不同的⼝感汝瓷⽩酒杯套组1壶4杯',price: 108,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3997403',name: '⼿⼯吹制更厚实⽩酒杯壶套装6壶6杯',price: 100,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3998274',name: '德国百年⼯艺⾼端⽔晶玻璃红酒杯2⽀装',price: 139,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},
]
</script>
<template><div class="list"><!-- 循环遍历、独⽴传值--><MyGoodsv-for="item in goodsList":key="item.id":imgUrl="item.picture":title="item.name":price="item.price"/></div>
</template>
<style lang="scss">
* {margin: 0;padding: 0;box-sizing: border-box;
}
.list {width: 1000px;margin: 0 auto;display: flex;flex-wrap: wrap;
}
</style>

MyGoods.vue 文件代码

<script setup>
// 接受数据
defineProps(['imgUrl', 'title', 'price'])
</script>
<template><div class="item"><img :src="imgUrl" :alt="title" /><p class="name">{{ title }}</p><p class="price">{{ price }}.00</p></div>
</template>
<style lang="scss" scoped>
.item {width: 240px;margin-left: 10px;padding: 20px 30px;transition: all 0.5s;margin-bottom: 20px;.item:nth-child(4n) {margin-left: 0;}&:hover {box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);transform: translate3d(0, -4px, 0);cursor: pointer;}img {width: 100%;}.name {font-size: 18px;margin-bottom: 10px;color: #666;}.price {display: flex;align-items: center;font-size: 22px;color: firebrick;button {margin-left: 48px;font-size: 14px;outline: none;}}.price::before {content: '¥';font-size: 22px;}
}
</style>

3.子传父(defineEmits)

数据传输步骤:

1. ⽗组件内,⼦组件上,绑定⾃定义事件,@⾃定义事件="⽗修改数据的函数" (⽗绑定)

2. ⼦组件恰当时机, 触发⽗组件的⾃定义事件 , emit('⾃定义事件', 携带的参数...), 从⽽导致⽗组件修改函数的时候(⼦触发)前提还需要拿到自定义事件的函数 emit   
const emit = defineEmits()
代码案例:

App.vue

<script setup>
import { ref } from 'vue'
// 导⼊ MyGoods 商品组件
import MyGoods from './components/MyGoods2.vue'
// 商品列表
const goodsList = ref([{id: '4001172',name: '称⼼如意⼿摇咖啡磨⾖机咖啡⾖研磨机',price: 289,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001594',name: '⽇式⿊陶功夫茶组双侧把茶具礼盒装',price: 288,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001009',name: '⽵制⼲泡茶盘正⽅形沥⽔茶台品茶盘',price: 109,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001874',name: '古法温酒汝瓷酒具套装⽩酒杯莲花温酒器',price: 488,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001649',name: '⼤师监制⻰泉⻘瓷茶叶罐',price: 139,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3997185',name: '与众不同的⼝感汝瓷⽩酒杯套组1壶4杯',price: 108,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3997403',name: '⼿⼯吹制更厚实⽩酒杯壶套装6壶6杯',price: 100,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3998274',name: '德国百年⼯艺⾼端⽔晶玻璃红酒杯2⽀装',price: 139,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},
])
// i: 下标,让哪个对象的 price 减少
// price: 每次减少的价格
//提供修改的函数
const subPrice = (i, price) => {goodsList.value[i].price -= price
}
</script>
<template><div class="list"><MyGoodsv-for="(item, index) in goodsList":key="item.id":imgUrl="item.picture":title="item.name":price="item.price":idx="index"@ccc="subPrice"/></div>
</template>
<style lang="scss">
* {margin: 0;padding: 0;box-sizing: border-box;
}
.list {width: 1000px;margin: 0 auto;display: flex;flex-wrap: wrap;
}
</style>

MyGoods.vue

<script setup>
import { ref } from 'vue'
// 导⼊ MyGoods 商品组件
import MyGoods from './components/MyGoods2.vue'
// 商品列表
const goodsList = ref([{id: '4001172',name: '称⼼如意⼿摇咖啡磨⾖机咖啡⾖研磨机',price: 289,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001594',name: '⽇式⿊陶功夫茶组双侧把茶具礼盒装',price: 288,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001009',name: '⽵制⼲泡茶盘正⽅形沥⽔茶台品茶盘',price: 109,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001874',name: '古法温酒汝瓷酒具套装⽩酒杯莲花温酒器',price: 488,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '4001649',name: '⼤师监制⻰泉⻘瓷茶叶罐',price: 139,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3997185',name: '与众不同的⼝感汝瓷⽩酒杯套组1壶4杯',price: 108,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3997403',name: '⼿⼯吹制更厚实⽩酒杯壶套装6壶6杯',price: 100,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},{id: '3998274',name: '德国百年⼯艺⾼端⽔晶玻璃红酒杯2⽀装',price: 139,picture: 'https://cxk-1305128831.cos.ap-beijing.myqcloud.com/11-00.gif',},
])
// i: 下标,让哪个对象的 price 减少
// price: 每次减少的价格
//提供修改的函数
const subPrice = (i, price) => {goodsList.value[i].price -= price
}
</script>
<template><div class="list"><MyGoodsv-for="(item, index) in goodsList":key="item.id":imgUrl="item.picture":title="item.name":price="item.price":idx="index"@ccc="subPrice"/></div>
</template>
<style lang="scss">
* {margin: 0;padding: 0;box-sizing: border-box;
}
.list {width: 1000px;margin: 0 auto;display: flex;flex-wrap: wrap;
}
</style>

4.祖先传后代

5.任意两个组件通信

五.props校验

作用:

我们前面使用 props 传输数据的时候,参数的格式传输错误,在控制台不会显示,我们添加了props校验之后,就会出现报错了,帮助开发者,快速发现错误。

完整语法格式:

defineProps({属性名: {type: 类型, // Number String Boolean ...required: true, // 是否必填default: 默认值, // 默认值// value: ⽗传⼦的值validator (value) {// ⾃定义校验逻辑return 布尔值}}
})

代码示例:里面有一种简单写法一种完整写法

App.vue 文件代码:

<script setup>
import { ref } from 'vue'
import MyProgress from './components/MyProgress.vue'
const width = ref(20)
</script>
<template><div class="app"><MyProgress :width="width" /><MyProgress :width="80" /></div>
</template>
<style></style>

MyProgress.vue 文件代码:

<script setup>
// 不验证的写法
// defineProps(['width'])// 验证的简易写法
// 属性:类型
// defineProps({
//   width: Number,
// })// 验证的完整写法
defineProps({width: {type: Number,// required: true  // 写了这个default就不会生效了default: 50,validator: (value) => {if (value >= 0 && value <= 100) {return true} else {console.error('The width prop must be between 0 and 100')return false}},},
})
</script>
<template><div class="my-progress"><div class="inner" :style="{ width: width + '%' }"><span>{{ width }}%</span></div></div>
</template>
<style scoped>
.my-progress {height: 26px;width: 400px;border-radius: 15px;background-color: #272425;border: 3px solid #272425;box-sizing: border-box;margin-bottom: 30px;
}
.inner {position: relative;background: #379bff;border-radius: 15px;height: 25px;box-sizing: border-box;left: -3px;top: -2px;
}
.inner span {position: absolute;right: -30px;top: 26px;
}
</style>

六.v-model进阶

1.原理

v-model本质上是⼀个语法糖。

例如应⽤在输⼊框上,就是 value 属性 和 input 事件的合写

<script setup>import { ref } from 'vue'const msg = ref('')
</script>
<template><p>{{ msg }}</p><input type="text" v-model="msg"><input type="text" :value="msg" @input="msg = $event.target.value" >
</template>

 $event.target.value 意思是获取当前输入框的内容。

2.v-model作用在组件上

作用:

实现父组件和子组件数据的双向绑定

代码实现:

App.vue 的代码实现

<script setup>
import { ref } from 'vue'
import BitSelect from './components/BitSelect.vue'
const activeId = ref('222')
</script>
<template><!-- 组件标签上的 $event 指的是 emit 传递来的参数 --><BitSelect :modelValue="activeId" @update:modelValue="activeId = $event" /><!--       自定义属性              自定义事件 --><!-- 简化写法 --><!-- <BitSelect v-model="activeId" /> -->
</template>

 BitSelect.vue

<script setup>
// 接收 modelValue 是父传子这里不能直接改,所以下面不能直接用 v-model
const props = defineProps({modelValue: {type: String,default: '',},
})
// 获取 emit
const emit = defineEmits()
</script>
<template><!-- 原生标签上的 $event 就是事件对象 --><!-- $event.target: select 原生DOM --><!-- $event.target.value: 选中的 option 的 value --><select :value="props.modelValue" @change="emit('update:modelValue', $event.target.value)"><option value="111">北京</option><option value="222">上海</option><option value="333">深圳</option><option value="444">杭州</option><option value="555">苏州</option></select>
</template>

流程图

简化:

App.vue 文件的代码

<script setup>
import { ref } from 'vue'
import BitSelect from './components/BitSelect.vue'
const activeId = ref('222')
</script>
<template><BitSelect v-model="activeId" />
</template>

  BitSelect.vue

<script setup>
// 子组件直接通过 defineModel() 来接收父组件传递的v-model值
// defineModel() 的返回值是一个 ref 数据,并且可以在子组件中直接操作这个 ref 数据,
// 并且可以在子组件中直接操作这个 ref 数据,会引起父组件的数据的同步更新
const model = defineModel()
</script>
<template><select v-model="model"><option value="111">北京</option><option value="222">上海</option><option value="333">深圳</option><option value="444">杭州</option><option value="555">苏州</option></select>
</template>

这里的defineModel是个语法糖。 

七.ref属性

1.ref是什么

是作用在标签上的属性,是 vue 新增的,原生不具备这个属性的。

2.作用:

用来获取原生 DOM 或 组件实例(进而调用组件提供的方法)。

3.获取原生DOM

<!-- @format -->
<script setup>
import { onMounted, ref } from 'vue'// 准备 ref 响应式数据
const divRef = ref(null)// 组件挂载后
onMounted(() => {// 获取原生 div// console.log(divRef.value)/// <div></div>// 以设置 div 字体颜色为例(DOM操作的代码):divRef.value.style.color = 'red'
})
</script><template><!-- 给目标元素添加ref属性并绑定数据 --><div ref="divRef">Some Text...</div>
</template><style scoped></style>

这个获取是直接从自己这个组件上获取,比原本从整棵DOM树上获取会快一点 

4.使用组件实例调用组件暴露的方法和属性(defineExpose)

App.vue 文件代码

<script setup>
import { ref } from 'vue'
import MyForm from './components/MyForm.vue'
const formRef = ref(null)
// 登录
const onLogin = () => {console.log(formRef.value.ccc)console.log(formRef.value.validate())// 进⾏校验// if (formRef.value.validate()) {//   console.log('ok')// } else {//   console.log('error')// }
}
</script>
<template><MyForm ref="formRef" /><button @click="onLogin">登 录</button>
</template>
<style>
#app {width: 300px;margin: 100px auto;
}
</style>

MyForm.vue 文件代码

<script setup>
// 表单校验
const validate = () => {return Math.random() > 0.5 ? true : false
}
// 暴漏给组件, ⽬的是⽗组件可以通过 ref 可以拿到⼦组件的⽅法
defineExpose({ccc: 100,validate,
})
</script>
<template><div class="login-box">账⼾:<input type="text" /><br /><br />密码:<input type="password" /><br /><br /></div>
</template>

八.nextTick

1.作用

当数据变了,想获取更新后的DOM,需要把代码写在这个方法的回调中。直接获取是获取不到的,因为DOM操作是异步的,获取DOM会在更新DOM之前执行。

2.代码案例

<script setup>
import { nextTick, ref } from 'vue'// 控制是否显示输入框
const isShowEdit = ref(false)// 点击编辑按钮
const onEdit = () => {isShowEdit.value = true// 问题:当数据变了,发现获取DOM拿不到的// 原因:在 vue3 中,当数据变了,DOM的更新是异步的;// 也就是说,数据变了,想立即获取最新的DOM是拿不到的,此时DOM并没有更新// console.log(inputRef.value) // null// 解决:利用 nextTick() 这个方法,因为在这个方法的回调中,DOM更新完毕了nextTick(() => {inputRef.value.focus()})
}const inputRef = ref(null)
</script><template><div class="box"><h3>大标题</h3><button @click="onEdit">编辑</button></div><div v-if="isShowEdit"><input type="text" ref="inputRef" /><button>确认</button></div>
</template>
<style scoped>
.box {display: flex;justify-content: space-between;align-items: center;width: 200px;height: 40px;
}
</style>

九.自定义指令

1.作用

封装⼀段 公共的DOM操作 代码,便于复⽤。

2.基本使用 

①语法

注册

// main.js 中
app.directive('指令名', {// 元素挂载后(成为真实DOM) ⾃动触发⼀次⾃动执⾏mounted(el) {// el: 指令所在的DOM元素}
}

使用

<p v-指令名></p>
②代码示例

main.js 文件代码

// 按需导入 createApp 函数
import { createApp } from 'vue'
// 导入 App.vue
import App from './App.vue'
// 创建应用
const app = createApp(App)// 定义指令
app.directive('jujiao', {// 指令所在的标签插入到真实DOM中自动执行一次mounted(el) {// el就是指令所在的DOM元素, 拿到 el 了,// 就可以对 el 做任何原生DOM操作// console.log('mounted', el)el.focus()}
})
// 指定渲染的位置
app.mount('#app')

App.vue 文件代码

<template><input type="text" v-jujiao />
</template><script setup></script><style scoped></style>

3.绑定数据

①语法

1.在绑定指令时,可以通过“等号”的形式为指令 绑定 具体的参数值

<div v-color="colorStr">Some Text</div>

 2.通过 binding.value 可以拿到指令值,指令值修改会 触发 updated 钩⼦

app.directive('指令名', {// 挂载后⾃动触发⼀次mounted(el, binding) { },// 数据更新, 每次都会执⾏updated(el, binding) { }
})
②代码示例

main.js 文件代码

// 按需导入 createApp 函数
import { createApp } from 'vue'
// 导入 App.vue
import App from './App.vue'
// 创建应用
const app = createApp(App)app.directive('color', {// 指令所在的DOM元素变成真实DOM了,会自动执行一次;// 换句话说,当指令所在表达式的值变了,该函数不会再次执行了mounted(el, binding) {// el: 指令所在元素// binding: 指令绑定的信息对象,binding.value 获取指令表达式的结果el.style.color = binding.valueconsole.log('1-mounted')},// 每次指令绑定表达式的值变了,都会执行一次updated(el, binding) {el.style.color = binding.valueconsole.log('2-updated')}
})// 指定渲染的位置
app.mount('#app')

App.vue 文件代码

<script setup>import { ref } from 'vue'// 提供的颜色响应式数据const colorStr = ref('red')
</script><template><!-- 使用自定义指令,并绑定表达式 --><div v-color="colorStr">Some Text</div>
</template><style scoped></style>

main.js 的简化写法 

对于自定义指令来说,一个很常见的情况是仅仅需要在 mounted 和 updated 上实现相同的行为。这种情况我们可以直接用一个函数的定义指令,如下所示:

app.directive('color', (el, binding) => {// 这会在 `mounted` 和 `updated` 时都调⽤el.style.color = binding.value
}

十.插槽

1.作用

让组件内部的⼀些 结构 ⽀持 ⾃定义

2.默认插槽

①基本语法

1. 组件内需要定制的结构部分,改⽤ <slot></slot> 占位

2. 使⽤组件时, <MyDialog></MyDialog>写成双标签 , 包裹结构, 传⼊替换slot

②代码示例

MyDialog.vue 文件代码

<script setup></script>
<template><div class="dialog"><div class="dialog-header"><h3>友情提⽰</h3><span class="close">✖</span></div><div class="dialog-content"><!-- 1. 在组件内标签不确定位置用 slot 组件占位 --><slot> </slot></div><div class="dialog-footer"><button>取消</button><button>确认</button></div></div>
</template>
<style scoped>
* {margin: 0;padding: 0;
}
.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}
.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}
.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}
.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}
.dialog-footer {display: flex;justify-content: flex-end;
}
.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}
.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
</style>

App.vue 文件代码

<script setup>
import MyDialog from './components/MyDialog.vue'
</script>
<template><!-- 2. 在使用组件的时候,给组件传入想展示的结构,从而替换掉占位的 slot 组件 --><MyDialog> 你取认要删除吗 </MyDialog><MyDialog><span>我已经删除了</span></MyDialog>
</template>
<style scoped>
body {background-color: #b3b3b3;
}
</style>

3.插槽默认值

通过插槽完成了内容的定制,传什么显⽰什么, 但是如果不传,则是空⽩,如果我们需要有默认值在, <slot></slot> 标签里面写默认值就可以了,比较简单这里就不代码演示了。

4.具名插槽

①作用

一个组件有多个位置需要使用插槽是使用具名插槽。

②语法

 • 多个slot使⽤name属性区分名字

• template配合v-slot:名字 来分发对应标签

v-slot写起来太⻓,vue给我们提供⼀个简单写法 v-slot: 直接简写为 #

③代码示例 

MyDialog.vue 文件代码

<script setup></script>
<template><div class="dialog"><div class="dialog-header"><slot name="header"><h3>温馨提⽰</h3></slot><span class="close">✖</span></div><div class="dialog-content"><slot>我是主体内容</slot></div><div class="dialog-footer"><slot name="footer"><button>取消</button><button>确认</button></slot></div></div>
</template>
<style>
* {margin: 0;padding: 0;
}
.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}
.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}
.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}
.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}
.dialog-footer {display: flex;justify-content: flex-end;
}
.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}
.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
</style>

App.vue 文件代码

<script setup>
import MyDialog from './components/MyDialog.vue'
</script>
<template><!-- 1. 不传、都显⽰插槽默认内容 --><MyDialog /><!-- 2. 传了 --><MyDialog><template v-slot:header><h3>友情提⽰</h3></template><template v-slot:default> 请输⼊正确⼿机号 </template><template v-slot:footer><button>关闭</button></template></MyDialog><!-- 3. 传了 --><MyDialog><template v-slot:header><h3>警告</h3></template><template v-slot:default> 你确认要退出么? </template><template v-slot:footer><button>确认</button><button>取消</button></template></MyDialog>
</template>
<style>
body {background-color: #b3b3b3;
}
</style>

5.作用域插槽

①作用

带数据的插槽, 可以让组件功能更强⼤、更灵活、复⽤性更⾼; ⽤ slot 占位的同时, 还可以给 slot 绑定数据, 将来使⽤组件时, 不仅可以传内容, 还能使⽤ slot 带来的数据。

②代码示例

MyDialog.vue 文件代码

<script setup></script><template><div class="dialog"><div class="dialog-header"><slot name="header"></slot><span class="close">×</span></div><div class="dialog-content"><slot name="body" account="xiaobit" password="123456"></slot></div><div class="dialog-footer"><button>取消</button><button>确认</button></div></div>
</template><style>
* {margin: 0;padding: 0;
}
.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}
.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}
.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}
.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}
.dialog-footer {display: flex;justify-content: flex-end;
}
.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}
.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
</style>

App.vue 文件代码

<script setup>
import MyDialog from './components/MyDialog.vue'
</script><template><MyDialog><template #header="obj"><h4>登录</h4></template><template #body="obj"><!-- 默认接收的数据 obj 是个空对象 --><p>{{ obj }}</p>账号:<input type="text" v-model="obj.account" /><br /><br />密码:<input type="password" v-model="obj.password" /><br /><br /></template></MyDialog>
</template><style scoped></style>
③代码工作流程图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88157.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88157.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/88157.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OS学习笔记

《几个基本知识点》 一、2的幂 1024210 51229 25628 12827 6426 3225 1624 823 422 221 K210 G220 M230 T240 P250 E260 Z270 Y280 R290 Q2100 二、常用的ASCII码 ‘1’0x31 ‘A’0x41 ‘a’0x61 空格0x20 换行0x0A 回车0x0D 三、存储器层次中的典型速度 CPU/寄存器&#xff1a…

嵌入式学习笔记-MCU阶段-DAY01

恭喜大家完成了C语言的学习&#xff0c;现在咱们来到咱们的硬件MCU阶段&#xff0c;咱们这里的工程用的是keil&#xff0c;环境搭建不再赘述&#xff0c;希望大家在这一阶段仍然学的愉快 1.资料部分 用的最多的就是STM32f103的手册&#xff0c;搭配STM32F103ZET6的开发板 2.概…

three案例 Three.js波纹效果演示

波纹效果&#xff0c;在智慧城市可视化开发中经常用到&#xff0c;这里分享一个比较好玩的案例 以下是详细的步骤&#xff1a; 初始化部分&#xff1a;设置 Three.js 环境&#xff0c;包括场景、相机、渲染器和控制器 几何体和纹理&#xff1a;创建平面几何体并加载波纹纹理 着…

Flutter-详解布局

上一章我们详细的学习了 Flutter 中的Widget&#xff0c;这一章我们将要学习 Flutter 的布局&#xff0c; 在上一章我们了解到了&#xff1a;Everything is a widget&#xff0c;在 Flutter 中几乎所有的对象都是一个 Widget &#xff0c;当然也包括布局&#xff0c;Flutter 的…

EPLAN 电气制图:建立自己的部件库,添加部件-加SQL Server安装教程(三)上

在智能电气设计领域&#xff0c;EPLAN 作为主流的设计软件&#xff0c;其部件库的完善程度直接影响项目设计的效率与质量。本文将从实际操作出发&#xff0c;详细讲解如何在 EPLAN 中建立专属部件库并添加部件&#xff0c;为电气设计奠定坚实基础。一、部件库&#xff1a;电气设…

静态路由进阶实战全解

一、项目背景二、项目拓扑图三、设备命名与IP地址规划设备名接口编号IP地址规划R1GE0/0192.168.1.1/24GE0/1172.16.1.1/24R2GE0/0192.168.1.2/24GE0/1192.168.2.2/24R3GE0/0192.168.2.3/24GE0/1192.168.3.3/24GE0/2192.168.4.3/24R4GE0/0192.168.3.4/24GE0/1192.168.4.4/24GE0/…

stm32hal模块驱动(3)ssd1305 oled驱动

SD1305 OLED 驱动芯片详细介绍SSD1305 是 Solomon Systech 公司生产的一款 OLED 显示控制器/驱动器&#xff0c;专为 128x64 或 128x32 点阵的 OLED 显示屏设计。下面我将从多个方面详细介绍这款驱动芯片。一、SSD1305 基本特性显示分辨率&#xff1a;最大支持 128 segments 6…

安全为先:如何在 Python 中安全处理数据库连接与敏感信息

安全为先:如何在 Python 中安全处理数据库连接与敏感信息 引言:Python 与安全的数据库交互 自 1991 年诞生以来,Python 凭借其简洁优雅的语法和强大的生态系统,成为 Web 开发、数据科学、人工智能和数据库交互的首选语言。作为“胶水语言”,Python 不仅让开发者能够快速…

服务器经常出现蓝屏是什么原因导致的?如何排查和修复?

服务器出现蓝屏&#xff08;BSOD&#xff0c;Blue Screen of Death&#xff09;是一个严重的问题&#xff0c;通常表明系统内核或硬件发生了不可恢复的错误。蓝屏不仅会导致服务器宕机&#xff0c;还可能对业务运行造成重大影响。要有效解决蓝屏问题&#xff0c;需要先找到根本…

为什么elementui的<el-table-column label=“名称“ prop=“name“ label不用写成:label

在 Vue.js 中&#xff0c;label 和 prop 是 el-table-column 组件的普通属性&#xff0c;而不是动态绑定的表达式。因此&#xff0c;不需要使用 : 来绑定它们。 1. Vue.js 中的属性绑定 在 Vue.js 中&#xff0c;属性绑定有两种方式&#xff1a; 静态属性绑定&#xff1a;直接写…

分布式光纤传感:为储能安全保驾护航

储能系统是指一种能够将电能、化学能、动能等形式的能量进行转化、储存和释放的装置&#xff0c;广泛应用于可再生能源发电、智能电网、电动车等领域。储能行业这几年得到了稳步发展&#xff0c;受到政府机构、行业协会、大型能源企业、电网公司、系统集成商、检测认证机构等业…

从历史航拍图像中去除阴影

在光学遥感中&#xff0c;阴影是影响土地覆盖制图精度和分辨率的一个因素&#xff0c;无论是历史影像&#xff08;黑白影像&#xff09;还是近期影像&#xff08;全彩影像&#xff09;。阴影的产生取决于太阳光照&#xff08;太阳方位角和天顶角&#xff09;、相机视点&#xf…

UE material advance 学习笔记

如何体现轮胎速度的快速感&#xff1a;就是增加一个radial blur&#xff0c;会让视觉效果感觉轮胎已经转冒烟了&#xff0c;但是上面两个轮胎的转速其实是相同的这种磨砂的感觉&#xff0c;可以用上ditherAA来实现只看法线这一块&#xff0c;ditherAA就是让他的表面颜色有大量的…

Vue--2、Vue2 项目配置与组件化开发

一、Vue2 项目环境搭建1. 环境准备安装 Node.js&#xff1a;推荐使用 nvm 管理多版本 Node# 安装Node 16.20.2 nvm install 16.20.2 # 切换至指定版本 nvm use 16.20.2 # 验证安装 node -v && npm -v安装 Vue CLI 脚手架&#xff1a;# 国内镜像源安装 npm install --re…

虚幻基础:函数的返回节点

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录函数的返回节点&#xff1a;返回执行后的值返回执行后的值若不执行第一次 返回参数的默认值第二次 返回上一次执行值示例函数的返回节点&#xff1a;返回执行后的值 返回执行后的值 若不执行 第一次 返回参数的默…

FFmpeg 升级指北

近期我参与了部门底层库依赖的 FFmpeg 从 3.4 升级至 7.0.2 的工作&#xff0c;在此分享一些经验和遇到的 API 变动。 将 FFmpeg 升级到高版本后&#xff0c;编译过程中遇到大量报错是常态。这些错误通常源于 API 接口变更或结构体字段调整。此时不必惊慌&#xff0c;核心解决…

RISCV Linux 虚拟内存精讲系列三 -- setup_vm()

在 Linux 使用虚拟地址前&#xff0c;需要先配置页表&#xff0c;这就是 setup_vm() 的作用。然而&#xff0c;Linux 的页表配置&#xff0c;并不是一次过完成的&#xff0c;分了两个阶段&#xff0c;如下&#xff1a;在 setup_vm() 中&#xff0c;主要初始化了&#xff1a;1. …

创客匠人:解析创始人 IP 打造的底层逻辑与知识变现路径

在数字经济时代&#xff0c;创始人 IP 的价值被不断放大&#xff0c;而知识变现作为 IP 商业闭环的核心环节&#xff0c;正成为无数创业者探索的方向。创客匠人深耕知识付费领域多年&#xff0c;见证了大量创始人从 0 到 1 打造 IP 并实现变现的全过程&#xff0c;其背后的逻辑…

Visual Studio 2022 MFC Dialog 添加Toolbar及Tips提示

主要步骤&#xff1a;在主框架类中添加消息处理函数声明在 OnCreate 函数中启用工具栏提示在消息映射中注册 TTN_NEEDTEXT 消息使用 OnToolTipText 函数实现自定义提示文本1.在主程序的.h文件中加入afx_msg BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); 2.在…

2025Q2大模型更新汇总(大语言模型篇)

摘要 2025年Q2大语言模型更新汇总&#xff1a; Qwen3&#xff0c;Deepseek-R1-0528&#xff0c;Doubao-Seed-1.6, MiniMax-M1, GPT4.1/O3/O4&#xff0c;Claude4/Gemini2.5 Qwen3 • 开源MOE模型&#xff0c; • MOE模型&#xff1a;Qwen3-235B-A22B&#xff0c;Qwen3-30B-…