Vue 可组合范围界定问题

Vue composable scoping issue

我们将 Vue 2 与 Vue Composition API 结合使用,我们正在尝试创建一个可组合的应用程序首选项:

// useApplicationPreferences.ts
import { ref, watch } from '@vue/composition-api'
import { useSetDarkModeMutation, useViewerQuery } from 'src/graphql/generated/operations'

const darkMode = ref(false) // global scope

export const useApplicationPreferences = () => {
  const { mutate: darkModeMutation } = useSetDarkModeMutation(() => ({
    variables: {
      darkMode: darkMode.value,
    },
  }))

  watch(darkMode, async (newDarkMode) => {
    console.log('darkMode: ', newDarkMode)
    await darkModeMutation()
  })

  return { darkMode }
}

此代码运行良好,但当在同时呈现的两个组件中使用可组合项时,我们可以看到 watch 已被触发两次。这很容易通过将 watch 函数移动到全局范围(函数外部)来解决。

然而,问题是我们不能使用darkModeMutation。这个 graphql 突变不能移动到函数之外的全局范围,如果我们这样做,页面甚至不会被渲染。

目标是让 darkMode 在许多地方可用,并且当 darkMode ref 的值发生变化时,突变只会触发一次。如何实现?

通过创建仅在需要时 watch 启动的可调用函数解决了该问题(即仅在应用程序中的某处启动一次)。

// useApplicationPreferences.ts
import { ref, watch } from '@vue/composition-api'
import { useSetDarkModeMutation, useViewerQuery } from 'src/graphql/generated/operations'

const darkMode = ref(false) // global scope

export const useApplicationPreferences = () => {
  const { mutate: darkModeMutation } = useSetDarkModeMutation(() => ({
    variables: {
      darkMode: darkMode.value,
    },
  }))

  const startWatch = () => {
    watch(darkMode, async (newDarkMode) => {
      await darkModeMutation()
    })
  }
  return { darkMode, startWatch }
}

可以在MainLayout.vue中调用一次:

// MainLayout.vue
import { defineComponent } from '@vue/composition-api'
import { useApplicationPreferences } from 'useApplicationPreferences'

export default defineComponent({
  setup() {
    const { startWatch } = useApplicationPreferences()
    startWatch()
  },
})

然后所有其他组件可以根据需要简单地使用 (get/set) darkMode 引用,而 watch 仅 运行 一次。

// Settings.vue
import { defineComponent } from '@vue/composition-api'
import { useApplicationPreferences } from 'useApplicationPreferences'

export default defineComponent({
  setup() {
    const { darkMode } = useApplicationPreferences()

    return { darkMode }
  },
})