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

          @vue/composition-api 與 Vue3 的前生今世

          共 8855字,需瀏覽 18分鐘

           ·

          2021-10-11 09:47

          通過本文你將會(huì) GET

          1. compositions-api 的誕生背景
          2. @vue/composition-api 和 vue3 的‘姻緣’
          3. @vue/composition-api 實(shí)現(xiàn)原理
          4. @vue/composition-api 的優(yōu)勢與劣勢

          Why @vue/compositions-api?

          首先,來區(qū)分一下 compositions-api@vue/compositions-api 這兩個(gè)東東。

          compositions-api(組合式 API) 是 Vue3 提出的一個(gè)新的 Vue 概念(語法)。

          @vue/compositions-api 是 Vue2 的一個(gè)插件,需通過 Vue.use() 進(jìn)行調(diào)用。

          為什么會(huì)有 compositions-api

          根據(jù)官方文檔描述:

          ?

          composition-api-rfc[1]組合式 API: 一組低侵入式的、函數(shù)式的 API,使得我們能夠更靈活地「組合」組件的邏輯。

          ?

          好處是:

          1. 更好的邏輯復(fù)用與代碼組織
          2. 更好的類型推導(dǎo)

          相同組件邏輯下,原來的 options 形式實(shí)現(xiàn)與新的 composition-api 實(shí)現(xiàn)代碼結(jié)構(gòu)對(duì)比:

          代碼對(duì)比

          為什么會(huì)有 @vue/compositions-api

          為了抹平 compositions-api 語法和 Vue2 的 gap,或者說為了讓 Vue2 項(xiàng)目也能體驗(yàn)到 compositions-api 帶來的便利和快感, Vue團(tuán)隊(duì)提供了 @vue/compositions-api 插件的解決方案進(jìn)行處理。

          因此在 Vue2 項(xiàng)目中你也可以歡快的使用 compositions-api 語法(當(dāng)然了由于實(shí)現(xiàn)原理的差異,某些語法功能支持并不友好)。

          @vue/composition-api 和 vue3 的‘姻緣’

          @vue/composition-api 插件與 Vue3 一樣,都是誕生于 2019 年,也就是 在 Vue3 提出來的基于 Proxy 實(shí)現(xiàn)的時(shí)候,Vue團(tuán)隊(duì)就已經(jīng)考慮到利用 @vue/composition-api 插件,來抹平瀏覽器的兼容性問題了。

          并且上篇文章也已經(jīng)提到,為什么會(huì)有 vue2 + @vue/composition-api 這種產(chǎn)物,直接用 Vue3 不香嗎,主要的原因還是 Vue3 的兼容性問題(各大瀏覽器廠商對(duì)Proxy的支持還沒普及)。

          那么 vue2 + @vue/composition-api 到底是個(gè)什么東東呢,怎么用呢?

          簡單用法如下:

          1. 在 vue2 項(xiàng)目中安裝

          npm install @vue/composition-api

          1. 在使用 @vue/composition-api 前,必須先通過 Vue.use() 進(jìn)行安裝。之后才可使用新的 組合式 API 進(jìn)行組件開發(fā)。
          import?Vue?from?'vue'
          import?VueCompositionAPI?from?'@vue/composition-api'

          Vue.use(VueCompositionAPI)
          //?使用?API
          import?{?ref,?reactive?}?from?'@vue/composition-api'
          //?而在?vue3?中?
          //?直接?import?{?ref,?reactive?}?from?'vue'?即可,?
          //?不需要引入插件,并單獨(dú)從?'@vue/composition-api'?解構(gòu) api
          ?

          ?? 當(dāng)遷移到 Vue 3 時(shí),只需簡單的將 @vue/composition-api 替換成 vue 即可。現(xiàn)有的代碼幾乎無需進(jìn)行額外的改動(dòng)。

          ?
          1. 你可以盡情的享受 composition-api 帶來的快感了



          @vue/composition-api 部分實(shí)現(xiàn)原理

          這里我們主要介紹,基于 Vue2 @vue/composition-api 的一些實(shí)現(xiàn)原理(基于 Vue3 composition-api實(shí)現(xiàn)后面單獨(dú)篇幅進(jìn)行討論)。

          源碼整體結(jié)構(gòu)如下圖(index 入口文件)

          源碼整體結(jié)構(gòu)

          可以看出來,默認(rèn)導(dǎo)出是 install 函數(shù),用于 Vue.use 進(jìn)行插件安裝, 其他的都是一些具體的 composition-api 的功能函數(shù)。

          那么,為了有側(cè)重點(diǎn),下面我們主要圍繞幾個(gè)問題進(jìn)行重點(diǎn)討論

          1. 來一看 install 主要干了什么?
          2. setup 中為什么可以隨意使用 composition-api,并脫離了 this?
          3. 基于 vue2 的 reactive / ref 是怎么實(shí)現(xiàn)的?

          首先,一起來剖析一下 install 函數(shù)


          //?install(Vue,?mixin)

          export?function?install(
          ??Vue:?VueConstructor,
          ??_install:?(Vue:?VueConstructor)?=>?void
          )?
          {
          ??//?這里去掉了?dev?調(diào)試模式的邏輯
          ??if?(currentVue?&&?currentVue?===?Vue)?{
          ????return
          ??}
          ??//?你可能會(huì)困惑 Vue.config.optionMergeStrategies 這個(gè)是什么東東?
          ??//?vue2.6?源碼中你可以找到答案?
          ??//?vue/src/core/util/options.js
          ??//?Option?overwriting?strategies?are?functions?that?handle
          ??//?how?to?merge?a?parent?option?value?and?a?child?option
          ??//?value?into?the?final?value.
          ??//?
          ??Vue.config.optionMergeStrategies.setup?=?function?(
          ????parent:?Function,
          ????child:?Function
          ??
          )?
          {
          ????//?mergeData?函數(shù)在?vue2.6?源碼中同樣存在
          ????//?mergeData?-?recursively?merges?two?data?objects?together.
          ????//?
          ????return?function?mergedSetupFn(props:?any,?context:?any)?{
          ??????return?mergeData(
          ????????typeof?parent?===?'function'???parent(props,?context)?||?{}?:?undefined,
          ????????typeof?child?===?'function'???child(props,?context)?||?{}?:?undefined
          ??????)
          ????}
          ??}
          ??//?設(shè)置全劇唯一?currentVue?實(shí)例
          ??setCurrentVue(Vue)
          ??//?注冊(cè)安裝到?Vue,@vue/composition-api?最核心邏輯
          ??_install(Vue)
          }

          下面來看看 ?_install(Vue) 到底干了什么, 也就是 mixin 函數(shù)

          export?function?mixin(Vue:?VueConstructor)?{
          ??//?可以看出核心邏輯?就是通過?Vue.mixin?并結(jié)合?hooks?
          ??//?混入一些初始化?composition-api?的功能邏輯
          ??//?functionApiInit??updateTemplateRef?主要這兩個(gè)核心函數(shù)的插入
          ??//?可以看出來,結(jié)合?hooks?機(jī)制,侵入性并不強(qiáng),不會(huì)影響到原有的?Vue2?功能的正常使用
          ??Vue.mixin({
          ????beforeCreate:?functionApiInit,
          ????mounted(this:?ComponentInstance)?{
          ??????updateTemplateRef(this)
          ????},
          ????updated(this:?ComponentInstance)?{
          ??????updateTemplateRef(this)
          ????},
          ??})

          ??//?...
          ??
          ??//?其實(shí)?functionApiInit?做的事情很簡單,
          ??//?如果?vm.$options?中存在?setup,?render?就復(fù)寫?setup,?render?做一些處理
          ??function?functionApiInit(this:?ComponentInstance)?{
          ????const?vm?=?this
          ????const?$options?=?vm.$options
          ????const?{?setup,?render?}?=?$options
          ????//?如果存在?render?函數(shù),復(fù)寫?$options.render
          ????if?(render)?{
          ??????//?keep?currentInstance?accessible?for?createElement
          ??????$options.render?=?function?(...args:?any):?any?{
          ????????//?activateCurrentInstance?維護(hù)當(dāng)前?vm,?并執(zhí)行?render-fn
          ????????return?activateCurrentInstance(vm,?()?=>?render.apply(this,?args))
          ????????//?這里列出來?activateCurrentInstance?函數(shù)的具體邏輯
          ??????????/*?
          ??????????//?維護(hù)全局的?currentInstance?對(duì)象,?
          ??????????//?讓?setup、render?的執(zhí)行始終是在正確的?vm?對(duì)象(必須要維護(hù)當(dāng)前執(zhí)行的組件實(shí)例,因?yàn)闆]有了?this)
          ??????????function?activateCurrentInstance(vm,?fn)?{
          ????????????let?preVm?=?getCurrentInstance()
          ????????????setCurrentVM(vm)
          ????????????try?{
          ??????????????return?fn(vm)
          ????????????}?catch?(err)?{}?finally?{
          ??????????????setCurrentVM(preVm)
          ????????????}
          ??????????}
          ??????????*/

          ??????}
          ????}

          ????if?(!setup)?{
          ??????return
          ????}
          ????if?(typeof?setup?!==?'function')?{
          ??????return
          ????}

          ????const?{?data?}?=?$options
          ????//?wrapper?the?data?option,?so?we?can?invoke?setup?before?data?get?resolved
          ????//?把?this.data?復(fù)寫,?引入?initSetup()
          ????$options.data?=?function?wrappedData()?{
          ??????//?核心功能函數(shù),?初始化注冊(cè)?setup?
          ??????initSetup(vm,?vm.$props)
          ??????return?typeof?data?===?'function'
          ??????????data.call(vm,?vm)
          ????????:?data?||?{}
          ????}
          ??}

          ??//?最最核心的邏輯之一
          ??function?initSetup(vm:?ComponentInstance,?props:?Record<any,?any>?=?{})?{
          ????const?setup?=?vm.$options.setup!
          ????//?創(chuàng)建?setup?上下文對(duì)象?,因?yàn)?setup?本身也可以接受一些?vm?實(shí)例的參數(shù)
          ????const?ctx?=?createSetupContext(vm)

          ????//?mark?props?as?reactive
          ????markReactive(props)

          ????//?resolve?scopedSlots?and?slots?to?functions
          ????resolveScopedSlots(vm,?ctx.slots)

          ????let?binding
          ????//?同樣的,涉及到?setup的執(zhí)行,需要維護(hù)全局的?currentInstance?對(duì)象
          ????activateCurrentInstance(vm,?()?=>?{
          ??????//?setup?函數(shù)執(zhí)行后,如果有返回,并且是響應(yīng)式對(duì)象,是需要在?view?層?template?中處理
          ??????binding?=?setup(props,?ctx)
          ????})

          ????if?(!binding)?return
          ????//?如果?binding?是?對(duì)象則進(jìn)行處理
          ????if?(isPlainObject(binding))?{
          ??????const?bindingObj?=?binding
          ??????//?vm.__secret_vfa_state__[rawBindings]?=?binding
          ??????vmStateManager.set(vm,?'rawBindings',?binding)
          ??????//?遍歷?binding?對(duì)象?keys
          ??????Object.keys(binding).forEach((name)?=>?{
          ????????let?bindingValue?=?bindingObj[name]
          ????????//?如果?binding[key]?不是響應(yīng)式的,?需要進(jìn)一步響應(yīng)式處理,
          ????????//?因?yàn)樾枰S護(hù)?view?層變更,?也就是響應(yīng)式系統(tǒng)的雙向綁定關(guān)系
          ????????//?only?make?primitive?value?reactive
          ????????if?(!isRef(bindingValue))?{
          ??????????//?...
          ??????????//?ref?這不是?vue3?提出來的嗎,怎么vue2?也能用
          ??????????bindingValue?=?ref(bindingValue)
          ??????????//?...
          ????????}
          ????????//?如果?name?不存在?vm?中,?并且也沒有?vm.$options.props[name]
          ????????//?則進(jìn)行代理處理?proxy(vm,?name,?{get,?set}),proxy?即?Object.defineProperty
          ????????asVmProperty(vm,?name,?bindingValue)
          ??????})
          ??????return
          ????}
          ??}

          ??//?這里不詳細(xì)介紹,不是本篇重點(diǎn)
          ??function?updateTemplateRef()?{
          ????//?...
          ??}
          }

          下面來看看 ref / reactive 這些 vue3 的新語法功能 為什么 vue2 中也能進(jìn)行使用

          ?

          預(yù)備知識(shí): Object.seal(obj)方法封閉一個(gè)對(duì)象, 阻止添加新屬性并將所有現(xiàn)有屬性標(biāo)記為不可配置。當(dāng)前屬性的值只要原來是可寫的就可以改變。obj 是將要被密封的對(duì)象,返回一個(gè) 被密封的對(duì)象。

          ?
          //?來看看?ref?干了什么
          export?function?ref(raw?:?unknown)?{
          ??if?(isRef(raw))?{
          ????return?raw
          ??}
          ??//?利用?reactive?函數(shù)生成響應(yīng)式對(duì)象
          ??const?value?=?reactive({?[RefKey]:?raw?})
          ??//?利用?createRef?返回?ref?對(duì)象
          ??return?createRef({
          ????get:?()?=>?value[RefKey]?as?any,
          ????set:?(v)?=>?((value[RefKey]?as?any)?=?v),
          ??})
          }
          //?createRef?函數(shù)
          export?function?createRef<T>(options:?RefOption)?{
          ??//?seal?the?ref,?this?could?prevent?ref?from?being?observed
          ??//?It's?safe?to?seal?the?ref,?since?we?really?shouldn't?extend?it.
          ??return?Object.seal(new?RefImpl(options))
          ??//?RefImpl?類具體內(nèi)容如下,會(huì)初始化?value?屬性,并在構(gòu)造函數(shù)中進(jìn)行?proxy?處理,
          ??//?上面也提到了?proxy?就是?Object.defineProperty
          ??//?當(dāng)然了,?在?vue3?中是基于?Proxy?api?實(shí)現(xiàn)的,在?vue2?中則是基于?Object.defineProperty?實(shí)現(xiàn)
          ????/*
          ????class?RefImpl?implements?Ref?{
          ??????readonly?[_refBrand]!:?true
          ??????public?value!:?T
          ??????constructor({?get,?set?}:?RefOption)?{
          ????????proxy(this,?'value',?{
          ??????????get,
          ??????????set,
          ????????})
          ??????}
          ????}
          ????*/

          }

          //?reactivity?函數(shù)
          //?Make?obj?reactivity
          export?function?reactive<T?extends?object>(obj:?T):?UnwrapRef<T>?{
          ??if?(
          ????!isPlainObject(obj)?||
          ????isReactive(obj)?||
          ????isRaw(obj)?||
          ????!Object.isExtensible(obj)
          ??)?{
          ????return?obj
          ??}
          ??//?observe?函數(shù)?即?Vue.observable(obj)?用于初始化構(gòu)建響應(yīng)式對(duì)象,vue2.6?源碼中的?api
          ??//?具體細(xì)節(jié)見?vue/src/core/global-api/index.js
          ??const?observed?=?observe(obj)
          ??//?Object.defineProperty(obj,?ReactiveIdentifierKey,?ReactiveIdentifier);
          ??//?markReactive(obj)
          ??//?setupAccessControl(observed)
          ??return?observed?
          }

          看到這里, 再回頭想一想剛剛提到的三個(gè)問題:

          1. install 主要干了什么?
          2. setup 中為什么可以隨意使用 composition-api,并脫離了 this?
          3. 基于 vue2 的 reactive / ref 是怎么實(shí)現(xiàn)的?

          現(xiàn)在是不是已經(jīng)知道答案了呢。
          其實(shí)這些問題本身并不難,難的是能不能花心思和精力去進(jìn)行專研,思考。

          @vue/composition-api 的優(yōu)勢與劣勢

          最后,來看看 基于 Vue2 的 composition-api 有哪些優(yōu)缺點(diǎn)。優(yōu)點(diǎn)其實(shí)上面也已經(jīng)提到了,這里主要看一下缺點(diǎn)。

          • composition-api 的使用限制[2]
            • 不能在數(shù)組中使用含有 ref 的普通對(duì)象。在數(shù)組中,應(yīng)該總是將 ref 存放到 reactive 對(duì)象中
            • reactive() 會(huì)返回一個(gè)修改過的原始的對(duì)象。此行為與 Vue 2 中的 Vue.observable 一致。在 Vue 3 中,reactive() 會(huì)返回一個(gè)新的的代理對(duì)象
            • watch 中不支持 ?onTrack 和 onTrigger 選項(xiàng)
            • Vue 3 新引入的 API ,在本插件中暫不適用:onRenderTracked onRenderTriggered isProxy
            • 在 data() 中使用 ref, reactive 或其他組合式 API 將不會(huì)生效
            • emit 選項(xiàng), emit 僅因在類型定義中對(duì)齊 Vue3 的選項(xiàng)而提供,不會(huì)有任何效果。
          • 性能影響
            • 由于 Vue 2 的公共 API 的限制,@vue/composition-api 不可避免地引入了額外的性能開銷

          至此,對(duì)于 @vue/composition-api 先介紹到這里,如果還有什么疑問或者想討論的,后臺(tái)回復(fù) 好友 即可加筆者微信。






          Reference

          [1]

          1: https://github.com/vuejs/composition-api-rfc/blob/master/index.md

          [2]

          2: https://github.com/vuejs/composition-api/blob/main/README.md



          瀏覽 106
          點(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>
                  国产一级a爱做片免费 | 天天透天天干 | 特级西西人体444.444人体聚色 | 国产 在线 | 欧美伦理一区二区三区 |