From 35d60545ca65aecc9c0658fc22369990de6d526d Mon Sep 17 00:00:00 2001 From: Marco Boffo Date: Tue, 8 Nov 2016 09:40:47 +0100 Subject: [PATCH 1/4] components' documentation update base on issue#4117 https://github.com/vuejs/vue/issues/4117 --- src/v2/guide/components.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/v2/guide/components.md b/src/v2/guide/components.md index 57518af238..8a491720be 100644 --- a/src/v2/guide/components.md +++ b/src/v2/guide/components.md @@ -1020,6 +1020,28 @@ template: '
' A component like the above will result in a "max stack size exceeded" error, so make sure recursive invocation is conditional (i.e. uses a `v-if` that will eventually be `false`). +When using [single file components](single-file-components.html) and recursion between two of them, you may need to lazy-load your recursive component: + +``` js +// MyComponent.vue +export default { + template: '' +} + +// StackOverflow.vue +export default { + template: '', + beforeCreate () { + this.$options.components.StackOverflow = require('./MyComponent.vue') + } +} + +// App.vue +export default { + template: '' +} +``` + ### Inline Templates When the `inline-template` special attribute is present on a child component, the component will use its inner content as its template, rather than treating it as distributed content. This allows more flexible template-authoring. From 9e8aca33914d082cb61740f7405a6587ebb88597 Mon Sep 17 00:00:00 2001 From: Marco Boffo Date: Tue, 8 Nov 2016 15:34:03 +0100 Subject: [PATCH 2/4] require paths in both components and lifecycle reference --- src/v2/guide/components.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/v2/guide/components.md b/src/v2/guide/components.md index 8a491720be..9569a95ed8 100644 --- a/src/v2/guide/components.md +++ b/src/v2/guide/components.md @@ -1020,19 +1020,22 @@ template: '
' A component like the above will result in a "max stack size exceeded" error, so make sure recursive invocation is conditional (i.e. uses a `v-if` that will eventually be `false`). -When using [single file components](single-file-components.html) and recursion between two of them, you may need to lazy-load your recursive component: +When using [single file components](single-file-components.html) and recursion between two of them, you may need to lazy-load your recursive component, in order to achieve this you should load your recursive components inside the `beforeCreate()` lifecycle step ``` js // MyComponent.vue export default { - template: '' + template: '', + beforeCreate () { + this.$options.components.StackOverflow = require('./StackOverflow.vue') + } } // StackOverflow.vue export default { template: '', beforeCreate () { - this.$options.components.StackOverflow = require('./MyComponent.vue') + this.$options.components.MyComponent = require('./MyComponent.vue') } } From d7192480231710318e21b5d4b7a6b735ac54ff4e Mon Sep 17 00:00:00 2001 From: Marco Boffo Date: Tue, 8 Nov 2016 15:49:42 +0100 Subject: [PATCH 3/4] set a name property since we are in a recursive context --- src/v2/guide/components.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/v2/guide/components.md b/src/v2/guide/components.md index 9569a95ed8..db55552f8d 100644 --- a/src/v2/guide/components.md +++ b/src/v2/guide/components.md @@ -1025,6 +1025,7 @@ When using [single file components](single-file-components.html) and recursion b ``` js // MyComponent.vue export default { + name: 'my-component', template: '', beforeCreate () { this.$options.components.StackOverflow = require('./StackOverflow.vue') @@ -1033,6 +1034,7 @@ export default { // StackOverflow.vue export default { + name: 'stack-overflow', template: '', beforeCreate () { this.$options.components.MyComponent = require('./MyComponent.vue') From 5bdabf7f969b3db87981606326795d6fb67d1fb6 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Mon, 14 Nov 2016 23:47:31 -0500 Subject: [PATCH 4/4] Update component circular dependency explanation --- src/v2/guide/components.md | 59 ++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/v2/guide/components.md b/src/v2/guide/components.md index db55552f8d..3fbb3f8cbe 100644 --- a/src/v2/guide/components.md +++ b/src/v2/guide/components.md @@ -995,7 +995,7 @@ If your component isn't passed content via `slot` elements, you can even make it Again, this _only_ works within string templates, as self-closing custom elements are not valid HTML and your browser's native parser will not understand them. -### Recursive Component +### Recursive Components Components can recursively invoke themselves in their own template. However, they can only do so with the `name` option: @@ -1020,33 +1020,48 @@ template: '
' A component like the above will result in a "max stack size exceeded" error, so make sure recursive invocation is conditional (i.e. uses a `v-if` that will eventually be `false`). -When using [single file components](single-file-components.html) and recursion between two of them, you may need to lazy-load your recursive component, in order to achieve this you should load your recursive components inside the `beforeCreate()` lifecycle step +### Circular References Between Components -``` js -// MyComponent.vue -export default { - name: 'my-component', - template: '', - beforeCreate () { - this.$options.components.StackOverflow = require('./StackOverflow.vue') - } -} +Let's say you're building a file directory tree, like in Finder or File Explorer. You might have a `tree-folder` component with this template: -// StackOverflow.vue -export default { - name: 'stack-overflow', - template: '', - beforeCreate () { - this.$options.components.MyComponent = require('./MyComponent.vue') - } -} +``` html +

+ {{ folder.name }} + +

+``` + +Then a `tree-folder-contents` component with this template: -// App.vue -export default { - template: '' +``` html +
    +
  • + + {{ child.name }} +
  • +
+``` + +When you look closely, you'll see that these components will actually be each other's descendent _and_ ancestor in the render tree - a paradox! When registering components globally with `Vue.component`, this paradox is resolved for you automatically. If that's you, you can stop reading here. + +However, if you're requiring/importing components using a __module system__, e.g. via Webpack or Browserify, you'll get an error: + +``` +Failed to mount component: template or render function not defined. +``` + +To explain what's happening, I'll call our components A and B. The module system sees that it needs A, but first A needs B, but B needs A, but A needs B, etc, etc. It's stuck in a loop, not knowing how to fully resolve either component without first resolving the other. To fix this, we need to give the module system a point at which it can say, "A needs B _eventually_, but there's no need to resolve B first." + +In our case, I'll make that point the `tree-folder` component. We know the child that creates the paradox is the `tree-folder-contents` component, so we'll wait until the `beforeCreate` lifecycle hook to register it: + +``` js +beforeCreate: function () { + this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue') } ``` +Problem solved! + ### Inline Templates When the `inline-template` special attribute is present on a child component, the component will use its inner content as its template, rather than treating it as distributed content. This allows more flexible template-authoring.