最近 Vue 官方公布了 Vue 3.0 最重要的RFC:Function-based component API,并發(fā)布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前體驗(yàn) Vue 3.0 版本的 Function-based component API。筆者出于學(xué)習(xí)的目的,提前在項(xiàng)目中嘗試了vue-function-api。
專注于為中小企業(yè)提供做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)城區(qū)免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了超過(guò)千家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
筆者計(jì)劃寫(xiě)兩篇文章,本文為筆者計(jì)劃的第一篇,主要為筆者在體驗(yàn) Vue Function API 的學(xué)習(xí)心得。第二篇計(jì)劃寫(xiě)閱讀vue-function-api的核心部分代碼原理,包括setup、observable、lifecycle。
本文閱讀時(shí)間約為15~20分鐘。
概述
Vue 2.x 及以前的高階組件的組織形式或多或少都會(huì)面臨一些問(wèn)題,特別是在需要處理重復(fù)邏輯的項(xiàng)目中,一旦開(kāi)發(fā)者組織項(xiàng)目結(jié)構(gòu)組織得不好,組件代碼極有可能被人詬病為“膠水代碼”。而在 Vue 2.x 及之前的版本,解決此類問(wèn)題的辦法大致是下面的方案:
筆者維護(hù)的項(xiàng)目也需要處理大量復(fù)用邏輯,在這之前,筆者一直嘗試使用mixin的方式來(lái)實(shí)現(xiàn)組件的復(fù)用。有些問(wèn)題也一直會(huì)對(duì)開(kāi)發(fā)者和維護(hù)者造成困惑,如一個(gè)組件同時(shí)mixin多個(gè)組件,很難分清對(duì)應(yīng)的屬性或方法寫(xiě)在哪個(gè)mixin里。其次,mixin的命名空間沖突也可能造成問(wèn)題。難以保證不同的mixin不用到同一個(gè)屬性名。為此,官方團(tuán)隊(duì)提出函數(shù)式寫(xiě)法的意見(jiàn)征求稿,也就是RFC:Function-based component API。使用函數(shù)式的寫(xiě)法,可以做到更靈活地復(fù)用組件,開(kāi)發(fā)者在組織高階組件時(shí),不必在組件組織上考慮復(fù)用,可以更好地把精力集中在功能本身的開(kāi)發(fā)上。
注:本文只是筆者使用vue-function-api提前體驗(yàn) Vue Function API ,而這個(gè) API 只是 Vue 3.0 的 RFC,而并非與最終 Vue 3.x API 一致。發(fā)布后可能有不一致的地方。
在 Vue 2.x 中使用
要想提前在Vue 2.x中體驗(yàn) Vue Function API ,需要引入vue-function-api,基本引入方式如下:
import Vue from 'vue'; import { plugin as VueFunctionApiPlugin } from 'vue-function-api'; Vue.use(VueFunctionApiPlugin);
基本組件示例
先來(lái)看一個(gè)基本的例子:
count is {{ count }} plusOne is {{ plusOne }}
詳解
setup
setup函數(shù)是Vue Function API 構(gòu)建的函數(shù)式寫(xiě)法的主邏輯,當(dāng)組件被創(chuàng)建時(shí),就會(huì)被調(diào)用,函數(shù)接受兩個(gè)參數(shù),分別是父級(jí)組件傳入的props和當(dāng)前組件的上下文context。看下面這個(gè)例子,可以知道在context中可以獲取到下列屬性值:
const MyComponent = { props: { name: String }, setup(props, context) { console.log(props.name); // context.attrs // context.slots // context.refs // context.emit // context.parent // context.root } }
value & state
value函數(shù)創(chuàng)建一個(gè)包裝對(duì)象,它包含一個(gè)響應(yīng)式屬性value:
那么為何要使用value呢,因?yàn)樵贘avaScript中,基本類型并沒(méi)有引用,為了保證屬性是響應(yīng)式的,只能借助包裝對(duì)象來(lái)實(shí)現(xiàn),這樣做的好處是組件狀態(tài)會(huì)以引用的方式保存下來(lái),從而可以被在setup中調(diào)用的不同的模塊的函數(shù)以參數(shù)的形式傳遞,既能復(fù)用邏輯,又能方便地實(shí)現(xiàn)響應(yīng)式。
直接獲取包裝對(duì)象的值必須使用.value,但是,如果包裝對(duì)象作為另一個(gè)響應(yīng)式對(duì)象的屬性,Vue內(nèi)部會(huì)通過(guò)proxy來(lái)自動(dòng)展開(kāi)包裝對(duì)象。同時(shí),在模板渲染的上下文中,也會(huì)被自動(dòng)展開(kāi)。
import { state, value } from 'vue-function-api'; const MyComponent = { setup() { const count = value(0); const obj = state({ count, }); console.log(obj.count) // 作為另一個(gè)響應(yīng)式對(duì)象的屬性,會(huì)被自動(dòng)展開(kāi) obj.count++ // 作為另一個(gè)響應(yīng)式對(duì)象的屬性,會(huì)被自動(dòng)展開(kāi) count.value++ // 直接獲取響應(yīng)式對(duì)象,必須使用.value return { count, }; }, template: ``, };
如果某一個(gè)狀態(tài)不需要在不同函數(shù)中被響應(yīng)式修改,可以通過(guò)state創(chuàng)建響應(yīng)式對(duì)象,這個(gè)state創(chuàng)建的響應(yīng)式對(duì)象并不是包裝對(duì)象,不需要使用.value來(lái)取值。
watch & computed
watch和computed的基本概念與 Vue 2.x 的watch和computed一致,watch可以用于追蹤狀態(tài)變化來(lái)執(zhí)行一些后續(xù)操作,computed用于計(jì)算屬性,用于依賴屬性發(fā)生變化進(jìn)行重新計(jì)算。
computed返回一個(gè)只讀的包裝對(duì)象,和普通包裝對(duì)象一樣可以被setup函數(shù)返回,這樣就可以在模板上下文中使用computed屬性??梢越邮軆蓚€(gè)參數(shù),第一個(gè)參數(shù)返回當(dāng)前的計(jì)算屬性值,當(dāng)傳遞第二個(gè)參數(shù)時(shí),computed是可寫(xiě)的。
import { value, computed } from 'vue-function-api'; const count = value(0); const countPlusOne = computed(() => count.value + 1); console.log(countPlusOne.value); // 1 count.value++; console.log(countPlusOne.value); // 2 // 可寫(xiě)的計(jì)算屬性值 const writableComputed = computed( // read () => count.value + 1, // write val => { count.value = val - 1; }, );
watch第一個(gè)參數(shù)和computed類似,返回被監(jiān)聽(tīng)的包裝對(duì)象屬性值,不過(guò)另外需要傳遞兩個(gè)參數(shù):第二個(gè)參數(shù)是回調(diào)函數(shù),當(dāng)數(shù)據(jù)源發(fā)生變化時(shí)觸發(fā)回調(diào)函數(shù),第三個(gè)參數(shù)是options。其默認(rèn)行為與 Vue 2.x 有所不同:
// double 是一個(gè)計(jì)算包裝對(duì)象 const double = computed(() => count.value * 2); watch(double, value => { console.log('double the count is: ', value); }); // -> double the count is: 0 count.value++; // -> double the count is: 2
當(dāng)watch多個(gè)被包裝對(duì)象屬性時(shí),參數(shù)均可以通過(guò)數(shù)組的方式進(jìn)行傳遞,同時(shí),與 Vue 2.x 的vm.$watch一樣,watch返回取消監(jiān)聽(tīng)的函數(shù):
const stop = watch( [valueA, () => valueB.value], ([a, b], [prevA, prevB]) => { console.log(`a is: ${a}`); console.log(`b is: $`); } ); stop();
注意:在RFC:Function-based component API初稿中,有提到effect-cleanup,是用于清理一些特殊情況的副作用的,目前已經(jīng)在提案中被取消了。
生命周期
所有現(xiàn)有的生命周期都有對(duì)應(yīng)的鉤子函數(shù),通過(guò)onXXX的形式創(chuàng)建,但有一點(diǎn)不同的是,destoryed鉤子函數(shù)需要使用unmounted代替:
import { onMounted, onUpdated, onUnmounted } from 'vue-function-api'; const MyComponent = { setup() { onMounted(() => { console.log('mounted!'); }); onUpdated(() => { console.log('updated!'); }); // destroyed 調(diào)整為 unmounted onUnmounted(() => { console.log('unmounted!'); }); }, };
一些思考
上面的詳解部分,主要抽取的是 Vue Function API 的常見(jiàn)部分,并非RFC:Function-based component API的全部,例如其中的依賴注入,TypeScript類型推導(dǎo)等優(yōu)勢(shì),在這里,由于篇幅有限,想要了解更多的朋友,可以點(diǎn)開(kāi)RFC:Function-based component API查看。個(gè)人也在Function-based component API討論區(qū)看到了更多地一些意見(jiàn):
setup() { const state = reactive({ count: 0, }); const double = computed(() => state.count * 2); function increment() { state.count++; } return { ...toBindings(state), // retains reactivity on mutations made to `state` double, increment, }; }
下一篇文章中,筆者將閱讀vue-function-api的核心部分代碼原理,包括setup、observable、lifecycle等,從內(nèi)部探索 Vue Function API 可能帶給我們的改變。
當(dāng)然,目前 Vue Function API 還處在討論階段,Vue 3.0 還處在開(kāi)發(fā)階段,還是期待下半年 Vue 3.0 的初版問(wèn)世吧,希望能給我們帶來(lái)更多的驚喜。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。