哈喽,大家好,这次给大家带来的是属于Vue3的TS版,组件传参、父子传参、子父传参、兄弟之间传参、插槽、内置组件与TS组合式API写法以及实践应用。
计算属性语法
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
Class 与 Style 绑定
<div :class="{ active: isActive }"></div>
<scrpit>
const isActive = ref(true)
const hasError = ref(false)
</script>
深入组件
生命周期钩子
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
console.log(`the component is now mounted.`)
})
</script>
侦听器watch
import { ref, watch } from 'vue'
const question = ref(1)
const answer = ref('Questions usually contain a question mark. ;-)')
const loading = ref(false)
// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
if (newQuestion.includes('?')) {
loading.value = true
answer.value = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
answer.value = (await res.json()).answer
} catch (error) {
answer.value = 'Error! Could not reach the API. ' + error
} finally {
loading.value = false
}
}
console.log(typeof newQuestion,oldQuestion);
{ immediate: true }//第一次执行
{deep:true}//深度监听 对性能消耗很大
})
</script>
<template>
<p>
question:
<input v-model="question" :disabled="loading" />
</p>
<p>{{ answer }}</p>
</template>
watchEffect()
不需要指定 immediate: true 初始化自动调用
每当值变化 自动更新
深度监听比watch更好
自定义函数 吊销的意思IsRevoked
Props 写法
泛型定义传参的 参数
const pop = defineProps<{
foo:string,
bar?:number
}>()
props接口定义 接收父组件参数
// 接口定义
interface pops{
foo:string,
bar?:number
}
const pops = defineProps<pops>()
withDefaults设置默认值,避免在使用组件时的繁琐检查,提高组件的可读性和可维护性。下面是WithDefaults的基本用法:
import { withDefaults } from 'vue'
const DEFAULTS = {
someDefaultValue: 'default',
otherDefaultValue: 42,
}
export default withDefaults({
props: {
someProp: String,
otherProp: Number,
},
}, DEFAULTS)
组件事件$defineEmits
子组件 定义defineEmits(【‘事件名’】)
import { defineProps,ref,defineEmits } from 'vue'
const value = ref<string>("子组件给父组件传参")
const emit = defineEmits(["getvalue"])
const transValue = ()=>{
emit('getvalue',value.value)
}
// 接口定义
interface pops{
foo:string,
bar?:number
}
const pops = defineProps<pops>()
console.log(pops,"当前数据");
</script>
<template>
<button >
{{foo}}
</button>
<button @click="transValue" style="margin: 5px">传值给父组件</button>
</template>
父组件
import HelloWorld from './components/HelloWorld.vue'
import {ref} from 'vue'
const foo = ref<string>('你好')
const bar = ref<number>(123)
const sonMessage = ref<string>("")
const getValuehello = (vlaue:string)=>{
sonMessage.value = vlaue
}
</script>
<template>
<HelloWorld :foo="foo" :bar="bar" @getvalue="getValuehello"/>
<div>
我是父组件: {{sonMessage}}
</div>
</template>
子组件暴露属性给父组件 defineExpose
子组件可以使用defineExpose暴露自身的属性或者方法,父组件中使用ref调用子组件暴露的属性或方
法。
如下为子组件Son.vue
<div style="margin: 10px;border: 2px solid red">
我是子组件
</div>
</template>
<script setup lang="ts">
import {ref, defineExpose} from "vue";
// 暴露给父组件的值
const toFatherValue = ref<string>("我是要暴露给父组件的值")
// 暴露给父组件的方法
const toFatherMethod = () => {
console.log("我是要暴露给父组件的方法")
}
// 暴露方法和属性给父组件
defineExpose({toFatherMethod, toFatherValue})
</script>
如下为父组件Father.vue
<div class="fa">
<div style="margin: 10px;">我是父组件</div>
<button @click="getSonMethod">获取子组件的方法</button>
<Son ref="sonMethodRef"></Son>
</div>
</template>
<script setup lang="ts">
import Son from './Son.vue'
import {ref} from "vue";
const sonMethodRef = ref()
const getSonMethod = () => {
sonMethodRef.value.toFatherMethod()
console.log(sonMethodRef.value.toFatherValue)
}
</script>
<style scoped>
.fa{
border: 3px solid cornflowerblue;
width: 400px;
text-align: center;
}
</style>
为 provide / inject 标注类型 (组件与组件之间的值)
根组件
<div>
我是root组件
<Footer></Footer>
</div>
</template>
<script setup lang="ts">
import { provide, ref } from 'vue'
import Footer from './components/Footer.vue'
const toChildValue= ref<string>("我是给所有子组件的值")
// 将toChildValue注入到所有子组件中
provide(/* 注入名 */ 'toChildValue', /* 值 */ toChildValue)
</script>
第二组件
<div>
我是footer组件
<div>
接收父组件的值:{{getFatherValue}}
</div>
<DeepChild></DeepChild>
</div>
</template>
<script setup lang="ts">
import DeepChild from "./DeepChild.vue"
import {ref,inject,Ref} from "vue";
// 获取父组件提供的值
// 如果没有祖先组件提供 "toChildValue"
// ref("") 会是 "这是默认值"
const getFatherValue = inject<Ref<string>>(/* 注入名 */"toChildValue",/* 默认值 */ ref(""))
</script>
第三组件
<div>
我是deepChild组件
<div>
接收爷爷组件的值:{{getGrandFatherValue}}
</div>
</div>
</template>
<script setup lang="ts">
import {inject, ref, Ref} from "vue";
// 获取爷爷组件提供的值
// 如果没有爷爷组件提供 "toChildValue"
// value 会是 ""
const getGrandFatherValue = inject<Ref<string>>(/* 注入名 */"toChildValue",/* 默认值 */ ref(""))
</script>
兄弟组件之间 动态赋值 根组件 provide
<div>
我是root组件
<Footer></Footer>
</div>
</template>
<script setup lang="ts">
import {InjectionKey, provide, Ref, ref} from 'vue'
import Footer from './components/Footer.vue'
const toChildValue= ref<string>("我是给所有子组件的值")
/**
* 修改父组件值的方法
*/
const changeValue = () => {
toChildValue.value = "我是父组件修改的值"
}
// 定义一个注入key的类型(建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入)
interface ProvideType {
toChildValue: Ref<string>;
changeValue: () => void;
}
// 为注入值标记类型
const toValue = Symbol() as InjectionKey<ProvideType>
// 将toChildValue和changeValue注入到所有子组件中
provide(/* 注入名 */ 'toValue', /* 值 */{
toChildValue,
changeValue
})
</script>
无数根组件 inject
<div>
我是deepChild组件
<div>
<button @click="changeValue">改变祖先组件的值</button>
{{toChildValue}}
</div>
</div>
</template>
<script setup lang="ts">
import {inject, Ref} from "vue";
// 定义注入值的类型
interface ProvideType {
toChildValue: Ref<string>;
changeValue: () => void;
}
// 解构获取父组件传的值,需要进行强制类型转换
const {toChildValue, changeValue} = inject(/* 注入名 */"toValue") as ProvideType
// 不解构时,只需指定类型即可
// const value = inject<ProvideType>(/* 注入名 */"toValue")
</script>
插槽 Slots
子组件:SlotsComponent.vue
<div class="red-text">
<slot>
子组件插槽
</slot>
</div>
</template>
<style scoped>
.red-text {
color: red;
}
</style>
父组件:index.vue
<div>
<label>父组件</label>
<SlotComponent></SlotComponent>
</div>
</template>
<script>
import SlotComponent from "./component/SlotsComponent";
export default {
components: {
SlotComponent
}
};
</script>
##具名插槽
就是具有名称的插槽,公司项目中会用到
子组件
设置插槽名字 <slot name="名字">
<header >
<slot name="header">
你好
</slot>
</header>
<main>
<slot name="main">
中部
</slot>
</main>
<footer>
<slot name="footer">
尾巴
</slot>
</footer>
</div>
父组件
使用 <template v-slot:插槽名字>
<template v-slot:header>
尾巴
</template>
<template v-slot:footer>尾巴1</template>
</Admin>
动态插槽名
我们可以将父组件的内容,动态渲染到到子组件插槽接口中。
父组件 <template v-slot:[dynamicSlotName]>
JS声明:let dynamicSlotName = ref("header")
针对reactive的响应式引用类型 同类型。
- 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
- 语法:
const name = toRef(person,'name') - 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
- 扩展:
toRefs与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
import { toRef,reactive } from "vue";
const person = reactive({
name:"你好"
})
const name = toRef(person,'name')
console.log(name.value);
</script>