Vue: 允许孩子“继承”为父母注册的组件。

创建于 2015-09-10  ·  36评论  ·  资料来源: vuejs/vue

我认为这个功能是故意删除的,但在某些情况下它可能非常有用。 为什么不把这个东西放回去可能使它成为可选的呢?

最有用的评论

显式地导入它们是值得重复的。 它允许您单独查看该层次结构中的任何组件并了解其依赖项的来源。 使用隐式回退,您将不记得 3 个月后在层次结构中导入这些组件的位置。

所有36条评论

任何现实世界的用例?

在小型用例中,只需全局注册所有内容; 在大型应用程序中,每个组件显式地依赖于它的需要更易于维护。 它在某些情况下可能有用,但好处是全局权衡。 1.0 背后的理念是“如果某些东西只有一点点有用,或者对可维护性有负面影响,让我们删除它。”

是的。 我的应用程序有一个弹出窗口,其中包含复杂的自定义编辑器元素结构。 这些元素可以组合成 3-4 级的层次结构,从而使为每个父元素显式声明它们效率低下(3-5 个父类型 * 10 个声明的元素 = 50 行重复代码)。 而且,全局注册它们也不好,因为它们永远不会出现在应用程序的其他部分。 所以我想让它们“本地”加载。

显式地导入它们是值得重复的。 它允许您单独查看该层次结构中的任何组件并了解其依赖项的来源。 使用隐式回退,您将不记得 3 个月后在层次结构中导入这些组件的位置。

@yyx990803我记性很好,谢谢。 所以我会记得我的应用程序由两个非常不同的部分组成,每个部分都注册了一组明确定义的特定于它的组件。 所以我更愿意选择在哪里加载我的资产(我怀疑自定义指令和过滤器也发生了同样的事情)。

让我分享一下我的印象。 我使用 0.12.x 并且进展顺利(减去一些小的学习曲线问题)。 简约而干净的 API、语法、可靠的代码。 现在,我们处于 1.0.0-beta 版本,它变得更糟,而不是更好。 更多的代码重复,我使用的功能被删除,让我一遍又一遍地重写相同的代码。 我开始认为我在选择 Vue 而不是 React 时犯了一个错误,因为我不确定将来不会有更多的重大变化和浪费我的时间。

@karevn

  1. 除了这个问题之外,如果您实际上列出了使开发体验变得更糟的具体事情,那将会更有帮助。
  2. 1.0.0-alpha 是预发布版本,这意味着首先没有 API 稳定性保证。 如果你看重稳定性,你应该坚持 0.12 并等待稳定的 1.0(这也将有一个最终的迁移版本)。 使用预发布意味着您已同意处理不断的重大更改。
  3. 1.0.0-beta 甚至没有发布。 使用未发布的、正在进行中的分支可能不是一个好主意。
  4. 我正在根据我的经验和整个社区的反馈来设计 API。 你有权得到你所想的任何东西,如果变化不是你喜欢的方向,你可以随意切换到其他框架。 (事实上​​,在 React 中你还必须显式地导入所有内容,并且必须重复更多的东西。)
  1. 在阅读了#1170 的讨论后,我现在几乎同意了。 但是.. 我真的没有看到删除提供此功能的现有代码的意义,而不仅仅是将strict: true设为默认值。 口味不同,有些人会更喜欢“后备”方法,这种方法有时更直观。 特别是在动态加载组件时。 它可以使用 mixins、工厂等来解决,但这都需要宝贵的时间。
  2. 肯定的事。 但在“理想架构”和变更成本之间始终存在平衡。 在这种情况下,如果留下几行代码(即:大约 10 行),它们不会伤害任何人,这会花费我大量时间。 而且我必须使用这个不稳定的版本,因为我真的需要“读写绑定过滤器”功能,这些功能可能不会被反向移植到 0.12.x
  3. 见 2。
  4. 问题不是“我更喜欢哪个 API”。 我更喜欢 Vue。 时期。 问题是“我是否可以长期依赖 Vue API”。 可靠性胜过美貌。 如果更改正在中断,则应该有严重的原因。 在新的绑定语法的情况下,它破坏了我的所有代码 - 好吧,顺其自然,它更具可读性并强制使用更好的代码结构。 在这种情况下 - 不。 在默认情况下设置options.strict = true时,此更改可能不会中断。

是的,升级总是伴随着重构的痛苦,但 Vue 1.0 是摆脱这些遗留配置选项的唯一机会。 在 1.0 之后,它将是严格的 semver,并且在 2.0 之前什么都不会中断。 由于您谈到的可靠性问题,我希望 1.x 能够持续尽可能长的时间。

关于严格模式:当你严重依赖它时,它肯定会花费重构时间——但理想情况下,对于在 1.0 之后选择 Vue 的新用户来说,他们甚至不需要知道这个东西的存在。 API 表面应尽可能小,全局结构化模式应尽可能一致。 禁用严格模式本质上鼓励了两种不同风格的 Vue 应用程序结构——想象人们在一个使用strict: true的应用程序上工作,然后转移到另一个使用strict: false的项目......它创建开发人员体验的碎片化,我想摆脱这种可能性,而 1.0 是唯一合理的地方。

在此过渡过程中陷入困境对您来说有点不走运,我感谢您的反馈。 但是需要做的事情必须要做。

@yyx990803我可以看到一个我有点坚持的具体用例。

我想要做什么

我构建了一个可扩展的应用程序:可使用小部件进行扩展。 小部件是开发人员定义的应用程序的一部分,它插入全局应用程序以在某些点上对其进行扩展; 它在应用程序启动时动态加载。 应用程序的每个实例都可以有不同的小部件集,并且应用程序的 2 个实例可以存在于同一页面上。

加载时,小部件将向 vuejs 应用程序添加一个动态创建的组件。
小部件可以包含其他小部件(子小部件)。我们不知道它在启动时会如何,因为这部分是在应用程序加载后由用户管理的。 这就是为什么小部件需要相互了解并注册其他小部件的原因。

问题

我想避免全局注册这些小部件(兼容性原因)。
因为组件是动态构建和加载的,所以我需要在每个可以包含子组件的组件中注册所有组件。 这做了很多可能不会使用的注册。 明白我的意思(请注意,目前我没有尝试对组件进行本地注册,而是使用全局注册进行测试):

var components = {

    appComponents: {
       template: "...",
       components: components
    },

    appComponents2: {
       template: "...",
       components: components
    },

    widgetComponents: {
       template: "...",
       components: components
    },

    widgetComponents2: {
       template: "...",
       components: components
    },

}

这样做时是否存在性能瓶颈?

这就是为什么我认为“半全局”组件的作用域可能有用。 它可以帮助构建具有封闭组件范围的应用程序,其中组件可以从根组件和子组件访问。 但不是来自其他 vuejs 根。 你怎么认为?

全局注册不递归注册,它只注册
组件本身,而不是其中的components选项。 所以我猜
没有问题。

2016 年 8 月 22 日星期一 16:00 Soufiane Ghzal [email protected]写道:

@yyx990803 https://github.com/yyx990803我可以看到一个具体的用例
我有点坚持。

_我想要做什么_

我构建了一个可扩展的应用程序:可使用小部件进行扩展。 小部件是一个
开发者定义的应用程序,它在
应用程序的启动。 应用程序的每个实例都可以有
不同的小部件集。

加载时,小部件将添加一个动态创建的组件到
Vue.js 应用程序。
小部件可以包含其他小部件(子)。,我们不知道它是如何
将在启动时,因为这部分是由用户管理后
应用程序已加载。 这就是为什么小部件需要相互了解的原因。

_问题_

我想避免在全局范围内注册这些小部件。
因为小部件是动态加载的,所以我需要在其中注册所有小部件
每个可以包含孩子的小部件。 这做了很多注册
可能不会使用。 看:

变量组件 = {

appComponents: {
   template: "...",
   components: components
},

appComponents2: {
   template: "...",
   components: components
},

}

_这样做有没有性能瓶颈?_


您收到此消息是因为您订阅了此线程。
直接回复此邮件,在 GitHub 上查看
https://github.com/vuejs/vue/issues/1297#issuecomment -241339596,或者静音
线程
https://github.com/notifications/unsubscribe-auth/AFTLl6QDePtH93VOU2lgrC72Z0vKLsv-ks5qiVcXgaJpZM4F7M1v
.

@fnlctrl这与全球注册无关。

问题是

  • 所有 vue+component 实例的全局注册寄存器。
  • 仅当前组件的本地注册寄存器
  • 并且没有办法为 root 和孩子“半全局”注册:允许注册当前 vue 实例的东西(包括添加到这个实例中的组件)。

在我看来,问题在于 vue 虽然适用于静态(全局)方式,但它仅限于打包/可分发(本地)方式。

semi-global注册实际上已经成为可能,因为 Vue 具有原型继承。
https://jsfiddle.net/fnlCtrl/32dt9e9g/

@fnlctrl
我不确定你想用你发送的小提琴显示什么(请注意你的例子有错误: Unknown custom element: <bar> - did you register the component correctly?

我不够清楚,也许你没有理解我要解释的内容。 重新开始:

我想解释的是,我们可以:

  • globaly 使用Vue.component('name', {...})注册组件(非常适合单页应用程序)
  • 在组件new Vue({ components: {...} });中本地注册一个组件(这样可以很好地发送具有依赖关系的组件以实现本地可重用性)

但是我们不能让父组件对子组件可用。 例如为当前vm实例和在此实例中加载的所有组件注册全局组件,但不为在其他 vm 实例中加载的组件注册。 查看示例: https ://jsfiddle.net/p8wqafm1/2/

你明白吗?

哎呀,似乎我的小提琴没有正确保存..
这是我打算给你看的..
https://jsfiddle.net/fnlCtrl/32dt9e9g/1/

我现在正在阅读您的示例。

我在这里分叉了您的示例
您想在 Foo 中添加受限的动态组件。

@fnlctrl感谢您的示例,但它似乎还没有涵盖我想要实现的目标。

在您的示例中使用该方法仅在Foo中注册组件,但这不会使它们在Foo的子项中可用(在此示例中Bar )。

看小提琴,我在Foo $ 中注册Baz #$ 并且我希望它在Bar中可用,因为它是从Foo加载的: https://jsfiddle .net/8y0Lmb01/3/

分叉你的例子: https ://jsfiddle.net/fnlCtrl/uvzaotaz/

关键是组件应该有清晰的依赖树,相互依赖的动态组件也不例外。

@fnlctrl在您的示例中Baz不再在Foo中可用。

为此,我可以使用Vue.component('baz', {...} ,但问题是它会用这个baz组件“污染”其他 vue 实例。

或者

我可以在FooBar以及所有 foo 孩子、所有 bar 孩子和所有 Foo Grand Children 等中注册Baz ......但这增加了一个大型/动态应用程序的复杂性

你明白我的意思吗? 我可以在本地注册,但我们不能继承当前组件的子、孙、...的组件_only_

是的,我现在明白你的意思了。 抱歉,我不知道注册
Foo 下的组件不会使其在 Foo 的范围内成为全局的,不像
Vue.component 。 将查看源代码以了解原因。

2016 年 8 月 22 日星期一 20:17 Soufiane Ghzal [email protected]写道:

@fnlctrl https://github.com/fnlctrl在你的例子中 Baz 不可用
在 Foo 了。

为此,我可以使用 Vue.component('baz', {...} 但问题是
它会用这个 baz 组件“污染”其他 vue 实例。

或者

我可以在 Foo 和 Bar 以及所有 foo 孩子中注册 Baz,并且
所有酒吧的孩子,所有的 Foo Grand Children,等等……但这增加了很多
大型/动态应用程序的复杂性

你明白我的意思吗? 我可以在本地注册,但我们不能继承
当前组件的子、孙、...的组件
_只要_


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/vuejs/vue/issues/1297#issuecomment -241395119,或者静音
线程
https://github.com/notifications/unsubscribe-auth/AFTLl2ud0GDO_hOwFN8GIA1TzVEF1q0Fks5qiZM9gaJpZM4F7M1v
.

谢谢 :)

关键是我想发布一个依赖于 vue 的独立库,为给定实例注册组件将是一个真正的好处,因为无论如何,作为一个独立库,我不允许在全局 Vue 实例中注册它(这会破坏独立部分)。

请让我知道我所说的是否可以在 Vue 中实现

好吧,我想原因太明显以至于我忽略了:我不是
使用new Foo() ...

将在 20 分钟内得到小提琴,在我回家的路上。

2016 年 8 月 22 日星期一 20:27 宋铄运[email protected]写道:

是的,我现在明白你的意思了。 抱歉,我不知道注册
Foo 下的组件不会使其在 Foo 的范围内成为全局的,不像
Vue.component 。 将查看源代码以了解原因。

2016 年 8 月 22 日星期一 20:17 Soufiane Ghzal [email protected]
写道:

@fnlctrl https://github.com/fnlctrl在你的例子中 Baz 不是
Foo 不再可用。

为此,我可以使用 Vue.component('baz', {...} 但问题是
它会用这个 baz 组件“污染”其他 vue 实例。

或者

我可以在 Foo 和 Bar 以及所有 foo 孩子中注册 Baz,并且
所有酒吧的孩子,所有的 Foo Grand Children,等等……但这增加了很多
大型/动态应用程序的复杂性

你明白我的意思吗? 我可以在本地注册,但我们不能继承
当前组件的子、孙、...的组件
_只要_


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/vuejs/vue/issues/1297#issuecomment -241395119,或者静音
线程
https://github.com/notifications/unsubscribe-auth/AFTLl2ud0GDO_hOwFN8GIA1TzVEF1q0Fks5qiZM9gaJpZM4F7M1v
.

好吧, new Foo(...)也没有用: https ://jsfiddle.net/8y0Lmb01/5/

确实很奇怪...... https://jsfiddle.net/fnlCtrl/p0ggkncu/
现在调查它。

我已经阅读了一些源代码,发现在 Vue 本身上,我们可以这样破解:
https://jsfiddle.net/fnlCtrl/522aw9sm/
(不使用 Vue.extend 或 Vue.component,顺便说一句,Vue.component 只是一个辅助函数,它执行 Vue.extend 并修改 Vue.options.components)

虽然相同的方法不适用于扩展的 Vue:
https://jsfiddle.net/fnlCtrl/v1m2s16u/

所以我认为问题是由组件分辨率引起的。 我会继续寻找。

@fnlctrl好的,谢谢,我尝试检查一些事情并得出与您相同的结论。 我对核心的了解不够,无法找到它为什么会这样工作。 我们知道这是否是预期的行为吗?

我认为这些关于resolveAsset的评论

解决资产。
使用此功能是因为子实例需要访问
到其祖先链中定义的资产。

建议在扩展的 Vue 上注册组件应该可以工作。

函数体中的代码不看祖先链吧? 也许它还没有实施?

我还不够了解,仍在学习它的行为,但我猜“祖先链”是指第一个参数options

我想我可以得出结论,原因是这个 (src/core/global-api/extend)
它使扩展类使用与父类相同的方法。

我已经测试过了,如果您复制core/global-api/assets中的内容(当然,使用 dist 版本中的相应代码,该代码已去除类型)
到 vue.extend,使其看起来像这样(将Vue更改为Sub ):

config._assetTypes.forEach(function (type) {
        Sub[type] = function (id, definition) {
          if (!definition) {
            return this.options[type + 's'][id];
          } else {
            /* istanbul ignore if */
            if ("development" !== 'production') {
              if (type === 'component' && config.isReservedTag(id)) {
                warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id);
              }
            }
            if (type === 'component' && isPlainObject(definition)) {
              definition.name = definition.name || id;
              definition = Sub.extend(definition);
            }
            if (type === 'directive' && typeof definition === 'function') {
              definition = { bind: definition, update: definition };
            }
            this.options[type + 's'][id] = definition;
            return definition;
          }
        };
      });

Foo = Vue.extend()Foo.component()将起作用。

虽然我猜这会导致一些性能损失。

@gsouf我想我已经找到了拼图的最后一块(无需修改 vue 的等效解决方法):
https://jsfiddle.net/fnlCtrl/v1m2s16u/

var Root = Vue.extend()

Root.options.components.Foo = Root.extend({
    template: '<div>Foo</div>'
})

Root.options.components.Bar = Root.extend({
    template: '<div>Bar, uses <foo></foo></div>'
})

new Root({
    template: `
  <div>
    <foo></foo>
    <bar></bar>
  </div>
  `
}).$mount('#app')

@fnlctrl ,感谢您的输出,抱歉耽搁了。

确实看起来组件从它们的构造函数继承组件,而不是从它们的父级继承。 目前正在寻找是否可以为我的用例应用补丁。

在您的情况下,它仍然附加到构造函数,而不是实例,我正在寻找它只是实例的一部分

@fnlctrl由于 javascript 的工作方式,并且由于您的示例,我可以通过为我创建的每个实例“动态扩展”vue 来解决它,使所有内容仅可用于此应用程序。:

createVueInstance = function(el, data){
    var vExtend = Vue.extend();
    vExtend.partial('some-semiglobal-partial', "...");
    vExtend.component('some-semiglobal-component', vExtend.extend({...}));

    return new vExtend({
        el: el,
        data: data
    });
};

在检查了核心是如何构建的之后,它看起来并不是为了轻松集成每个实例可用的组件而构建的,而且这种解决方法对我来说足够稳定。

谢谢你的帮助!

顺便说一句,我认为您向我展示的示例可以在文档中进行深入解释。 我没有找到关于那个的提及

@gsouf不客气。 我认为这个问题对于那些想要实现类似功能的人来说已经足够了 :smile:

在这里,我有一个“半全局”用例:

我有一个相对通用的 Layout 组件,但内容可以由使用 Layout 组件的组件配置,例如。 组件 A 使用 Layout 并希望使用组件 B 配置其内容,其他一些组件可能使用 Layout 并使用组件 C 配置其内容等。

应该支持这种模式吗?

或者有什么解决方案可以替代这种设计?

该模式在 iOS 中被广泛使用以提高代码重用,这是一种灵活的设计。

@hpsoar您可能需要的是插槽

我的设计如下,基本上这将允许我对单元格做两件事:

  1. 使用 css 样式简单地配置单元格,这在许多情况下就足够了;
  2. 在单元格中插入一个组件,该组件将用于特殊情况。
<template>
  <div class="tile is-ancestor">
    <div class="tile is-parent">
      <article class="tile is-child box">
        <div class="table-responsive">
          <table class="table is-bordered is-striped is-narrow">
            <thead>
            <tr>
              <th v-for="c in columns">
                {{c.title}}
              </th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="(item, index) in items">
              <template v-for="c in columns">
                <td v-if="c.hasOwnProperty('component')"><div :is="c.component"></div></td>
                <td v-else>{{ item[c.name] }}</td>
              </template>
            </tr>
            </tbody>
          </table>
        </div>
      </article>
    </div>
  </div>
</template>

<script>

export default {
  components: {
  },
  props: [
    'columns',
    'items'
  ],
  data: function () {
    return {
    }
  }
}

</script>

<style lang="scss" rel="stylesheet/scss">
  .table-responsive {
    display: block;
    width: 100%;
    min-height: .01%;
    overflow-x: auto;
  }
</style>

此页面是否有帮助?
0 / 5 - 0 等级