vue3之setup的使用理解

  1. vue3中的setup有什么用?
    setup的設(shè)計(jì)是為了使用組合式api
  2. 為什么不用之前的組件的選項(xiàng)
    data、computed、methods谎替、watch 組織邏輯在大多數(shù)情況下都有效。然而哄孤,當(dāng)我們的組件變得更大時(shí),邏輯關(guān)注點(diǎn)的列表也會(huì)增長(zhǎng)吹截。這可能會(huì)導(dǎo)致組件難以閱讀和理解瘦陈,尤其是對(duì)于那些一開始就沒(méi)有編寫這些組件的人來(lái)說(shuō)。而通過(guò)setup可以將該部分抽離成函數(shù),讓其他開發(fā)者就不用關(guān)心該部分邏輯了.
  3. setup的在vue生命周期的位置
    setup位于created 和beforeCreated只前,用于代替created 和beforeCreated,但是在setup函數(shù)里不能訪問(wèn)到this,另外setup內(nèi)可以通過(guò)以下hook操作整個(gè)生命周期
    onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onErrorCaptured,onRenderTracked,onRenderTriggered
  4. setup可以接收哪些參數(shù)?
    setup可接受props,context,其中props由于是響應(yīng)式數(shù)據(jù),不能直接解構(gòu)賦值,context不是響應(yīng)式數(shù)據(jù),可以直接解構(gòu)賦值;setup必須返回一個(gè)對(duì)象,一旦return,就可以像vue2.x的方式使用該屬性
props:['test']
setup(props,context){
//const {test} = props //錯(cuò)
const {test} = toRefs(props) //對(duì)
const { attrs, slots, emit }= context //對(duì)
  return {
    test
  }
}
  1. 優(yōu)先級(jí),如果data,props,setup都有一個(gè)同名屬性,setup返回的該屬性優(yōu)先級(jí)最高,以執(zhí)行以下代碼為例,將顯示:test from son's setup
//father.vue
...
<custField :test="test" />
setup(){
  const test = ref('test from father')
  return{
    test
  }
}
...

//son.vue
<template>
  <div class="custField">
    優(yōu)先級(jí)測(cè)試
    <h1>{{ test }}</h1>
  </div>
</template>

<script>
import { toRefs } from "vue";
export default {
  props: ["test"],
  data() {
    return {
      test: "test from son's data",
    };
  },
  setup(props) {
    let test = toRefs(props);
    test = "test from son's setup";
    return { test };
  },
};
</script>

執(zhí)行結(jié)果如下


結(jié)果
  1. 如上代碼所示,若要在setup內(nèi)執(zhí)行ref,toRefs,toRef,computed,watch,watchEffect等函數(shù),需要通過(guò)import的方式從vue中引入后才能使用,eg:import { toRefs, ref, onMounted, nextTick } from "vue";
  2. 如何在setup中拿到ref對(duì)應(yīng)的子組件,并執(zhí)行其的函數(shù),場(chǎng)景如下:使用antd的form表單的驗(yàn)證,在vue2.x方案時(shí)可以在methods中通過(guò)this時(shí)需要使用this.$refs.ruleForm.validate(),而在setup中拿不到this,應(yīng)該從{ref}入手,看下面代碼
//...
 <a-form
    ref="ruleForm"
    :model="form"
    :rules="rules"
  >
    <a-form-item ref="name" label="Activity name" name="name">
      <a-input v-model:value="form.name" />
    </a-form-item>
    <a-form-item :wrapper-col="{ span: 14, offset: 4 }">
      <a-button type="primary" @click="onSubmit"> 驗(yàn)證</a-button>
      <a-button style="margin-left: 10px" @click="resetForm"> 重置</a-button>
    </a-form-item>
</a-form>
//...vue2.x
 methods: {
    onSubmit() {
      this.$refs.ruleForm
        .validate()
        .then(() => {
          console.log('values', this.form);
        })
        .catch(error => {
          console.log('error', error);
        });
    },
    resetForm() {
      this.$refs.ruleForm.resetFields();
    },
  },

//..vue3
setup(){
  //1.設(shè)置一個(gè) <a-form
  // ref="ruleForm"
  //  :model="form"
  // :rules="rules"
  //> ref同名屬性,并使用ref(null)包裝
  const ruleForm=ref(null)//通過(guò)ref或reactive包裹起來(lái)讓其成為響應(yīng)式數(shù)據(jù)
  //2.一旦后面return {ruleForm},vue3會(huì)自動(dòng)綁定ref="ruleForm"的組件
  //設(shè)定方法,但是要通過(guò)ruleForm.value才能拿到組件
  const onSubmit=()=>{
    ruleForm.value//通過(guò)ref包裹的數(shù)據(jù)需要使用.value來(lái)取得相應(yīng)的值
        .validate()//,而reactive包裹的數(shù)據(jù)不需要通過(guò).value來(lái)取得相應(yīng)的值
        .then(() => {
          console.log("values", form);
        })
        .catch((error) => {
          console.log("error", error);
        });
  }
  const resetForm = () => {
      console.log("resetForm");
      ruleForm.value.resetFields();
  };
  //3.setup必須返回一個(gè)對(duì)象,把vue在生命周期需要調(diào)用的方法,屬性暴露出去
  return  {
    ruleForm,//Q:為什么上面要用.value的形式,A:這里會(huì)自動(dòng)解綁
    onSubmit,
    resetForm 
  }
}
  1. 目前"ant-design-vue": "^2.0.0-rc.8",與"vue": "^3.0.0",在使用a-form里的submit事件時(shí),若需要校驗(yàn),會(huì)使得校驗(yàn)無(wú)論如何都不能走then,只能走catch,需要將a-form的submit事件改為按鈕執(zhí)行方法
//以下代碼上述版本會(huì)導(dǎo)致點(diǎn)擊提交表單時(shí)數(shù)據(jù)驗(yàn)證通不過(guò)
<a-form
    ref="ruleFormRef"
    :model="formData"
    :rules="rules"
    :label-col="labelCol"
    :wrapper-col="wrapperCol"
    @submit='onSubmit'
  >
  //....
  <a-form-item :wrapper-col="{ span: 14, offset: 4 }">
      <a-button type="primary" html-type='submit'> Create </a-button>
  </a-form-item>
</a-form>
//...
setup(){
  const ruleFormRef = ref(null)
  const onSubmit= () => {
        ruleFormRef.value
          .validate()
          .then(() => {
            console.log('sucess') //上述版本無(wú)論如何都不會(huì)執(zhí)行then,只會(huì)走catch
          })
          .catch((error) => {
            console.log("error", error);
          });
      }
  return{
    onSubmit
  }
}
  1. 截止2021/1/14,最新版vite與最新版antd有沖突,得改成vuecli;
    沖突版本:vite"vite": "^1.0.0-rc.1"與"ant-design-vue": "^2.0.0-rc.8",
  2. 如何調(diào)用子組件內(nèi)setup內(nèi)的方法?
    i. 子組件在setup寫好方法method,并通過(guò)return暴露出去
    ii. 父組件調(diào)用子組件時(shí)為其添加ref屬性
    iii. 父組件setup內(nèi)拿到ii添加的ref屬性property,再通過(guò)property.value.method()調(diào)用
    子組件
<template>
  // 渲染從父級(jí)接受到的值
  <div>Son: {{ valueRef }}</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
  name: 'Son',
  setup() {
    const valueRef = ref('')    
    // 該函數(shù)可以接受父級(jí)傳遞一個(gè)參數(shù)波俄,并修改valueRef的值
    const acceptValue = (value: string) => (valueRef.value = value)
    return {
      acceptValue,
      valueRef
    }
  }
})
</script>

父組件

<template>
  <div>sonRef</div>
  <button @click="sendValue">send</button>
  // 這里ref接受的字符串晨逝,要setup返回的ref類型的變量同名
  <Son ref="sonRef" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Son from '@/components/Son.vue'
export default defineComponent({
  name: 'Demo',
  components: {
    Son
  },
  setup() {
    // 如果ref初始值是一個(gè)空,可以用于接受一個(gè)實(shí)例
    // vue3中獲取實(shí)例的方式和vue2略有不同
    const sonRef = ref()
    const sendValue = () => {
      // 可以拿到son組件實(shí)例懦铺,并調(diào)用其setup返回的所有信息
      console.log(sonRef.value)      
      // 通過(guò)調(diào)用son組件實(shí)例的方法捉貌,向其傳遞數(shù)據(jù)
      sonRef.value.acceptValue('123456')
    }
    return {
      sonRef,
      sendValue
    }
  }
})
</script>
  1. defineComponent是便于typescript推斷類型的組件構(gòu)造函數(shù),可以傳入name,data,setup,methods等參數(shù),如果只有setup,則可以直接傳入setup方法;但是注意:若要在setup中使用props屬性,props為必輸,所以方法二訪問(wèn)不到props屬性
//方法一,傳入其他屬性
export default defineComponent({
  name:'xxx',
  props:['aaa']//若要在setup中使用props,必輸props
  setup(props,context){
    const aaa = ref(aaa)
    return {aaa}
  }
})
//方法二,直接傳入setup函數(shù)
export default defineComponent((props,context)=>{
  //...你的代碼 ,但是注意這里props拿不到實(shí)際數(shù)據(jù)
})
  1. vue3如何setup函數(shù)如何實(shí)現(xiàn)多屬性監(jiān)聽,如何實(shí)現(xiàn)深度監(jiān)聽?
    i. 引入watch,watch最后返回unwatch方法,在調(diào)用該方法將停止監(jiān)聽
    ii. watch傳入數(shù)組,注意,監(jiān)聽的是普通類型可直接輸入,若是引用類型,則需要輸入函數(shù)返回的值,例如要想同時(shí)監(jiān)聽data.form.c.c1屬性和ddd屬性
    iii. 對(duì)于watch第三個(gè)傳參deepimmediate都不陌生,而flush的作用是決定callback的執(zhí)行時(shí)機(jī),有三個(gè)選項(xiàng),pre(默認(rèn)),post,sync,分別對(duì)應(yīng)watch在組件更新前,后,時(shí)執(zhí)行callback.
const ddd = ref("wwww");
const data = reactive({
      form: {
        a: 1,
        b: 2,
        c: {
          c1: "c1",
          c2: "c2",
        },
      },
      haha: "haha",
    });
const unwatch = watch(
    [ddd, () => data.form.c.c1],//傳入數(shù)組
    (newValue, oldValue) => {//結(jié)構(gòu)的也是數(shù)組,
    //也可以寫成([nowddd,nowC1],[preddd,preC1])=>{...}
      console.log(`new--->${newValue}`);
      console.log(`old--->${oldValue}`);
      console.log(newValue[0]);
      console.log(newValue);
    },
    { deep: true }//第三個(gè)參數(shù)傳入deep,immediate,flush屬性
);
    setTimeout(() => {
      ddd.value = "eee";
    }, 1000);
    setTimeout(() => {
      data.form.c.c1 = "2222";
      setTimeout(() => {
        unwatch();//這里異步使用unwatch方法,后面的ddd.value = "ffff"將不被監(jiān)聽
      });
    }, 2000);
    setTimeout(() => {
      ddd.value = "ffff";
    }, 3000);
  1. vue3的watchEffect有什么用?
    i. 它是一個(gè)與偵聽器,作用和watch差不多,但是不能拿到newValueoldValue,下面是它的定義,傳參effect函數(shù)option對(duì)象,effect函數(shù)又可傳入onInvalidate函數(shù),option對(duì)象可傳入flush,onTrack,onTrigger,flush與watch的flush相同,onTrack,onTrigger又可傳入DebuggerEvent 函數(shù)用于開發(fā)調(diào)試,返回與watch相同返回一個(gè)停止偵聽的函數(shù)
function watchEffect(
  effect: (onInvalidate: InvalidateCbRegistrator) => void,
  options?: WatchEffectOptions
): StopHandle;
interface WatchEffectOptions {
  flush?: "pre" | "post" | "sync";
  onTrack?: (event: DebuggerEvent) => void;
  onTrigger?: (event: DebuggerEvent) => void;
}
interface DebuggerEvent {
  effect: ReactiveEffect;
  target: any;
  type: OperationTypes;
  key: string | symbol | undefined;
}
type InvalidateCbRegistrator = (invalidate: () => void) => void;
type StopHandle = () => void;

ii. 傳參的effect函數(shù)會(huì)在組件beforeCreate之前就執(zhí)行一次,若該函數(shù)里使用到了某些數(shù)據(jù),將監(jiān)聽該數(shù)據(jù),當(dāng)監(jiān)聽的數(shù)據(jù)發(fā)生變化時(shí)就會(huì)(若watchEffect傳入了onInvalidate函數(shù),則會(huì)先執(zhí)行onInvalidate函數(shù)后)再次執(zhí)行effect函數(shù).

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <h4>{{ count }}</h4>
    <h4>{{ test }}</h4>
    <button @click="jump">jump</button>
  </div>
</template>
<script lang='ts'>
import { onMounted, ref, watchEffect, onBeforeMount } from "vue";
import { useRoute, useRouter } from "vue-router";
const fetchData = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("success");
    }, 1000);
  });
};
export default {
  setup() {
    const test = ref("test");
    const route = useRoute();
    const router = useRouter();
    const count = ref(0);
    const effect = async (onInvalidate) => {
      console.log('監(jiān)聽route'+route.query);
      onInvalidate(() => {
        console.log("執(zhí)行onInvalidate");
      });
      const res = await fetchData();
      console.log(res);
      test.value = res;
    };
    onBeforeMount(() => {
      console.log("onBeforeMount");
    });
    onMounted(() => {
      console.log("onmounted");
    });
    const unWachEffect = watchEffect(effect);
    useRoute();
    setTimeout(() => {
      console.log("5秒時(shí)間后注銷WachEffect");
      unWachEffect();
    }, 5000);
    setInterval(() => count.value++, 1000);//每一秒count自加1,因?yàn)閣atchEffect帶有該參數(shù),所以改變時(shí)會(huì)自動(dòng)觸發(fā)
    const jump = () => {
      router.push(`?time=${new Date().getTime()}`);
    };
    return { count, jump, test };
  },
  beforeCreate() {
    console.log("beforeCreate");
  },
};
</script>

iii. onInvalidate函數(shù)的執(zhí)行時(shí)機(jī),
(1). effect里的值改變時(shí),會(huì)先于內(nèi)部函數(shù)執(zhí)行
(2). 偵聽器被停止(組件unMounted也會(huì)關(guān)閉偵聽器)

  1. 如何使用向vue2那樣讀取route和使用router?
    使用import { useRoute, useRouter } from "vue-router";如上面示例代碼所示.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子趁窃,更是在濱河造成了極大的恐慌牧挣,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醒陆,死亡現(xiàn)場(chǎng)離奇詭異瀑构,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)刨摩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門寺晌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人澡刹,你說(shuō)我怎么就攤上這事呻征。” “怎么了罢浇?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵陆赋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我己莺,道長(zhǎng)奏甫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任凌受,我火速辦了婚禮,結(jié)果婚禮上思杯,老公的妹妹穿的比我還像新娘胜蛉。我一直安慰自己,他們只是感情好色乾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布誊册。 她就那樣靜靜地躺著,像睡著了一般暖璧。 火紅的嫁衣襯著肌膚如雪案怯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天澎办,我揣著相機(jī)與錄音嘲碱,去河邊找鬼。 笑死局蚀,一個(gè)胖子當(dāng)著我的面吹牛麦锯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播琅绅,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼扶欣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起料祠,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骆捧,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后髓绽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凑懂,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年梧宫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了接谨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡塘匣,死狀恐怖脓豪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忌卤,我是刑警寧澤扫夜,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站驰徊,受9級(jí)特大地震影響笤闯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棍厂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一颗味、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧牺弹,春花似錦浦马、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至航攒,卻和暖如春磺陡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漠畜。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工币他, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盆驹。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓圆丹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親躯喇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辫封,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容