From 29f957a37fcd59cc02e9da83e55c481439f95667 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 14 Oct 2017 23:51:37 -0400 Subject: [PATCH 1/9] refactor mixins into a new component composition page --- _config.yml | 3 +- src/v2/api/index.md | 8 +- src/v2/guide/composition.md | 546 +++++++++++++++++++++++++++++ src/v2/guide/mixins.md | 142 -------- themes/vue/layout/partials/toc.ejs | 2 +- 5 files changed, 554 insertions(+), 147 deletions(-) create mode 100644 src/v2/guide/composition.md delete mode 100644 src/v2/guide/mixins.md diff --git a/_config.yml b/_config.yml index 2eb260398d..2a8ab74d70 100644 --- a/_config.yml +++ b/_config.yml @@ -164,7 +164,8 @@ alias: guide/migration-vue-router.html: v2/guide/migration-vue-router.html guide/migration-vuex.html: v2/guide/migration-vuex.html guide/migration.html: v2/guide/migration.html - guide/mixins.html: v2/guide/mixins.html + guide/mixins.html: v2/guide/composition.html + v2/guide/mixins.html: v2/guide/composition.html guide/plugins.html: v2/guide/plugins.html guide/reactivity.html: v2/guide/reactivity.html guide/render-function.html: v2/guide/render-function.html diff --git a/src/v2/api/index.md b/src/v2/api/index.md index 33abc848f7..668ae29a31 100644 --- a/src/v2/api/index.md +++ b/src/v2/api/index.md @@ -44,7 +44,7 @@ type: api The merge strategy receives the value of that option defined on the parent and child instances as the first and second arguments, respectively. The context Vue instance is passed as the third argument. -- **See also:** [Custom Option Merging Strategies](../guide/mixins.html#Custom-Option-Merge-Strategies) +- **See also:** [Custom Merge Strategies](../guide/composition.html#Custom-Merge-Strategies) ### devtools @@ -366,7 +366,7 @@ type: api Apply a mixin globally, which affects every Vue instance created afterwards. This can be used by plugin authors to inject custom behavior into components. **Not recommended in application code**. -- **See also:** [Global Mixin](../guide/mixins.html#Global-Mixin) +- **See also:** [Global Mixins](../guide/composition.html#Global-Mixins)

Vue.compile( template )

@@ -950,7 +950,7 @@ type: api // => 2 ``` -- **See also:** [Mixins](../guide/mixins.html) +- **See also:** [Mixins](../guide/composition.html#Mixins) ### extends @@ -974,6 +974,8 @@ type: api } ``` + - **See also:** [Extension](../guide/composition.html#Extension) + ### provide / inject > New in 2.2.0+ diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md new file mode 100644 index 0000000000..a7387d1587 --- /dev/null +++ b/src/v2/guide/composition.md @@ -0,0 +1,546 @@ +--- +title: Component Composition +type: guide +order: 301 +--- + +Vue offers many strategies for building components and sharing functionality between them. Below, we'll provide a description of each strategy, followed by more in-depth explanations of when and how to apply them. + +## Overview + +- **Merging** + + - __[Extension](#Extension)__: For building more specific components, based on a generic base component. + + ``` js + var MyComponent = { + extends: GenericBaseComponent, + // ... additional/overwriting config ... + } + ``` + + - __[Mixins](#Mixins)__: For sharing functionality between mostly unrelated components. + + ``` js + var MyComponent = { + mixins: [sharedComponentOptions], + // ... component config ... + } + ``` + +- __[Wrapper Components](#Wrapper-Components)__: For defining variations of a base component with some props/slots already "filled in". + + ``` js + var MyComponent = { + components: { + WrappedComponent: WrappedComponent + }, + template: ` + + Filled-in slot content + + `, + } + ``` + +- __[Factory Functions](#Factory-Functions)__: For controlling and simplifying how component variations are created. + + ``` js + function createMyComponent (options) { + return { + // ... dynamically generated component config ... + } + } + ``` + + + +## Extension + +### An Example Use Case + +Whenever you want to build a more specific version of an existing component, you may find **extension** useful. For example, let's say you have a generic note component: + +``` js +var AppNote = { + props: { + type: { + type: String, + default: 'info' + } + }, + template: ` +

+ + + + +

+ ` +} +``` + +You might use it to display an error like this: + +``` html + + Error! + Something went wrong. + +``` + +But there's a problem. Every time you display an error, you'll have to remember to add `type="danger"` and `Error!` in the `bold` slot. It's easy to make a mistake, resulting in inconsistent error messages. + +Ideally, we'd avoid error-prone repetition with an `ErrorNote` component, where we'd simply provide the error text: + +``` html + + Something went wrong. + +``` + +Then this component would automatically prefix the content with "Error!" in bold and add the correct class. + +### The Problems with Copying Component Definitions + +We could create this new `ErrorNote` component by copying and pasting the definition for `AppNote`, then making a few changes: + +``` js +var ErrorNote = { + template: ` +

+ + Error! + + +

+ ` +} +``` + +This is the simplest option and sometimes it's appropriate, when two components only _incidentally_ share a lot in common. In this case, however, we probably want all the notes in our application to work mostly the same. For example, if we later added a close button to `AppNote`, we'd now have to add the exact same code to `ErrorNote` and any other variations we created. + +Fortunately, there's a better way. + +### Merging Options with `extends` + +In cases like this, the `extends` option is usually a more appropriate alternative: + +``` js +var ErrorNote = { + extends: AppNote, + props: { + type: { + type: String, + default: 'danger' + } + }, + beforeCreate: function () { + if (!this.$slots.bold) { + this.$slots.bold = this.$createElement('span', 'Error!') + } + } +} +``` + +In the above example, `extends` is telling Vue to merge our config with the `AppNote` config. In all cases, the component doing the extension (`ErrorNote`) takes priority, so that: + +- The definition for the `type` prop from `AppNote` is replaced with a new definition with a default of `'danger'`. +- A `beforeCreate` hook is added to create a default of `Error!` for the `bold` slot. + +### Merging of Instance Properties + +For options that create instance properties, such as `props`, `data`, `computed`, and `methods`, Vue's merging does **not** replace the entire definition. For example, in this case: + +``` js +var ComponentA = { + props: { + foo: String, + bar: Number + } +} + +var ComponentB = { + extends: ComponentA, + props: { + foo: { + type: String, + default: 'hi' + }, + baz: Boolean + } +} +``` + +The resulting options object from the `extends` in `ComponentB` would be: + +``` js +{ + props: { + foo: { + type: String, + default: 'hi' + }, + bar: Number, + baz: Boolean + } +} +``` + +Nothing is removed, prop definitions with the same name are replaced, and any new props are added. + +### Merging of Event Handlers + +For event handlers (i.e. watchers and lifecycle hooks), merging does not replace existing hooks, but rather adds new hooks to run after the inherited ones. + +For example, if both components define `created` hooks like this: + +``` js +var ComponentA = { + created: function () { + console.log('created from ComponentA') + } +} + +var ComponentB = { + extends: ComponentA, + created: function () { + console.log('created from ComponentB') + } +} +``` + +The resulting log will be: + +``` +created from ComponentA +created from ComponentB +``` + +The event handler in the extended config runs directly _after_ the one we're extending from. + + + +## Mixins + +### Mixins vs Extension + +**Mixins** work [like extension](#Merging-of-Instance-Properties), but allow you to define an array of configs instead of only one. For example, these two options do exactly the same thing: + +``` js +extends: ComponentA +``` + +``` js +mixins: [ComponentA] +``` + +So why have both then? Technically, you _could_ get everything done just with the `mixins` option, but we provide both to best describe their exact role in relation to the component: + +- **Extension is for building on a base component**, which will typically contain the template or render function. In the component we extend extending, we usually only add a few deviations from the default behavior. + +- **Mixins are for adding shared functionality to many components.** Unlike extended base components, mixins are typically not complete components by themselves. They do not contain a template or render function, leaving that to the components they're used in. + +### An Example Use Case + +Remember the `AppNote` and `ErrorNote` components [from earlier](#An-Example-Use-Case)? In another application, we might actually want two mostly different components for these use cases: + +``` js +var AppNote = { + template: ` +
+ +
+ ` +} + +var ErrorNotification = { + template: ` +

+ Error! + +

+ ` +} +``` + +However, there might be some specific behavior we want to be shared, such as the ability to add a close button, which upon click, would hide the component. Then across any relevant components, we could include the mixin and add a `` wherever we want it to appear, like this: + +``` js +var AppNote = { + mixins: [closeMixin], + template: ` +
+ + +
+ ` +} + +var ErrorNotification = { + mixins: [closeMixin], + template: ` +

+ Error! + + +

+ ` +} +``` + +And here's mixin definition that actually makes that work: + +``` js +var closeMixin = { + // Register a close button for templates + components: { + CloseButton: { + template: ` + + ` + } + }, + // Track of whether the component is closed + data: function () { + return { + isClosed: false + } + }, + // Simulate a v-if on the root element + beforeCreate: function () { + // Save the original, compiled render function + var origRender = this._render + // Replace the original render function + this._render = function _render () { + // If the element is closed... + return this.isClosed + // Render an empty text node + ? this._e() + // Otherwise call the original render + : origRender.apply(this, arguments) + } + } +} +``` + +Looking at the mixin above, you might be thinking, "Wow, that looks complex!" You're right! Advanced component composition often requires deeper knowledge of both JavaScript and Vue's internals. To write the above mixin, you'd need to be familiar with: + +- JavaScript's [`Function.prototype.apply`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply). + +- Vue's internal `_render` method, which will either reference a user-provided render function or one created from a compiled, user-provided template. + +- Vue's internal `_e` method, which is used to create a placeholder node for falsy `v-if`s without a `v-else`. Internal helpers like this are not currently documented, but you can discover them by trying out the template you want to reproduce with Vue's [template compiler](https://vuejs.org/v2/guide/render-function.html#Template-Compilation). + +

Note that _-prefixed methods are not considered part of the stable API, so they may be changed or removed without warning. Using render helpers is considered relatively safe, but accompanying unit tests are recommended to ensure that everything still works after upgrading Vue to a new version.

+ +### Global Mixins + +You can also apply a mixin globally, but do this with caution! + +

Global mixins affect every Vue instance and component, including third-party components.

+ +One appropriate use case, however, is to inject processing logic for custom options, such as in the [VueFire plugin](https://github.com/vuejs/vuefire#usage): + +``` js +// Inject handler for a `firebase` custom option +Vue.mixin({ + created: function () { + var firebaseBindings = this.$options.firebase + if (firebaseBindings) { + // Add instance properties and listeners + // for data from firebase. + } + } +}) +``` + +Check out [VueFire's source](https://github.com/search?utf8=%E2%9C%93&q=%22Vue.mixin%22+repo%3Avuejs%2Fvuefire+path%3A%2Fsrc&type=Code) for a complete example. + + + +## Custom Merge Strategies + +Plugins often define custom options, which use the default strategy of overwriting any existing value. If you're the author a plugin where this is undesirable, you can define a **custom merge strategy** for your options with `Vue.config.optionMergeStrategies`. + +For example, if your plugin uses a `foo` option: + +``` js +Vue.config.optionMergeStrategies.foo = function (newValue, baseValue) { + // Return the correctly merged value +} +``` + +For most object-based options, you can simply use the same strategy used by `methods`: + +``` js +var strategies = Vue.config.optionMergeStrategies +strategies.foo = strategies.methods +``` + +A more advanced example can be found on [Vuex](https://github.com/vuejs/vuex)'s 1.x merging strategy: + +``` js +const merge = Vue.config.optionMergeStrategies.computed +Vue.config.optionMergeStrategies.vuex = function (newValue, baseValue) { + if (!newValue) return baseValue + if (!baseValue) return newValue + return { + getters: merge(newValue.getters, baseValue.getters), + state: merge(newValue.state, baseValue.state), + actions: merge(newValue.actions, baseValue.actions) + } +} +``` + + + +## Wrapper Components + +As you've seen, advanced composition with extends and mixins can quickly grow complicated. One simpler alternative is creating **wrapper components**, which provide an interface for another component typically used as its root element. + +For example, the `ErrorNote` component we [created earlier with extension](#An-Example-Use-Case) could be refactored to: + +``` js +var ErrorNote = { + components: { + AppNote: AppNote + }, + template: ` + + Error! + + + ` +} +``` + +Instead of inheriting and building on the interface of a base component, this strategy does **not** provide direct access to the wrapped component. So unlike the `ErrorNote` using extension, this one has its own props (currently none) and its own slots (currently just the default slot). + +This is useful when we'd really like to _remove_ options from a base interface. For example, in the case of `ErrorNote`, it'd be very strange for it to be used like this: + +``` html + + Welcome! + Would you like to take the tour? + +``` + +Above, a developer has hijacked the component for a new kind of note that is not actually an error. The wrapper version of this component more strictly controls the API, so does not allow this kind of abuse. + +

In the example above, type="success" would actually affect the wrapped AppNote component, but as an attribute rather than a prop. To prevent attributes from automatically being added to the root element of a component, use the inheritAttrs: false option.

+ + + +## Factory Functions + +**Factory functions** are functions that return component options, like this: + +``` js +function createComponent (options) { + return { + // ... dynamically generated component config ... + } +} +``` + +Factory functions allow you to better control and simplify how component variations are created. For example, it could be argued that the `ErrorNote` we [created earlier with extension](#An-Example-Use-Case) would be conceptually simpler as a factory function: + +``` js +function createNote (options) { + options = options || {} + options.props = options.props || {} + options.slots = options.slots || {} + + return { + props: { + type: { + type: String, + default: options.props.type || 'info' + } + }, + template: ` +

+ + ${ + options.slots.bold || '' + } + + ${ + options.slots.default || '' + } +

+ ` + } +} +``` + +Then we could create our `AppNote` and `ErrorNote` components with: + +``` js +var AppNote = createNote() + +var ErrorNote = createNote({ + props: { + type: 'error' + }, + slots: { + bold: 'Error!' + } +}) +``` + +Using this strategy, all notes in the app maintain unified behavior, but _you_ control the API for creating them. This can be especially useful when you want to: + +- Create many variations of a component, abstracting away the specific implementation details. +- Give designers, less experienced developers, or members of a very large team the ability to create new component variations, but strictly control what they're allowed to change. +- Dynamically build template strings at runtime, as a more familiar alternative to render functions. + +We'll demonstrate the last advantage in the next section. + +### Computed Components + +Imagine you want a component that generates dynamic headings, with this kind of interface: + +``` html + + {{ headingContent }} + +``` + +So for example, this: + +``` html + + My heading title + +``` + +would render to: + +``` html +

My heading title

+``` + +We describe how to do this (and when you might want to) on the [Render Functions](render-function.html) page, but let's imagine you want to avoid learning how to write render functions. You could achieve the same results with a computed factory function that builds a child component config: + +``` js +var DynamicHeading = { + props: { + level: { + type: Number, + required: true + } + }, + computed: { + HeadingComponent: function () { + return { + template: `` + } + }, + template: '' + } +} +``` diff --git a/src/v2/guide/mixins.md b/src/v2/guide/mixins.md deleted file mode 100644 index 218c1fd7ec..0000000000 --- a/src/v2/guide/mixins.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Mixins -type: guide -order: 301 ---- - -## Basics - -Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be "mixed" into the component's own options. - -Example: - -``` js -// define a mixin object -var myMixin = { - created: function () { - this.hello() - }, - methods: { - hello: function () { - console.log('hello from mixin!') - } - } -} - -// define a component that uses this mixin -var Component = Vue.extend({ - mixins: [myMixin] -}) - -var component = new Component() // => "hello from mixin!" -``` - -## Option Merging - -When a mixin and the component itself contain overlapping options, they will be "merged" using appropriate strategies. For example, hook functions with the same name are merged into an array so that all of them will be called. In addition, mixin hooks will be called **before** the component's own hooks: - -``` js -var mixin = { - created: function () { - console.log('mixin hook called') - } -} - -new Vue({ - mixins: [mixin], - created: function () { - console.log('component hook called') - } -}) - -// => "mixin hook called" -// => "component hook called" -``` - -Options that expect object values, for example `methods`, `components` and `directives`, will be merged into the same object. The component's options will take priority when there are conflicting keys in these objects: - -``` js -var mixin = { - methods: { - foo: function () { - console.log('foo') - }, - conflicting: function () { - console.log('from mixin') - } - } -} - -var vm = new Vue({ - mixins: [mixin], - methods: { - bar: function () { - console.log('bar') - }, - conflicting: function () { - console.log('from self') - } - } -}) - -vm.foo() // => "foo" -vm.bar() // => "bar" -vm.conflicting() // => "from self" -``` - -Note that the same merge strategies are used in `Vue.extend()`. - -## Global Mixin - -You can also apply a mixin globally. Use with caution! Once you apply a mixin globally, it will affect **every** Vue instance created afterwards. When used properly, this can be used to inject processing logic for custom options: - -``` js -// inject a handler for `myOption` custom option -Vue.mixin({ - created: function () { - var myOption = this.$options.myOption - if (myOption) { - console.log(myOption) - } - } -}) - -new Vue({ - myOption: 'hello!' -}) -// => "hello!" -``` - -

Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components. In most cases, you should only use it for custom option handling like demonstrated in the example above. It's also a good idea to ship them as [Plugins](plugins.html) to avoid duplicate application.

- -## Custom Option Merge Strategies - -When custom options are merged, they use the default strategy which overwrites the existing value. If you want a custom option to be merged using custom logic, you need to attach a function to `Vue.config.optionMergeStrategies`: - -``` js -Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) { - // return mergedVal -} -``` - -For most object-based options, you can use the same strategy used by `methods`: - -``` js -var strategies = Vue.config.optionMergeStrategies -strategies.myOption = strategies.methods -``` - -A more advanced example can be found on [Vuex](https://github.com/vuejs/vuex)'s 1.x merging strategy: - -``` js -const merge = Vue.config.optionMergeStrategies.computed -Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) { - if (!toVal) return fromVal - if (!fromVal) return toVal - return { - getters: merge(toVal.getters, fromVal.getters), - state: merge(toVal.state, fromVal.state), - actions: merge(toVal.actions, fromVal.actions) - } -} -``` diff --git a/themes/vue/layout/partials/toc.ejs b/themes/vue/layout/partials/toc.ejs index 1d13a8aa9b..4ee2ce4c05 100644 --- a/themes/vue/layout/partials/toc.ejs +++ b/themes/vue/layout/partials/toc.ejs @@ -8,7 +8,7 @@ <% if (fileName === 'transitions') { %>
  • Transitions & Animation

  • <% } %> - <% if (fileName === 'mixins') { %> + <% if (fileName === 'composition') { %>
  • Reusability & Composition

  • <% } %> <% if (fileName === 'deployment') { %> From df1036b3f876f9de068a0d27eec5611467711f61 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Mon, 16 Oct 2017 16:36:31 -0400 Subject: [PATCH 2/9] add note about template literals on composition page --- src/v2/guide/composition.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index a7387d1587..7922a2a736 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -6,6 +6,8 @@ order: 301 Vue offers many strategies for building components and sharing functionality between them. Below, we'll provide a description of each strategy, followed by more in-depth explanations of when and how to apply them. +

    Examples on this page include template literals, which are not supported in Internet Explorer, but make string templates much more readable.

    + ## Overview - **Merging** From 087a8910e12fdfd2005dad5453bd265cd6593efe Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 11 Nov 2017 12:00:47 -0500 Subject: [PATCH 3/9] clarify intro paragraph of composition.md --- src/v2/guide/composition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index 7922a2a736..28de48164a 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -4,7 +4,7 @@ type: guide order: 301 --- -Vue offers many strategies for building components and sharing functionality between them. Below, we'll provide a description of each strategy, followed by more in-depth explanations of when and how to apply them. +Vue offers many advanced strategies for building components and sharing functionality between them. Below, we'll provide a description of some common strategies, followed by more in-depth explanations of when and how to apply them.

    Examples on this page include template literals, which are not supported in Internet Explorer, but make string templates much more readable.

    From 0d704f6f37cd44583814b33e5fd077cbb6bb9836 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 11 Nov 2017 12:01:24 -0500 Subject: [PATCH 4/9] correct typo in composition.md --- src/v2/guide/composition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index 28de48164a..d45b9c23e7 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -85,10 +85,10 @@ var AppNote = { You might use it to display an error like this: ``` html - + Error! Something went wrong. - + ``` But there's a problem. Every time you display an error, you'll have to remember to add `type="danger"` and `Error!` in the `bold` slot. It's easy to make a mistake, resulting in inconsistent error messages. From 5028f428dfc211fa6622eb2e00666eefe3920201 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 11 Nov 2017 12:23:50 -0500 Subject: [PATCH 5/9] update composition.md close button examples to make more sense --- src/v2/guide/composition.md | 44 +++++-------------------------------- 1 file changed, 6 insertions(+), 38 deletions(-) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index d45b9c23e7..77c60ca050 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -120,7 +120,7 @@ var ErrorNote = { } ``` -This is the simplest option and sometimes it's appropriate, when two components only _incidentally_ share a lot in common. In this case, however, we probably want all the notes in our application to work mostly the same. For example, if we later added a close button to `AppNote`, we'd now have to add the exact same code to `ErrorNote` and any other variations we created. +This is the simplest option and sometimes it's appropriate, when two components only _incidentally_ share a lot in common. In this case, however, we probably want all the notes in our application to work mostly the same. For example, if we later added a button to expand/hide details to `AppNote`, we'd now have to add the exact same code to `ErrorNote` and any other variations we created. Fortunately, there's a better way. @@ -245,53 +245,21 @@ So why have both then? Technically, you _could_ get everything done just with th ### An Example Use Case -Remember the `AppNote` and `ErrorNote` components [from earlier](#An-Example-Use-Case)? In another application, we might actually want two mostly different components for these use cases: +Imagine we want to add a close button to multiple components. Upon click, this button would hide the component it's included in. We could create a mixin that provides a `` component wherever we want it to appear, like in this `AppNotification` component: ``` js -var AppNote = { - template: ` -
    - -
    - ` -} - -var ErrorNotification = { - template: ` -

    - Error! - -

    - ` -} -``` - -However, there might be some specific behavior we want to be shared, such as the ability to add a close button, which upon click, would hide the component. Then across any relevant components, we could include the mixin and add a `` wherever we want it to appear, like this: - -``` js -var AppNote = { - mixins: [closeMixin], - template: ` -
    - - -
    - ` -} - -var ErrorNotification = { +var AppNotification = { mixins: [closeMixin], template: ` -

    - Error! +

    -

    +
    ` } ``` -And here's mixin definition that actually makes that work: +And here's the mixin definition to make this work: ``` js var closeMixin = { From 13120b4870cc5627d35ff31ce472935638b9e608 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 11 Nov 2017 12:25:31 -0500 Subject: [PATCH 6/9] add link to render function page in composition.md --- src/v2/guide/composition.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index 77c60ca050..86aa41cd05 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -282,6 +282,7 @@ var closeMixin = { // Simulate a v-if on the root element beforeCreate: function () { // Save the original, compiled render function + // (https://vuejs.org/v2/guide/render-function.html) var origRender = this._render // Replace the original render function this._render = function _render () { From be6ab0db4d879c5345232cbb317f467b2c851db6 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 11 Nov 2017 13:24:38 -0500 Subject: [PATCH 7/9] add console.log example to demo global mixins --- src/v2/guide/composition.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index 86aa41cd05..2d1be792f2 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -313,6 +313,16 @@ You can also apply a mixin globally, but do this with caution!

    Global mixins affect every Vue instance and component, including third-party components.

    +For example, the code below defines a mixin that will log the creation of _every_ component instance in our app. + +``` js +Vue.mixin({ + created: function () { + console.log('A component was just created!') + } +}) +``` + One appropriate use case, however, is to inject processing logic for custom options, such as in the [VueFire plugin](https://github.com/vuejs/vuefire#usage): ``` js From 6e8be5acaadd053efdc6f1c067fe449b448447d2 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 11 Nov 2017 13:30:53 -0500 Subject: [PATCH 8/9] add note about higher order components on composition.md --- src/v2/guide/composition.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index 2d1be792f2..aac9af7877 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -427,6 +427,8 @@ function createComponent (options) { } ``` +> If you're familiar with React, their [higher-order components](https://reactjs.org/docs/higher-order-components.html) are one kind of factory function. + Factory functions allow you to better control and simplify how component variations are created. For example, it could be argued that the `ErrorNote` we [created earlier with extension](#An-Example-Use-Case) would be conceptually simpler as a factory function: ``` js From 46f4fa0b420e3e78f2247ffe9c12d71245a32704 Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 11 Nov 2017 13:35:26 -0500 Subject: [PATCH 9/9] add link to explanation of factory functions in composition.md --- src/v2/guide/composition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v2/guide/composition.md b/src/v2/guide/composition.md index aac9af7877..4729dff94b 100644 --- a/src/v2/guide/composition.md +++ b/src/v2/guide/composition.md @@ -417,7 +417,7 @@ Above, a developer has hijacked the component for a new kind of note that is not ## Factory Functions -**Factory functions** are functions that return component options, like this: +[**Factory functions**](https://medium.com/javascript-scene/javascript-factory-functions-with-es6-4d224591a8b1) are functions that return an object - in our case, component options: ``` js function createComponent (options) {