如何在访问 Vue 实例时动态生成计算道具?

How to generate computed props on the fly while accessing the Vue instance?

我想知道是否有一种方法可以以编程方式创建计算道具,同时仍然访问实例以实现 动态值

类似的东西(this 下面未定义)

<script>
export default {
  computed: {
    ...createDynamicPropsWithTheContext(this), // helper function that returns an object
  }
}
</script>

关于这个问题,Linus给出了一个解决方案:https://forum.vuejs.org/t/generating-computed-properties-on-the-fly/14833/4看起来像

computed: {
  ...mapPropsModels(['cool', 'but', 'static'])
}

这工作正常,但主要问题是它是完全静态的。例如,有没有办法访问 Vue 实例以获取道具?


更多上下文

出于测试目的,我的辅助函数非常简单

export const createDynamicPropsWithTheContext = (listToConvert) => {
  return listToConvert?.reduce((acc, curr) => {
    acc[curr] = curr
    return acc
  }, {})
}

我实际上希望传递给这个辅助函数(通过 this)的是与特定前缀匹配的道具,也就是以任何 is|can|has|show 开头的道具(我使用的是正则表达式),我确实可以通过经典 parent/child 状态传输中的 this.$options.props 进行访问。

我的问题的最终想法主要是避免像 ...createDynamicPropsWithTheContext(['canSubmit', 'showModal', 'isClosed']) 那样手动编写所有道具,而是以编程方式填充它们(很多组件都需要这种模式)。

props是这样传递的

<my-component can-submit="false" show-modal="true" />

PS:它是 can-submit 而不是 :can-submit 故意的(虽然现在仍然被黑进了 falsy 结果!)。
这是为了方便最终用户使用,不需要记住前缀 :,是的,我知道......对于一个可以遵循 Vue 约定的分号来说有很多困难。

您可以使用 setup() 挂钩,它接收 props 作为第一个参数。将 props 参数传递给 createDynamicPropsWithTheContext,并将结果传播到 setup() 的 return(就像您之前在 computed 选项中所做的那样):

import { createDynamicPropsWithTheContext } from './props-utils'

export default {
  ⋮
  setup(props) {
    return {
      ...createDynamicPropsWithTheContext(props),
    }
  }
}

demo

如果整个事情都是为了避免使用 :,那么您可能要考虑使用一个简单的对象(或对象数组)作为数据源。您可以遍历列表并将数据绑定到生成的组件。在这种情况下,唯一使用的 : 是在对象

const comps = [{
    "can-submit": false,
    "show-modal": true,
    "something-else": false,
  },
  {
    "can-submit": true,
    "show-modal": true,
    "something-else": false,
  },
  {
    "can-submit": false,
    "show-modal": true,
    "something-else": true,
  },
]

const CustomComponent = {
  setup(props, { attrs }) {
    return {
      attrs
    }
  },
  template: `
    <div
      v-bind="attrs"
    >{{ attrs }}</div>
  `
}

const vm = Vue.createApp({
  setup() {
    return {
      comps
    }
  },
  template: `
    <custom-component
      v-for="(item, i) in comps"
      v-bind="item"
    ></custom-component>
  `

})

vm.component('CustomComponent', CustomComponent)

vm.mount('#app')
<script src="https://unpkg.com/vue@3"></script>

<div id="app">{{ message }}</div>

感谢 Vue's Discord Cathrineskirtle 的朋友们,我终于成功了!

这是对我有帮助的 the thread and here is the SFC example,尤其是这段代码

created () {
  const magicIsShown = computed(() => this.isShown === true || this.isShown === 'true')

  Object.defineProperty(this, 'magicIsShown', {
    get () {
      return magicIsShown.value
    }
  })
}

使用 Object.defineProperty(this... 有助于保持整个状态的反应性,并且 computed(() => 可以引用其他一些道具(我正在查看我的案例)。

使用 JS 对象是可行的,但我必须从模板中完成它(这是一个较低的进入门槛)。

不过,这是我想出的解决方案,作为在每个组件中导入的全局 mixin。

// helper functions
const proceedIfStringlean = (propName) => /^(is|can|has|show)+.*/.test(propName)

const stringleanCase = (string) => 'stringlean' + string[0].toUpperCase() + string.slice(1)

const computeStringlean = (value) => {
  if (typeof value == 'string') {
    return value == 'true'
  }
  return value
}

// the actual mixin
const generateStringleans = {
  created() {
    for (const [key, _value] of Object.entries(this.$props)) {
      if (proceedIfStringlean(key)) {
        const stringleanComputed = computed(() => this[key])

        Object.defineProperty(this, stringleanCase(key), {
          get() {
            return computeStringlean(stringleanComputed.value)
          },
          // do not write any `set()` here because this is just an overlay
        })
      }
    }
  },
}

这将扫描每个 .vue 组件,获取传递的道具,如果这些道具以 is|can|has|show 为前缀,将创建一个重复的 counter-part,前缀为 stringlean + 将初始道具传递给方法(在我的例子中为 computeStringlean)。

效果很好,因为我们直接在 vanilla JS 中连接它,所以没有预期的 devtools 支持。