Vue3.2 中新出的 expose 是做啥用的?

前端小智發表於2022-07-13
作者:Marina Mosti
譯者:前端小智
來源:vuemastery

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

隨著Vue 3.2的釋出,一個新的組合工具提供給我們,叫做 expose

你是否曾經建立過一個需要向模板提供一些方法和屬性的元件,但又希望這些方法對元件是私有的,不能被父類呼叫?

如果你在開發一個開源的元件或庫,你有可能想保持一些內部方法的私有性。在Vue 3.2之前,這並不容易實現,因為所有在選項API中宣告的方法或資料等都是公開的,所以模板可以訪問它。

組合API也是如此。我們從setup方法中返回的所有東西都可以被父類直接訪問。

組合 API

讓我們看一個實際的例子。想象一下,我們有一個元件,它建立了一個計數器,每一秒都會更新這個計數器。

MyCounter.vue

<template>
    <p>Counter: {{ counter }}</p>

    <button @click="reset">Reset</button>
    <button @click="terminate">☠️</button>
</template>

<script>
import { ref } from 'vue'

export default {
  setup () {
    const counter = ref(0)

    const interval = setInterval(() => {
      counter.value++
    }, 1000)

    const reset = () => {
      counter.value = 0
    }

    const terminate = () => {
      clearInterval(interval)
    }

    return {
      counter,
      reset,
      terminate
    }
  }
}
</script>

從組合的角度來看,我希望父級元件能夠在需要時直接呼叫reset方法--但我希望保持terminate 函式和 counter 的引用只對元件可用。

如果我們把這個元件例項化到一個父類中,例如 App.vue,並給它附加一個 ref 引用,我們可以很容易地讓父類呼叫 reset 方法,因為當我們從 setup 中返回它時,它已經和 terminate 一起被暴露了。

App.vue

<template>
  <MyCounter ref="counter" />

  <button @click="reset">Reset from parent</button>
  <button @click="terminate">Terminate from parent</button>
</template>

<script>
import MyCounter from '@/components/MyCounter.vue'

export default {
  name: 'App',
  components: {
    MyCounter
  },
  methods: {
    reset () {
      this.$refs.counter.reset()
    },
    terminate () {
      this.$refs.counter.terminate()
    }
  }
}
</script>

如果現在執行這個,並單擊重置或終止按鈕,兩者都可以工作。

讓我們明確說明我們要向父類暴露(expose)的內容,以便只有 reset 函式可用。

MyCounter.vue

<script>
import { ref } from 'vue'

export default {
  setup (props, context) {
    const counter = ref(null)

    const interval = setInterval(() => {
      counter.value++
    }, 1000)

    const reset = () => {
      counter.value = 0
    }

    const terminate = () => {
      console.log(interval)
      clearInterval(interval)
    }

    context.expose({ reset })

    return {
      counter,
      reset,
      terminate
    }
  }
}
</script>

這裡,我們在setup函式中加入了 propscontext 引數。我們需要有可用的上下文,因為這是 expose 函式的位置。我們也可以像這樣使用重構: { expose }

接下來,我們使用 context.expose 來宣告一個我們想要向例項化這個元件的父類公開的元素物件;在這個例子中,我們只打算讓 reset 功能可用。

如果我們再次執行這個例子,並點選 "Terminate from parent" 按鈕,我們會得到一個錯誤。

Uncaught TypeError: this.$refs.counter.terminate is not a function

terminate 功能不再可用,我們的私有API現在也無法訪問了。

選項API

上面我們在 composition API 使用 exponse,但在options API中也可以使用這個方法。我們可以把它改寫成如下。

//  MyCounter.vue


export default {
  created () { ... },
  data: () => ({ counter: null }),
  methods: {
    reset () { ... },
    terminate () { ... }
  },
  expose: ['reset']
}

注意,我們新增了一個新的選項API屬性expose,允許我們傳入一個陣列,其中字串'reset'是我們公開的函式的名稱。

組合API 渲染功能

建立一個強大臉靈活的元件的方法是利用渲染函式的力量。這對Vue 3來說並不新鮮,但是隨著composition API的建立,我們現在可以靈活地從setup方法中直接返回組合API h 函式。

這就產生了一個問題,因為在我們的setup函式中,整個return語句只是包含元件正在建立的節點的 h 方法。

如果在這個時候我們選擇向父類 expose 一些東西,我們就會遇到與我們之前看到的相反的問題。沒有任何東西被暴露,因為除了DOM元素,沒有任何東西被返回。

讓我們重寫 MyCounter.vue 元件來使用這個方法。

<script>
// The template has been deleted
import { ref, h } from 'vue'

export default {
  setup (props, context) {
    const counter = ref(0)

    const interval = setInterval(() => {
      counter.value++
    }, 1000)

    const reset = () => {
      counter.value = 0
    }

    const terminate = () => {
      clearInterval(interval)
    }

    // context.expose({ reset })

    return () => h('div', [
      h('p', `Counter: ${counter.value}`),
      h('button', { onClick: reset }, 'Reset'),
      h('button', { onClick: terminate }, 'Terminate')
    ])
  }
}
</script>

注意,我們在頂部從Vue匯入了 h,因為我們需要用它來建立我們的DOM元素。

為了說明問題,暫時註釋了context.expose方法。

現在的 return 語句複製了我們之前的 <template> 的DOM結構,如果我們執行這個例子,我們能夠正確點選元素上的重置和終止按鈕。

然而,如果我們現在點選 "Reset from parent"按鈕,我們會遇到一個錯誤。

Uncaught TypeError: this.$refs.counter.reset is not a function

reset方法不再被暴露,因為它沒有被setup函式返回。為了解決這個問題,我們需要取消對context.expose的呼叫,使其再次可用。

總結

新的 expose 方法是非常直觀的,而且很容易在我們的元件中實現。它清除了一些非常重要的組成問題,這些問題在過去甚至需要重寫一個完整的元件,所以即使它不是你日常使用的API,它也是值得收藏在我們資料夾中吃灰。


程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原文:https://www.vuemastery.com/bl...

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章