阅读vue的英文官网

中文的vue官网比vue的英文官网差许多,不管学什么都要去获取第一手资料,不要看中文官网,直接去看英文官网!!!

依据自己的运用习惯,设置vscode的vue代码片段,引荐运用snippet-generator.app

"vue3模版": {
"prefix": "vue3",
"body": [
   "<template>",
       " <div class='${1:box}'></div>",
   "</template>",
   " ",
   "<script setup lang='ts'>",
   " import {ref,reactive} from "vue";",
   " ${3}",
   "</script>",
   " ",
   "<style lang="scss" scoped>",
   " .${2:box} {",
   " }",
   "</style>"
],
"description": "vue3模版"
}

组件引入

当运用setup的时分,组件直接引入就能够了,不需求再自己手动注册

ref和reactive

ref一般用于基本的数据类型,比方string,boolean
reactive一般用于目标
ref的地方其实也是调用的reactive完成的。

defineEmits和defineProps获取父组件传过来值和事情

// 第一种不带默认值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"])

运用useAttrs和useSlots

useAttrs 能够获取父组件传过来的id和class等值。
useSlots 能够获得插槽的内容。
比如中,咱们运用useAttrs获取父组件传过来的id和class,useSlots获取插槽的内容。

父组件:

<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>

子组件:

<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>
<style lang="scss" scoped>
.child {
}
</style>

运用自定义指令

在setup里面自定义指令的时分,只需求遵从vNameOfDirective这样的命名标准就能够了

比方如下自定义focus指令,命名便是vMyFocus,运用的便是v-my-focus

自定义指令

<script setup lang="ts">
const vMyFocus = {
  onMounted: (el: HTMLInputElement) => {
    el.focus();
    // 在元素上做些操作
  },
};
</script>
<template>
  <input v-my-focus value="111" />
</template>

运用defineExpose子组件传父组件

子组件

<template>
    <div class="child"></div>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
function doSth() {
    console.log(333);
}
defineExpose({ doSth });
</script>
<style lang="scss" scoped>
.child {
}
</style>

父组件

<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>
<style lang="scss" scoped>
.father {
}
</style>

父组件传子组件

父组件

<template>
    <div class="father"></div>
    <Child @click="doSth"></Child>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import Child from "./Child.vue";
function doSth() {
    console.log(112);
}
</script>
<style lang="scss" scoped>
.father {
}
</style>

子组件

<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>
<style lang="scss" scoped>
.child {
}
</style>

toRefs

当从父组件向子组件传props的时分,有必要运用toRefs或者toRef进行转一下,这是为什么呢?

这儿是由于假如不运用toRefs转一次的话,当父组件中的props改动的时分,子组件假如运用了Es6的解析,会失掉响应性。

能够看下如下比如

父组件

<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>

子组件

<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>
<style lang="scss" scoped>
.child {
}
</style>

能够看到当父组件假如点击之后,由于运用const { fatherRef } = props;进行解析,就失掉了响应性

所以当父组件变成2的时分,子组件还是1。

这儿有两种解决办法

  1. 运用const { fatherRef } = toRefs(props);
  2. 在模版中中运用props.fatherRef

子组件运用v-model

能够在子组件中运用computed,完成双向绑定

父组件

<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>

子组件

<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>
<style lang="scss" scoped>
.child {
}
</style>

能够从父组件传递值和改动值的办法,然后子组件也能够运用v-model

比如中父组件传递 modelValue和update:modelValue办法
父组件:

<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>

子组件:

<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>
<style lang="scss" scoped>
.child {
}
</style>

递归组件

组件自身是能够调用组件自身的,也便是递归。
比方名为Child.vue的组件能够在其模板中用<Child/>引证它自己。这儿需求注意的是需求设置条件语句,用来中断递归,否则递归会无限递归下去。

父组件

<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>

子组件

<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>