Vue3.2一篇進階

什么是setup?

老版本:

<script lang="ts">
import { defineComponent,ref, watch } from 'vue';
import { Upload } from 'ant-design-vue';
export default defineComponent({
  name: 'good-card',
  components: { Upload },
  props: {
    name: {
      default: '',
      type: String,
      required: true,
    }
  },
  emits: ['changeCount'],
  setup: (props, context) => { // 傳入props 以及上下文context
    const count = ref(0)
    const addCount = (item) => {
      count.value++
      context.emit('changeCount', item)
    }
    function reset = ()=>{count.value = 0}
    context.expose({reset}) // 向外部組件暴露部分方法
    watch(
     () => props.name,
     (newVal, oldVal) => {
       console.log(newVal, oldVal)
     })
    return { count, addCount }
  },
})
</script>

你用的這個說明你Out啦,快試試下面這個方式吧!

一、如何使用setup語法糖

<script lang="ts" setup>
console.log('直接寫入script標簽')
</script>

上面的代碼可精簡為

<script lang="ts" setup>
import { ref } from 'vue';
import { Upload } from 'ant-design-vue';
const props = defineProps({
  name: {
    type: String,
    default: '',
  },
});
function reset = ()=>{count.value = 0}
const $emits = defineEmits(['changeCount']);
const count = ref(0)
const addCount = (item) => {
    count.value++
    $emits('changeCount', item)
}
watch(
   () => props.name,
   (newVal, oldVal) => {
     console.log(newVal, oldVal)
 })
defineExpose({ reset }); // 向外部組件暴露部分方法

const post = await fetch(`/api/xxx`).then(res => xxx) // 有await會自動給setup 前面加上async
</script>

setup是Vue3.0后推出的語法糖,并且在Vue3.2版本進行了大更新,像寫普通JS一樣寫vue組件,對于開發(fā)者更加友好了;按需引入computed、watch、directive等選項,一個業(yè)務邏輯可以集中編寫在一起,讓代碼更加簡潔便于瀏覽。在這個環(huán)境下,所有定義的變量,函數(shù),computed等等會數(shù)據(jù)自動return,頁面Html中可以直接使用,且script 標簽中引入的組件自動注冊 可以使用頂層 await。結果代碼會被編譯成 async setup()。

ps: 新版本defineProps, defineExpose, defineEmit,watch,computed,watchEffect 等都是內(nèi)置的,無需再import,可直接省略

警報!重點來了~ data響應是Vue 核心,千萬理解透喔~

二、定義data - Ref

<template>
    <div class="home-container">
        <p>姓名: {{ girl.name }}</p>
        <p>年齡: {{ girl.age }}</p>
        <p>身高: {{ girl.height }}</p>
        <p>
            科目: <span v-for="(item, index) in subjects" :key="item + index">{{ item }}</span>
        </p>
        <p>考試結果是: {{ score }}</p>
    </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
// 推薦定義無嵌套的類型  簡單理解: 除了對象以及內(nèi)含對象的所有類型
// 原理 靠Object.defineProperty()的get與set完成響應式
const score = ref(99);
score.value++; // 改值是.value 賦值

// 如果你用了對象呢?
const girl = ref({ name: '小紅', height: 170, age: 17 }); // 等同于 reactive({value: {name: '小紅', height: 170, age: 17}})
girl.value.age++; // 改值都要.value,顯得很冗余

// 數(shù)組是復雜類型,如果沒有嵌套,依然建議用ref
const subjects = ref(['語文', '數(shù)學']);
setTimeout(() => {
  subjects.value = ['語文', '英語'];
  subjects.value[1] = '美術';
}, 0);
</script>

三、定義data - Reactive

<template>
    <div class="home-container">
        <p>姓名: {{ girl.name }}</p>
        <p>年齡: {{ girl.age }}</p>
        <p>身高: {{ girl.height }}</p>
        <p>
            科目: <span v-for="(item, index) in girl.subjects" :key="item + index">{{ item }}</span>
        </p>
        <p>考試結果是: {{ girl.score }}</p>
    </div>
</template>

<script lang="ts" setup>
import { ref, reactive } from 'vue';
// 對象以及內(nèi)含對象的類型
// 原理 Proxy代理 可監(jiān)聽到對象的屬性值變化,以及key的增刪
const girl = reactive({
    name: '小紅',
    height: 170,
    age: 17,
    score: 99,
    subjects: ['語文', '數(shù)學']
});
girl.age++; // 改值是點出對應的key 賦值,切記 不能整體賦值,如 girl = {}  這樣的會丟失響應式
girl.score++;
girl.subjects = ['語文', '英語'];
</script>

問: Reactive 把數(shù)據(jù)聚合在一起,用它定義所有數(shù)據(jù)不就行了,Ref棄了呀,還要.value修改,多麻煩?
答: 不行
理由1、vue2 的data 形式就類似這樣,把所有的定義數(shù)據(jù)都放在一個data,別忘了,vue3改造composition API 的初衷就是想要把數(shù)據(jù)和邏輯整合在一起,一塊代碼一個功能。方便寫功能時代碼優(yōu)雅組織在一起,還便于未來用hook函數(shù)可以提取出去給其它頁面復用。
理由2、reactive 推薦場景是對象中數(shù)據(jù)是有關聯(lián)的一組,比如我們這里girl對象,如果還有比如form 對象表單相關屬性,應該另外定義而不是全雜合在一起。
理由3、reactive 定義的對象,尤其要注意不能整體賦值,比如girl = {xxx} 這樣會丟失響應式,即使是賦值girl = reactive({xxx}),如下,試驗,記得改girl 的const 為let

setTimeout(() => {
    girl = reactive({ ...girl, subjects: ['語文', '歷史'] });
}, 0);

不可行,原理解析: girl = reactive({XXX}) 中girl 被賦值為新的地址,就如同a = 1 變成了 a = 2,但是a 本身不是任何響應式對象的某個屬性

  • 網(wǎng)上解決方案1 改為ref
    這里有個隱藏的知識點,ref 賦值對象時,會借用reactive 包裝成為一個reactive,這時候的響應式是靠的proxy,賦值非對象類型時,則靠Object.defineProperty()的get與set完成響應式
    因此這里girl = ref({XXX}) 等同于 girl = reactive({value: {XXX}})
    你重新賦值 girl.value = {XXX} 觸發(fā)了proxy代理的響應式對象 {value: {XXX}} 的監(jiān)聽
    但我們上面說過,對象及含內(nèi)嵌對象的不推薦放到ref,這么做不優(yōu)雅 Pass
  • 網(wǎng)上解決方案2 嵌套
    定義對象的時候我們這樣嵌套 let baseData = reactive({girl: {XXX}})
    想要整體賦值修改girl 的時候,那就是baseData.girl = {XXX}
    這樣賦值觸發(fā)了 proxy代理的響應式對象{girl: {XXX}} 的屬性監(jiān)聽,當然可行
    但如果我們都這樣去處理,多了一個baseData層級,reactive 本身一個優(yōu)勢就是為了少去.value,而且再來個boy? man?
    那你可能想把 boy,man,woman都放進baseData里去,這又變得和vue2一樣,違背了composition API的初衷 Pass
    上述兩種方案都可行,但冗余不優(yōu)雅,改動量大
  • 最佳解決方案3
    我們無非是想要觸發(fā)girl = reactive({XXX}) 中 {XXX} proxy 代理的對象的監(jiān)聽,我們可以用Object.assign
    Object.assign({...girl, {subjects: ['語文', '歷史']}})
setTimeout(() => {
    Object.assign(girl, { subjects: ['語文', '歷史'] });
    // 可行,簡潔優(yōu)雅~  
    // 可別{ ...girl },人家是proxy代理過的,你解構就丟響應式了
}, 0);

問: 值得注意的是不管是用Ref 還是 Reactive,都用const定義為什么呢?
Ref 是賦值我們知道其實對其.value 屬性在賦值,本身指向的地址沒有變,而Reactive 是把一個對象處理成為proxy代理的響應式對象,應想著觸發(fā)對象的變化,而不是把這個girl賦值為即使是另一個新的reactive對象。我們用const 既符合實際,也避免了犯錯~

四、定義data - toRefs

上面我們論證了使用Reactive的必要和場景,以及怎么觸發(fā)它的修改,但是聰明的你一定發(fā)現(xiàn)在模板里使用并不簡潔,Reactive Html中會多一個層級,如果對象復雜,可能更麻煩。但你會發(fā)現(xiàn)Ref 不需要.value,我們打印一下Ref對象,可以發(fā)現(xiàn)有v_isRef 屬性,為true,系統(tǒng)會自動使用.value的值。那我們只要把Reactive 中的屬性值來一遍,加上這個屬性變成ref,這 就是toRefs。
ps: 其實就是把Reactive中的每一項都變成了Ref,而這個對象自身又是通過proxy代理監(jiān)聽實現(xiàn)響應式的

在setup 環(huán)境下,我們只要如下

<script lang="ts" setup>
import { toRefs, ref, reactive } from 'vue';
const { name, height, age, score, subjects } = toRefs(girl);
</script>

模板里就可以拿掉girl了,但命名記得唯一,按照我們使用reactive 的場景,最佳的是命名為girl_name這樣的形式

<template>
    <div class="home-container">
        <p>姓名: {{ name }}</p>
        <p>年齡: {{ age }}</p>
        <p>身高: {{ height }}</p>
        <p>
            科目: <span v-for="(item, index) in subjects" :key="item + index">{{ item }}</span>
        </p>
        <p>考試結果是: {{ score }}</p>
    </div>
</template>

貓咪

喝杯茶歇會
吃透上面的內(nèi)容,那就開啟輕松旅程了呀~

五、method方法

<script lang="ts" setup>
function add(){}
</script>

setup環(huán)境自動return ,只管定義,當然如果想暴露給父組件用上面提到的的expose,記得功能相關的放在一起更優(yōu)雅

六、watch、watchEffect、computed

都是自動return
computed 計算屬性,vue2一樣,寫法如下

<template>
     <p>考試結果是: {{ result }}</p>
</template>
<script lang="ts" setup>
import { ref } from "vue";
const score1 = ref(95);
const score2 = ref(92);
const result = computed(() => {
    return score1.value + score2.value;
});
setTimeout(() => {
    score2.value++;
}, 0);
</script>

watch和watchEffect

<script lang="ts" setup>
// 特定響應式對象監(jiān)聽 可以獲取新舊值
watch(
  searchInput,
 (newVal, oldVal) => {
    console.log("watch searchInput:", newVal, oldVal);
  },
  {
    immediate: true, // 可選, 立即監(jiān)聽,初始值也會執(zhí)行
    deep: true // 可選, 嵌套復雜類型,深度監(jiān)聽
  });

// 多響應式對象監(jiān)聽
watch(
  [firstName,lastName],
 ([newFirst,newLast], [oldFirst,oldlast]) => {
   // .....
  });

// 所有依賴響應式對象監(jiān)聽,無舊值
const result = ref(0);
watchEffect(() => {
   result.value = score1.value + score2.value + 12;
}); // 有點像計算屬性,注重執(zhí)行,無需return,computed注重結果,需要return
</script>

ps: watch,computed,watchEffect,在setup環(huán)境下都可直接使用,無需import

相信你對Vue3基操練習的比較到位了,感謝支持,會繼續(xù)更新,點贊支持喔~

還有精力,再來一發(fā)?年輕人, 別太肝啊, 喝點水,OK,來繼續(xù)
Vue3.2 組件間通信

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容