<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          關(guān)于 Vue3 + setup + ts 使用技巧的總結(jié)

          共 6753字,需瀏覽 14分鐘

           ·

          2023-06-20 18:26

          1. 組件引入

          當(dāng)使用?setup?的時(shí)候,組件直接引入就可以了,不需要再自己手動(dòng)注冊(cè)

          <template>
          ??<Child?/>
          template>

          <script?setup?lang="ts">
          import?Child?from?"./Child.vue";
          script>

          2. ref 和 reactive

          ref?一般用于基本的數(shù)據(jù)類型,比如?string,boolean?,reactive?一般用于對(duì)象 ref 的地方其實(shí)也是調(diào)用的?reactive?實(shí)現(xiàn)的。

          <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 和 defineProps 獲取父組件傳過來值和事件

          //?第一種不帶默認(rèn)值props
          const?props?=?defineProps<{
          ??foo:?string
          ??bar?:?number
          }>()
          //?第二種帶默認(rèn)值props

          export?interface?ChildProps?{
          ??foo:?string
          ??bar?:?number
          }

          const?props?=?withDefaults(defineProps(),?{
          ???foo:?"1qsd"
          ??bar?:?3
          })

          //?第一種獲取事件
          const?emit?=?defineEmits<{
          ??(e:?'change',?id:?number):?void
          ??(e:?'update',?value:?string):?void
          }>()

          //?第二種獲取事件

          const?emit?=?defineEmits(["dosth"])

          4. 使用 useAttrs 和 useSlots

          useAttrs?可以獲取父組件傳過來的?id?、class?等值。useSlots?可以獲得插槽的內(nèi)容。例子中,我們使用?useAttrs?獲取父組件傳過來的?id?、class、useSlots?獲取插槽的內(nèi)容。

          父組件:

          <template>
          ??<div?class="father">{{?fatherRef?}}div>
          ??<Child?:fatherRef="fatherRef"?@changeVal="changeVal"?class="btn"?id="111">
          ????<template?#test1>
          ??????<div>1223div>
          ????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?v-bind="attrs">
          ????<slot?name="test1">11slot>
          ????<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>

          使用自定義指令

          在?setup?里邊自定義指令的時(shí)候,只需要遵循vNameOfDirective? 這樣的命名規(guī)范就可以了

          比如如下自定義?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>

          5. 使用 defineExpose 子組件傳父組件

          子組件

          <template>
          ??<div?class="child">div>
          template>

          <script?setup?lang="ts">
          import?{?ref,?reactive?}?from?"vue";

          function?doSth()?{
          ??console.log(333);
          }

          defineExpose({?doSth?});
          script>

          父組件

          <template>
          ??<div?class="father"?@click="doSth1">222div>
          ??<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. 父組件傳子組件

          父組件

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

          子組件

          <template>
          ??<div?class="child">2222div>
          template>

          <script?setup?lang="ts">
          import?{?ref,?reactive,?onMounted?}?from?"vue";

          const?emits?=?defineEmits(["doSth"]);

          onMounted(()?=>?{
          ??emits("doSth");
          });
          script>

          7. toRefs

          當(dāng)從父組件向子組件傳?props?的時(shí)候,必須使用?toRefs?或者?toRef?進(jìn)行轉(zhuǎn)一下,這是為什么呢?

          這里是因?yàn)槿绻皇褂?toRefs?轉(zhuǎn)一次的話,當(dāng)父組件中的?props?改變的時(shí)候,子組件如果使用了 Es6 的解析,會(huì)失去響應(yīng)性。

          可以看下如下例子

          父組件

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

          可以看到當(dāng)父組件如果點(diǎn)擊之后,因?yàn)槭褂?const { fatherRef } = props;進(jìn)行解析,就失去了響應(yīng)性

          所以當(dāng)父組件變成 2 的時(shí)候,子組件還是 1。

          這里有兩種解決辦法

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

          8. 子組件使用 v-model

          8.1 可以在子組件中使用 computed,實(shí)現(xiàn)雙向綁定

          父組件

          <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>
          ??
          ??<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 可以從父組件傳遞值和改變值的方法,然后子組件也可以使用 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>
          ??
          ??
          ??<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. 遞歸組件

          組件本身是可以調(diào)用組件自身的,也就是遞歸。vue3 中使用文件名稱自動(dòng)注冊(cè)為組件的名稱,比如名為 ?Child.vue? 的組件可以在其模板中用 ?? 引用它自己。這里需要注意的是需要設(shè)置條件語句,用來中斷遞歸,不然遞歸會(huì)無限遞歸下去。

          父組件

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

          10. vue3 ts 獲取組件 ref 實(shí)例

          • 通過ref直接拿到dom引用
          <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>

          通過對(duì)div元素添加了ref屬性,為了獲取到這個(gè)元素,我們聲明了一個(gè)與ref屬性名稱相同的變量sectionRef,然后我們通過 sectionRef.value 的形式即可獲取該div元素

          • 通過父容器的ref遍歷拿到dom引用
          <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>

          通過對(duì)父元素添加了ref屬性,并聲明了一個(gè)與ref屬性名稱相同的變量listRef,此時(shí)通過listRef.value會(huì)獲得包含子元素的dom對(duì)象 此時(shí)可以通過listRef.value.children[index]的形式獲取子元素dom

          • 通過:ref將dom引用放到數(shù)組中

            <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
            ??})

            ??const?setRefAction?=?(el:?any)?=>?{
            ??????state.refList.push(el);
            ??}
            ??
            script>

            通過:ref循環(huán)調(diào)用setRefAction方法,該方法會(huì)默認(rèn)接收一個(gè)el參數(shù),這個(gè)參數(shù)就是我們需要獲取的div元素 此時(shí)可以通過state.refList[index]的形式獲取子元素dom

          • 通過子組件emit傳遞ref

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

          通過對(duì)子組件添加了ref屬性,并聲明了一個(gè)與ref屬性名稱相同的變量cellRef,此時(shí)可以通過emit將cellRef.value作為一個(gè)dom引用傳遞出去

          • tsx 等 render 組件中獲取的方式更簡(jiǎn)單
          import?{?defineComponent,?ref,?onMounted?}?from?"@vue/runtime-core";
          import?{?ElForm?}?from?"element-plus";

          export?default?defineComponent({
          ??setup()?{
          ????const?$form?=?reftypeof?ElForm>>(null);

          ????onMounted(()?=>?{
          ??????$form.value?.validate;?//?類型正確
          ????});

          ????return?()?=>?</ElForm>;
          ??},
          });

          需要注意的是,如果使用 expose 暴露方法出去,無法獲取到對(duì)應(yīng)的類型,您需要自定義類型?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?獲取組件實(shí)例?請(qǐng)使用這個(gè)類型
          export?interface?MyFormExpose?{
          ??validate:?ELEForm["validate"];
          }

          export?default?defineComponent({
          ??name:?"MyForm",

          ??setup(props,?{?expose?})?{
          ????const?$form?=?reftypeof?ElForm>>(null);

          ????expose({
          ??????validate:?(callback)?=>?$form.value?.validate(callback),
          ????}?as?MyFormExpose);

          ????return?()?=>?</ElForm>;
          ??},
          });

          <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?=?reftypeof
          ?MyForm>?&?MyFormExpose>(null)

          ????onMounted(()?=>?{
          ???????$form.value?.validate?//?類型正確
          ????})
          ??}
          })
          script>

          參考資料

          [1]

          https://github.com/vuejs/rfcs/pull/210#issuecomment-727067392:?https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fvuejs%2Frfcs%2Fpull%2F210%23issuecomment-727067392

          瀏覽 9
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www色婷婷| 天天爽,夜爽。 | 亚洲综合天堂 | 尻屄视频大全 | 中文字幕之中文字幕 |