vue3-Composition-API實操

  • 沒有this了吠架,我要怎么獲取組件實例锋喜?
  • 沒有this了凡涩,怎么派發(fā)自定義事件棒搜?
  • 我該如何在reactiveref之間做選擇?
  • setup函數(shù)太長了怎么辦活箕?
  • 我的屬性怎么就不響應(yīng)了
  • watchEffectwatch有啥不同力麸?
  • 生命周期鉤子能不能寫多個?順序是怎樣的育韩?

我要怎么獲取組件實例克蚂?

我們都知道composition api是可以和options api一起使用、友好相處的筋讨,比如下面的示例:

const { createApp } = Vue
  createApp({
    data() {
      return {
        foo: 'foo'
      }
    },
    setup() {
      // 沒有this埃叭,我該如何獲取data中的foo和methods中的bar哪?
      return { }
    },
    methods: {
      bar() {
        console.log('我是bar方法');
      }
    },
  }).mount('#app')

但是setup里面this指向window悉罕,composition的文檔中也沒有提到怎么獲取組件實例呀游盲,這著實難住了不少小伙伴,方法自然是有的:咱們可以通過getCurrentInstance()這個接口獲取組件實例:

setup() {
  // getCurrentInstance()可以獲取組件實例
  const instance = getCurrentInstance()
  console.log(instance);
  onMounted(()=>{
    // 組件實例的上下文才是我們熟悉的this
    console.log(instance.ctx.foo); // 'foo'
    console.log(instance.ctx.bar()); // '我是bar方法'
  })
  return {}
},

但是很快我們又蒙圈了蛮粮,這個組件實例和我們以前熟悉的this不一樣益缎,直接訪問this.foo還是找不到數(shù)據(jù)。

vue3中組件實例結(jié)構(gòu)如下然想,各個選項中的this實際上是ctxproxy

當(dāng)然坑還是有的莺奔,你仔細(xì)觀察這個ctx,發(fā)現(xiàn)它不是一個Proxy對象,也就是這位兄臺只有值卻沒有響應(yīng)性令哟,所以如果要利用響應(yīng)特性恼琼,還得用proxy這個屬性返回上下文對象,如果只是想要數(shù)據(jù)屏富,上圖中不是有個data也是Proxy類型的嘛晴竞。

setup() {
  const { proxy, data } = getCurrentInstance()
  // 想要利用響應(yīng)能力,就要使用proxy這個上下文
  watchEffect(() => {
    console.log(proxy.foo) // foo變化會有輸出
    console.log(data.foo) // foo變化會有輸出
  })
},

最后大家還要注意狠半,setup()執(zhí)行的時間點是很早的噩死,甚至早于created,因此foo和bar的訪問如果沒有特意放到onMounted里面還真沒有神年。

setup() {
  const instance = getCurrentInstance()
  console.log(instance.ctx.foo); // undefined
  console.log(instance.ctx.bar()); // undefined
},

怎么派發(fā)自定義事件已维?
突然之間沒有this了之后,好像突然生活不能自理了已日,再也不能用this.$emit()派發(fā)事件了垛耳。

其實通過組件實例是可以派發(fā)事件的,比如:

setup() {
  getCurrentInstance().emit('ooxx') 
}

但是這樣比較麻煩飘千,所以我們要用到setup函數(shù)的第二個參數(shù):

setup(props, ctx) {
    ctx.emit('ooxx')
}

當(dāng)然堂鲜,還能把emit直接結(jié)構(gòu)出來使用更方便:

setup(props, { emit }) {
    emit('ooxx')
}

我該如何在reactive和ref之間做選擇?

composition-api引入了獨立的數(shù)據(jù)響應(yīng)式方法reactive护奈,它可以將傳入的對象做響應(yīng)式處理:

const state = reactive({
  foo: 'foo',
})
watchEffect(() => {
  console.log(state.foo)
})
state.foo = 'foooooo' // 輸出 'foooooo'

這個方式類似于我們設(shè)置的data選項缔莲,能夠解決我們大部分需求。但是也有以下問題:

  • 當(dāng)我們直接導(dǎo)出這個state時逆济,我們在模板中就要帶上state這個前綴
setup() {
    const state = reactive({})
    return { state }
}
<div>{{ state.foo }}</div>

為了解決這個問題又要引入toRefs

setup() {
    const state = reactive({})
    return { ...toRefs(state) }
}

<div>{{ foo }}</div>

小伙伴們又懵了,toRefs是個啥磺箕?為啥不直接展開奖慌?

  • 單個值時用reactive()顯得比較多余

于是就有了Ref的概念,通過包裝單值為Ref對象松靡,這樣就可以對其做響應(yīng)式代理

setup() {
    const foo = ref('foo')
    return { foo } 
}

模板中使用還可以省掉前綴简僧,toRefs就是利用這一點將reactive()返回代理對象的每個key對應(yīng)的值都轉(zhuǎn)換為Ref

<div>{{ foo }}</div>

但是Ref對象也有副作用:

  • 在JS中修改這個值要額外加上value
setup() {
    const foo = ref('foo')
    setTimeout(() => {
    // 額外的value讓人惱火
        foo.value++
    }, 1000)
    return { foo }
}

  • 額外增加的心智負(fù)擔(dān):一個值到底是不是Ref,我要不要加.value雕欺?
setup(props) {
    const foo = props.foo
    // foo是Proxy還是Ref岛马?  
  // 編寫`watch`方法的時候?qū)懛ㄍ耆煌?  // Ref可以被直接watch
  watch(foo, () => {})
  // Proxy則需要寫成函數(shù)形式
  watch(() => foo.bar, () => {})
}

對比之后發(fā)現(xiàn)都有一些問題,我們來討論一下兩者選擇問題:

  • 如果是單值屠列,建議ref啦逆,哪怕是個單值的對象也可以
const counterRef = ref(1)
const usersRef = ref(['tom', 'jerry'])

一個業(yè)務(wù)關(guān)注點有多個值,建議reactive

const mouse = reactive({
    x: 0,
    y: 0
})
  • 降低Ref心智負(fù)擔(dān)的方法:利用unref笛洛、isRef夏志、isProxy等工具方法,利用一些命名約定苛让。
setup(props) {
  const foo = unref(props.foo) // foo是我們要的值
  // 等效于
  const foo = isRef(props.foo) ? props.foo.value : props.foo
}

setup函數(shù)太長了怎么辦沟蔑?

雖然很好的將關(guān)注點集中起來湿诊,就像下面這樣:

但是難免還是太長了(怎么太長還成了困擾?)瘦材,此時就可以開始函數(shù)拆分

setup(){
  let { val, todos, addTodo } = useTodo()
  let {count,double,add} = useCounter() 
  let {x, double:doubleX} = useMouse()
  return {
    val, todos, addTodo,
    count,double,add,
    x,doubleX
  }
}

return的上下文太長了厅须,我們可以使用vue3的setup script功能,把setup這個配置也優(yōu)化掉食棕,一個功能export一次

<script setup>
import useCounter from './counter'
import useMouse from './mouse'
import useTodo from './todos'

let { val, todos, addTodo } = useTodo()
export {val, todos, addTodo}

let {count,double,add} = useCounter()
export {count,double,add}

let {x, double:doubleX} = useMouse()
export {x,doubleX}
</script>

我的屬性怎么就不響應(yīng)了
下面的代碼是小伙伴們可能會寫出來的:

setup({ foo, bar }) {
  watchEffect(() => {
    console.log(foo, bar) // foo朗和,bar發(fā)生變化,也不會有輸出
  })
}

props是一個Proxy對象宣蠕,直接結(jié)構(gòu)就失去了響應(yīng)能力例隆,所以對待props要溫柔,不能動不動就劈開了

setup(props) {
  watchEffect(() => {
    console.log(props.foo, props.bar) // foo抢蚀,bar發(fā)生變化镀层,會有輸出
  })
}

真想劈開也行,看你喜歡什么姿勢了

setup(props) {
  const { foo, bar } = toRefs(props)
  watchEffect(() => {
    console.log(foo, bar) // foo皿曲,bar發(fā)生變化唱逢,會有輸出
  })
}

watchEffect和watch有啥不同?

這倆方法很相似屋休,都是觀察響應(yīng)式數(shù)據(jù)坞古,變化執(zhí)行副作用函數(shù),但有如下不同:

watch需要明確指定監(jiān)視目標(biāo),

watch(() => state.counter, (val, prevVal) => {})

watchEffect不需要

watchEffect(() => {
    console.log(state.counter)
})
  • watch可以獲取變化前后的值

  • watch是懶執(zhí)行的劫樟,它等效于vue2中的this.$watch()痪枫,watchEffect為了收集依賴,要立即執(zhí)行一次

現(xiàn)在知道怎么選擇它們了吧叠艳?

生命周期鉤子能不能寫多個奶陈?
當(dāng)然可以寫多個,最后它們會按注冊順序依次執(zhí)行:

setup() {
    onMounted(() => {})
  onMounted(() => {})
  onMounted(() => {})
}

甚至還能拆分出多個相同生命周期鉤子到獨立函數(shù)中呢附较,相當(dāng)帥氣

function fun1() {
  // 這里可以用onMounted執(zhí)行代碼
    onMounted(() => {})
}
function fun2() {
  // 這里還可以用onMounted執(zhí)行代碼
    onMounted(() => {})
}
setup() {
    fun1()
  fun2()
  onMounted(() => {})
}

抄襲自掘金羊村楊老師

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吃粒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拒课,更是在濱河造成了極大的恐慌徐勃,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件早像,死亡現(xiàn)場離奇詭異僻肖,居然都是意外死亡,警方通過查閱死者的電腦和手機卢鹦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門檐涝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事谁榜》福” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵窃植,是天一觀的道長帝蒿。 經(jīng)常有香客問我,道長巷怜,這世上最難降的妖魔是什么葛超? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮延塑,結(jié)果婚禮上绣张,老公的妹妹穿的比我還像新娘。我一直安慰自己关带,他們只是感情好侥涵,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宋雏,像睡著了一般芜飘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磨总,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天嗦明,我揣著相機與錄音,去河邊找鬼蚪燕。 笑死娶牌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的馆纳。 我是一名探鬼主播诗良,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼厕诡!你這毒婦竟也來了累榜?” 一聲冷哼從身側(cè)響起营勤,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤灵嫌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后葛作,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寿羞,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年赂蠢,在試婚紗的時候發(fā)現(xiàn)自己被綠了绪穆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖玖院,靈堂內(nèi)的尸體忽然破棺而出菠红,到底是詐尸還是另有隱情,我是刑警寧澤难菌,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布试溯,位于F島的核電站,受9級特大地震影響郊酒,放射性物質(zhì)發(fā)生泄漏遇绞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一燎窘、第九天 我趴在偏房一處隱蔽的房頂上張望摹闽。 院中可真熱鬧,春花似錦褐健、人聲如沸付鹿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倘屹。三九已至,卻和暖如春慢叨,著一層夾襖步出監(jiān)牢的瞬間纽匙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工拍谐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烛缔,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓轩拨,卻偏偏與公主長得像践瓷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子亡蓉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351