1. Component introduction
When used setup
, the component can be imported directly, no need to manually register
<template>
<Child />
</template>
<script setup lang="ts">
import Child from "./Child.vue";
</script>
2. ref and reactive
ref
It is generally used for basic data types, such as string
, boolean
, andreactive
the place where it is generally used for object ref is actually the reactive
implementation of the call.
<template>
<h1>{
{ title }}</h1>
<div>
{
{ data }}
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
const title = ref("title");
const data = reactive({
userName: "xiaoming",
age: 18,
});
</script>
3. defineEmits and defineProps get the values and events passed by the parent component
// 第一种不带默认值props
const props = defineProps<{
foo: string
bar?: number
}>()
// 第二种带默认值props
export interface ChildProps {
foo: string
bar?: number
}
const props = withDefaults(defineProps<ChildProps>(), {
foo: "1qsd"
bar?: 3
})
// 第一种获取事件
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// 第二种获取事件
const emit = defineEmits(["dosth"])
4. Use useAttrs and useSlots
useAttrs
You can get the value passed from the parent component . The content of the slot can be obtained. In the example, we use to get the content passed by the parent component , and get the content of the slot. id
class
useSlots
useAttrs
id
class
useSlots
parent component:
<template>
<div class="father">{
{ fatherRef }}</div>
<Child :fatherRef="fatherRef" @changeVal="changeVal" class="btn" id="111">
<template #test1>
<div>1223</div>
</template>
</Child>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
const fatherRef = ref("1");
function changeVal(val: string) {
fatherRef.value = val;
}
</script>
<style lang="scss" scoped>
.father {
margin-top: 40px;
margin-bottom: 40px;
}
.btn {
font-size: 20px;
color: red;
}
</style>
Subassembly:
<template>
<!-- <div class="child">{
{ props.fatherRef }}</div> -->
<div v-bind="attrs">
<slot name="test1">11</slot>
<input type="text" v-model="inputVal" />
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs, useSlots } from "vue";
const props = defineProps<{
fatherRef: string;
}>();
const emits = defineEmits(["changeVal"]);
const slots = useSlots();
const attrs = useAttrs();
console.log(122, attrs, slots);
const inputVal = computed({
get() {
return props.fatherRef;
},
set(val: string) {
emits("changeVal", val);
},
});
</script>
Using custom directives
setup
When customizing instructions inside, you only need to follow this vNameOfDirective
naming convention
For example, the following custom focus
instructions are named vMyFocus
and used v-my-focus
custom directive
<script setup lang="ts">
const vMyFocus = {
onMounted: (el: HTMLInputElement) => {
el.focus();
// 在元素上做些操作
},
};
</script>
<template>
<input v-my-focus value="111" />
</template>
5. Use defineExpose child component to pass parent component
Subassembly
<template>
<div class="child"></div>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
function doSth() {
console.log(333);
}
defineExpose({ doSth });
</script>
parent component
<template>
<div class="father" @click="doSth1">222</div>
<Child ref="childRef"></Child>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import Child from "./Child.vue";
const childRef = ref();
function doSth1() {
childRef.value.doSth();
}
</script>
6. Parent component to child component
parent component
<template>
<div class="father"></div>
<Child @doSth="doSth"></Child>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import Child from "./Child.vue";
function doSth() {
console.log(112);
}
</script>
Subassembly
<template>
<div class="child">2222</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
const emits = defineEmits(["doSth"]);
onMounted(() => {
emits("doSth");
});
</script>
7. toRefs
When passing from a parent component to a child component props
, it must be used toRefs
or toRef
transferred. Why is this?
This is because if you don't use toRefs
one turn, when the parent component props
changes, if the child component uses Es6 parsing, it will lose responsiveness.
You can see the following example
parent component
<template>
<div class="father" @click="changeVal">{
{ fatherRef }}</div>
<Child :fatherRef="fatherRef"></Child>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import Child from "./Child.vue";
const fatherRef = ref(1);
function changeVal() {
fatherRef.value = 2;
}
</script>
<style lang="scss" scoped>
.father {
margin-bottom: 40px;
}
</style>
Subassembly
<template>
<div class="child" @click="changeVal">{
{ fatherRef }}</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, toRefs } from "vue";
const props = defineProps<{
fatherRef: any;
}>();
const { fatherRef } = props;
function changeVal() {
fatherRef.value = 34;
}
</script>
It can be seen that when the parent component is clicked, const { fatherRef } = props;
it loses responsiveness because it is parsed using
So when the parent component becomes 2, the child component is still 1.
There are two solutions here
-
use
const { fatherRef } = toRefs(props)
; -
use in the template
props.fatherRef
8. Subcomponents use v-model
8.1 Computed can be used in subcomponents to achieve two-way binding
parent component
<template>
<div class="father">{
{ fatherRef }}</div>
<Child :fatherRef="fatherRef" @changeVal="changeVal"></Child>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
const fatherRef = ref("1");
function changeVal(val: string) {
fatherRef.value = val;
}
</script>
<style lang="scss" scoped>
.father {
margin-top: 40px;
margin-bottom: 40px;
}
</style>
Subassembly
<template>
<!-- <div class="child">{
{ props.fatherRef }}</div> -->
<input type="text" v-model="inputVal" />
</template>
<script setup lang="ts">
import { computed } from "vue";
const props = defineProps<{
fatherRef: string;
}>();
const emits = defineEmits(["changeVal"]);
const inputVal = computed({
get() {
return props.fatherRef;
},
set(val: string) {
emits("changeVal", val);
},
});
</script>
8.2 You can pass values and change values from parent components, and then child components can also use v-model
In the example, the parent component passes modelValue
and update:modelValue
methods the parent component:
<template>
<Child :modelValue="searchText" @update:modelValue="changeVal"> </Child>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
const searchText = ref(1);
function changeVal(val: number) {
searchText.value = val;
}
</script>
<style lang="scss" scoped>
.father {
margin-top: 40px;
margin-bottom: 40px;
}
.btn {
font-size: 20px;
color: red;
}
</style>
Subassembly:
<template>
<!-- <div class="child">{
{ props.fatherRef }}</div> -->
<!-- <div v-bind="attrs">
<slot name="test1">11</slot>
<input type="text" v-model="inputVal" />
</div> -->
<input v-model="modelValue" />
</template>
<script setup lang="ts">
import { computed, useAttrs, useSlots } from "vue";
const props = defineProps<{
modelValue: number;
}>();
// const emits = defineEmits(["changeVal"]);
</script>
9. Recursive components
The component itself can call the component itself, that is, recursion. In vue3, the file name is automatically registered as the name of the component. For example, a component named can refer to itselfChild.vue
in its template . What needs to be noted here is that a conditional statement needs to be set to interrupt the recursion, otherwise the recursion will recurse infinitely. <Child/>
parent component
<template>
<Child :modelValue="searchText" @update:modelValue="changeVal"> </Child>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
const searchText = ref(1);
function changeVal(val: number) {
searchText.value = val;
}
</script>
<style lang="scss" scoped>
.father {
margin-top: 40px;
margin-bottom: 40px;
}
.btn {
font-size: 20px;
color: red;
}
</style>
Subassembly
<template>
<input v-model="modelValue" />
<Child
:modelValue="test"
@update:modelValue="changeTest"
v-if="modelValue > 2"
></Child>
</template>
<script setup lang="ts">
import { computed, useAttrs, useSlots, ref } from "vue";
const props = defineProps<{
modelValue: number;
}>();
const test = ref(0);
function changeTest(val: number) {
test.value = val;
}
// const emits = defineEmits(["changeVal"]);
</script>
<style lang="scss" scoped>
.child {
position: relative;
}
</style>
10. vue3 ts get component ref instance
-
Get the dom reference directly through ref
<template>
<div class="demo1-container">
<div ref="sectionRef" class="ref-section"></div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const sectionRef = ref()
</script>
By adding the ref attribute to the div element , in order to obtain this element, we declare a variable sectionRef with the same name as the ref attribute , and then we can obtain the div element in the form of sectionRef.value
-
Get the dom reference through the ref traversal of the parent container
<template>
<div class="demo2-container">
<div ref="listRef" class="list-section">
<div @click="higherAction(index)" class="list-item" v-for="(item, index) in state.list" :key="index">
<span>{
{item}}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
const listRef = ref()
</script>
By adding the ref attribute to the parent element and declaring a variable listRef with the same name as the ref attribute, the dom object containing the child element will be obtained through listRef.value. At this time, the child element dom can be obtained in the form listRef.value.children[index]
of
-
Put the dom reference into the array by: ref
<template> <div class="demo2-container"> <div class="list-section"> <div :ref="setRefAction" @click="higherAction(index)" class="list-item" v-for="(item, index) in state.list" :key="index"> <span>{ {item}}</span> </div> </div> </div> </template> <script setup lang="ts"> import { reactive } from 'vue' const state = reactive({ list: [1, 2, 3, 4, 5, 6, 7], refList: [] as Array<any> }) const setRefAction = (el: any) => { state.refList.push(el); } </script>
Call the method through the :ref loop , which will receive an el parameter by default. This parameter is the form of
setRefAction
the div element we need to get at this time to get the child element domstate.refList[index]
-
Pass ref through child component emit
<template>
<div ref="cellRef" @click="cellAction" class="cell-item">
<span>{
{item}}</span>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue';
const props = defineProps({
item: Number
})
const emit = defineEmits(['cellTap']);
const cellRef = ref();
const cellAction = () => {
emit('cellTap', cellRef.value);
}
</script>
By adding the ref attribute to the subcomponent and declaring a variable cellRef with the same name as the ref attribute , cellRef.value can be passed as a dom reference through emit
-
The way to obtain it in render components such as tsx is simpler
import { defineComponent, ref, onMounted } from "@vue/runtime-core";
import { ElForm } from "element-plus";
export default defineComponent({
setup() {
const $form = ref<InstanceType<typeof ElForm>>(null);
onMounted(() => {
$form.value?.validate; // 类型正确
});
return () => <ElForm ref={$form}></ElForm>;
},
});
It should be noted that if the exposed method is used, the corresponding type cannot be obtained, and you need to customize the type github.com/vuejs/rfcs/…[1]
// 组件 MyForm
import { defineComponent, ref, onMounted } from "@vue/runtime-core";
import { ElForm } from "element-plus";
type ELEForm = InstanceType<typeof ElForm>;
// 在外界通过 ref 获取组件实例 请使用这个类型
export interface MyFormExpose {
validate: ELEForm["validate"];
}
export default defineComponent({
name: "MyForm",
setup(props, { expose }) {
const $form = ref<InstanceType<typeof ElForm>>(null);
expose({
validate: (callback) => $form.value?.validate(callback),
} as MyFormExpose);
return () => <ElForm ref={$form}></ElForm>;
},
});
<!-- Home.vue -->
<template>
<MyForm :ref="$form" />
</template>
<script>
import { defineComponent, ref, onMounted } from '@vue/runtime-core'
import MyForm, { MyFormExpose } from '@/components/MyForm'
export default defineComponent({
components: { MyForm }
setup(){
const $form = ref<InstanceType<typeof MyForm> & MyFormExpose>(null)
onMounted(() => {
$form.value?.validate // 类型正确
})
}
})
</script>