diff --git a/src/.vuepress/config.js b/src/.vuepress/config.js index e16ae1cc03..e35d0c14c1 100644 --- a/src/.vuepress/config.js +++ b/src/.vuepress/config.js @@ -54,7 +54,22 @@ const sidebar = { children: ['writing-guide'] } ], - api: ['/api/application-config', '/api/application-api'] + api: [ + '/api/application-config', + '/api/application-api', + { + title: 'Options', + collapsable: false, + children: [ + '/api/options-data', + '/api/options-dom', + '/api/options-lifecycle-hooks', + '/api/options-assets', + '/api/options-composition', + '/api/options-misc' + ] + } + ] } module.exports = { @@ -65,22 +80,22 @@ module.exports = { 'link', { href: 'https://use.fontawesome.com/releases/v5.13.0/css/all.css', - rel: 'stylesheet', - }, + rel: 'stylesheet' + } ], ['link', { rel: 'icon', href: '/logo.png' }], [ 'script', { - src: 'https://player.vimeo.com/api/player.js', - }, + src: 'https://player.vimeo.com/api/player.js' + } ], [ 'script', { src: 'https://extend.vimeocdn.com/ga/72160148.js', - defer: 'defer', - }, + defer: 'defer' + } ] ], themeConfig: { diff --git a/src/api/application-api.md b/src/api/application-api.md index a0e800c032..8e2651ca97 100644 --- a/src/api/application-api.md +++ b/src/api/application-api.md @@ -200,7 +200,7 @@ app.mount('#my-app') - **Details:** - This option is used together with [`inject`](TODO:inject) are used together to allow an ancestor component to serve as a dependency injector for all its descendants, regardless of how deep the component hierarchy is, as long as they are in the same parent chain. + This option is [used together with `inject`](../api/options-composition.html#provide-inject) to allow an ancestor component to serve as a dependency injector for all its descendants, regardless of how deep the component hierarchy is, as long as they are in the same parent chain. The `provide` option should be an object or a function that returns an object. This object contains the properties that are available for injection into its descendants. You can use ES2015 Symbols as keys in this object, but only in environments that natively support `Symbol` and `Reflect.ownKeys`. diff --git a/src/api/options-assets.md b/src/api/options-assets.md new file mode 100644 index 0000000000..06b1364b62 --- /dev/null +++ b/src/api/options-assets.md @@ -0,0 +1,21 @@ +# Assets + +## directives + +- **Type:** `Object` + +- **Details:** + + A hash of directives to be made available to the Vue instance. + +- **See also:** [Custom Directives](../guide/custom-directive.html) + +## components + +- **Type:** `Object` + +- **Details:** + + A hash of components to be made available to the Vue instance. + +- **See also:** [Components](../guide/component-basics.html) diff --git a/src/api/options-composition.md b/src/api/options-composition.md new file mode 100644 index 0000000000..375aa79a31 --- /dev/null +++ b/src/api/options-composition.md @@ -0,0 +1,321 @@ +# Composition + +## mixins + +- **Type:** `Array` + +- **Details:** + + The `mixins` option accepts an array of mixin objects. These mixin objects can contain instance options like normal instance objects, and they will be merged against the eventual options using the certain option merging logic. For example, if your mixin contains a `created` hook and the component itself also has one, both functions will be called. + + Mixin hooks are called in the order they are provided, and called before the component's own hooks. + +- **Example:** + + ```js + const mixin = { + created: function() { + console.log(1) + } + } + + Vue.createApp({ + created() { + console.log(2) + }, + mixins: [mixin] + }) + + // => 1 + // => 2 + ``` + +- **See also:** [Mixins](../guide/mixins.html) + +## extends + +- **Type:** `Object | Function` + +- **Details:** + + Allows declaratively extending another component (could be either a plain options object or a constructor). This is primarily intended to make it easier to extend between single file components. + + This is similar to `mixins`. + +- **Example:** + + ```js + const CompA = { ... } + + // extend CompA without having to call `Vue.extend` on either + const CompB = { + extends: CompA, + ... + } + ``` + +## provide / inject + +- **Type:** + + - **provide:** `Object | () => Object` + - **inject:** `Array | { [key: string]: string | Symbol | Object }` + +- **Details:** + + This pair of options are used together to allow an ancestor component to serve as a dependency injector for all its descendants, regardless of how deep the component hierarchy is, as long as they are in the same parent chain. If you are familiar with React, this is very similar to React's `context` feature. + + The `provide` option should be an object or a function that returns an object. This object contains the properties that are available for injection into its descendants. You can use ES2015 Symbols as keys in this object, but only in environments that natively support `Symbol` and `Reflect.ownKeys`. + + The `inject` option should be either: + + - an array of strings, or + - an object where the keys are the local binding name and the value is either: + - the key (string or Symbol) to search for in available injections, or + - an object where: + - the `from` property is the key (string or Symbol) to search for in available injections, and + - the `default` property is used as fallback value + + > Note: the `provide` and `inject` bindings are NOT reactive. This is intentional. However, if you pass down a reactive object, properties on that object do remain reactive. + +- **Example:** + + ```js + // parent component providing 'foo' + const Provider = { + provide: { + foo: 'bar' + } + // ... + } + + // child component injecting 'foo' + const Child = { + inject: ['foo'], + created() { + console.log(this.foo) // => "bar" + } + // ... + } + ``` + + With ES2015 Symbols, function `provide` and object `inject`: + + ```js + const s = Symbol() + + const Provider = { + provide() { + return { + [s]: 'foo' + } + } + } + + const Child = { + inject: { s } + // ... + } + ``` + + Using an injected value as the default for a prop: + + ```js + const Child = { + inject: ['foo'], + props: { + bar: { + default() { + return this.foo + } + } + } + } + ``` + + Using an injected value as data entry: + + ```js + const Child = { + inject: ['foo'], + data() { + return { + bar: this.foo + } + } + } + ``` + + Injections can be optional with default value: + + ```js + const Child = { + inject: { + foo: { default: 'foo' } + } + } + ``` + + If it needs to be injected from a property with a different name, use `from` to denote the source property: + + ```js + const Child = { + inject: { + foo: { + from: 'bar', + default: 'foo' + } + } + } + ``` + + Similar to prop defaults, you need to use a factory function for non-primitive values: + + ```js + const Child = { + inject: { + foo: { + from: 'bar', + default: () => [1, 2, 3] + } + } + } + ``` + +- **See also:** [Provide / Inject](../guide/component-provide-inject.html) + +## setup + +- **Type:** `Function` + +The `setup` function is a new component option. It serves as the entry point for using the Composition API inside components. + +- **Invocation Timing** + + `setup` is called right after the initial props resolution when a component instance is created. Lifecycle-wise, it is called before the [beforeCreate](./options-lifecycle-hooks.html#beforecreate) hook. + +- **Usage with Templates** + + If `setup` returns an object, the properties on the object will be merged on to the render context for the component's template: + + ```html + + + + ``` + + Note that [refs](TODO) returned from `setup` are automatically unwrapped when accessed in the template so there's no need for `.value` in templates. + +- **Usage with Render Functions / JSX** + + `setup` can also return a render function, which can directly make use of reactive state declared in the same scope: + + ```js + import { h, ref, reactive } from 'vue' + + export default { + setup() { + const count = ref(0) + const object = reactive({ foo: 'bar' }) + + return () => h('div', [count.value, object.foo]) + } + } + ``` + +- **Arguments** + + The function receives the resolved props as its first argument: + + ```js + export default { + props: { + name: String + }, + setup(props) { + console.log(props.name) + } + } + ``` + + Note this `props` object is reactive - i.e. it is updated when new props are passed in, and can be observed and reacted upon using `watchEffect` or `watch`: + + ```js + export default { + props: { + name: String + }, + setup(props) { + watchEffect(() => { + console.log(`name is: ` + props.name) + }) + } + } + ``` + + However, do NOT destructure the `props` object, as it will lose reactivity: + + ```js + export default { + props: { + name: String + }, + setup({ name }) { + watchEffect(() => { + console.log(`name is: ` + name) // Will not be reactive! + }) + } + } + ``` + + The `props` object is immutable for userland code during development (will emit warning if user code attempts to mutate it). + + The second argument provides a context object which exposes a selective list of properties that were previously exposed on `this`: + + ```js + const MyComponent = { + setup(props, context) { + context.attrs + context.slots + context.emit + } + } + ``` + + `attrs` and `slots` are proxies to the corresponding values on the internal component instance. This ensures they always expose the latest values even after updates so that we can destructure them without worrying accessing a stale reference: + + ```js + const MyComponent = { + setup(props, { attrs }) { + // a function that may get called at a later stage + function onClick() { + console.log(attrs.foo) // guaranteed to be the latest reference + } + } + } + ``` + + There are a number of reasons for placing `props` as a separate first argument instead of including it in the context: + + - It's much more common for a component to use `props` than the other properties, and very often a component uses only `props`. + + - Having `props` as a separate argument makes it easier to type it individually without messing up the types of other properties on the context. It also makes it possible to keep a consistent signature across `setup`, `render` and plain functional components with TSX support. + +- **See also:** [Composition API](TODO) diff --git a/src/api/options-data.md b/src/api/options-data.md new file mode 100644 index 0000000000..3e79026dc6 --- /dev/null +++ b/src/api/options-data.md @@ -0,0 +1,292 @@ +# Data + +## data + +- **Type:** `Function` + +- **Details:** + + The function that returns a data object for the Vue instance. In `data`, we don't recommend to observe objects with their own stateful behavior like browser API objects and prototype properties. A good idea would be to have here just a plain object that represents component data. + + Once observed, you can no longer add reactive properties to the root data object. It is therefore recommended to declare all root-level reactive properties upfront, before creating the instance. + + After the instance is created, the original data object can be accessed as `vm.$data`. The Vue instance also proxies all the properties found on the data object, so `vm.a` will be equivalent to `vm.$data.a`. + + Properties that start with `_` or `$` will **not** be proxied on the Vue instance because they may conflict with Vue's internal properties and API methods. You will have to access them as `vm.$data._property`. + +- **Example:** + + ```js + // direct instance creation + const data = { a: 1 } + + // The object is added to a Vue instance + const vm = Vue.createApp({ + data() { + return data + } + }).mount('#app') + + console.log(vm.a) // => 1 + ``` + + Note that if you use an arrow function with the `data` property, `this` won't be the component's instance, but you can still access the instance as the function's first argument: + + ```js + data: vm => ({ a: vm.myProp }) + ``` + +- **See also:** [Reactivity in Depth](../guide/reactivity.html) + +## props + +- **Type:** `Array | Object` + +- **Details:** + + A list/hash of attributes that are exposed to accept data from the parent component. It has an Array-based simple syntax and an alternative Object-based syntax that allows advanced configurations such as type checking, custom validation and default values. + + With Object-based syntax, you can use following options: + + - `type`: can be one of the following native constructors: `String`, `Number`, `Boolean`, `Array`, `Object`, `Date`, `Function`, `Symbol`, any custom constructor function or an array of those. Will check if a prop has a given type, and will throw a warning if it doesn't. [More information](../guide/component-props.html#prop-types) on prop types. + - `default`: `any` + Specifies a default value for the prop. If the prop is not passed, this value will be used instead. Object or array defaults must be returned from a factory function. + - `required`: `Boolean` + Defines if the prop is required. In a non-production environment, a console warning will be thrown if this value is truthy and the prop is not passed. + - `validator`: `Function` + Custom validator function that takes the prop value as the sole argument. In a non-production environment, a console warning will be thrown if this function returns a falsy value (i.e. the validation fails). You can read more about prop validation [here](../guide/component-props.html#prop-validation). + +- **Example:** + + ```js + const app = Vue.createApp({}) + + // simple syntax + app.component('props-demo-simple', { + props: ['size', 'myMessage'] + }) + + // object syntax with validation + app.component('props-demo-advanced', { + props: { + // type check + height: Number, + // type check plus other validations + age: { + type: Number, + default: 0, + required: true, + validator: value => { + return value >= 0 + } + } + } + }) + ``` + +- **See also:** [Props](../guide/component-props.html) + +## computed + +- **Type:** `{ [key: string]: Function | { get: Function, set: Function } }` + +- **Details:** + + Computed properties to be mixed into the Vue instance. All getters and setters have their `this` context automatically bound to the Vue instance. + + Note that if you use an arrow function with a computed property, `this` won't be the component's instance, but you can still access the instance as the function's first argument: + + ```js + computed: { + aDouble: vm => vm.a * 2 + } + ``` + + Computed properties are cached, and only re-computed on reactive dependency changes. Note that if a certain dependency is out of the instance's scope (i.e. not reactive), the computed property will **not** be updated. + +- **Example:** + + ```js + const app = Vue.createApp({ + data() { + return { a: 1 } + }, + computed: { + // get only + aDouble() { + return this.a * 2 + }, + // both get and set + aPlus: { + get() { + return this.a + 1 + }, + set(v) { + this.a = v - 1 + } + } + } + }) + + const vm = app.mount('#app') + console.log(vm.aPlus) // => 2 + vm.aPlus = 3 + console.log(vm.a) // => 2 + console.log(vm.aDouble) // => 4 + ``` + +- **See also:** [Computed Properties](../guide/computed.html) + +## methods + +- **Type:** `{ [key: string]: Function }` + +- **Details:** + + Methods to be mixed into the Vue instance. You can access these methods directly on the VM instance, or use them in directive expressions. All methods will have their `this` context automatically bound to the Vue instance. + + :::tip Note + Note that **you should not use an arrow function to define a method** (e.g. `plus: () => this.a++`). The reason is arrow functions bind the parent context, so `this` will not be the Vue instance as you expect and `this.a` will be undefined. + ::: + +- **Example:** + + ```js + const app = Vue.createApp({ + data() { + return { a: 1 } + }, + methods: { + plus() { + this.a++ + } + } + }) + + const vm = app.mount('#app') + + vm.plus() + console.log(vm.a) // => 2 + ``` + +- **See also:** [Event Handling](../guide/events.html) + +## watch + +- **Type:** `{ [key: string]: string | Function | Object | Array}` + +- **Details:** + + An object where keys are expressions to watch and values are the corresponding callbacks. The value can also be a string of a method name, or an Object that contains additional options. The Vue instance will call `$watch()` for each entry in the object at instantiation. + +- **Example:** + + ```js + const app = Vue.createApp({ + data() { + return { + a: 1, + b: 2, + c: { + d: 4 + }, + e: 'test', + f: 5 + } + }, + watch: { + a(val, oldVal) { + console.log(`new: ${val}, old: ${oldVal}`) + }, + // string method name + b: 'someMethod', + // the callback will be called whenever any of the watched object properties change regardless of their nested depth + c: { + handler(val, oldVal) { + console.log('c changed') + }, + deep: true + }, + // the callback will be called immediately after the start of the observation + e: { + handler(val, oldVal) { + console.log('e changed') + }, + immediate: true + }, + // you can pass array of callbacks, they will be called one-by-one + f: [ + 'handle1', + function handle2(val, oldVal) { + console.log('handle2 triggered') + }, + { + handler: function handle3(val, oldVal) { + console.log('handle3 triggered') + } + /* ... */ + } + ] + }, + methods: { + someMethod() { + console.log('b changed') + }, + handle1() { + console.log('handle 1 triggered') + } + } + }) + + const vm = app.mount('#app') + + vm.a = 3 // => new: 3, old: 1 + ``` + + ::: tip Note + Note that _you should not use an arrow function to define a watcher_ (e.g. `searchQuery: newValue => this.updateAutocomplete(newValue)`). The reason is arrow functions bind the parent context, so `this` will not be the Vue instance as you expect and `this.updateAutocomplete` will be undefined. + ::: + +- **See also:** [Watchers](../guide/computed.html#watchers) + +## emits + +- **Type:** `Array | Object` + +- **Details:** + + A list/hash of custom events that can be emitted from the component. It has an Array-based simple syntax and an alternative Object-based syntax that allows to configure an event validation. + + In Object-based syntax, the value of each property can either be `null` or a validator function. The validation function will receive the additional arguments passed to the `$emit` call. For example, if `this.$emit('foo', 1)` is called, the corresponding validator for `foo` will receive the argument `1`. The validator function should return a boolean to indicate whether the event arguments are valid. + +- **Usage:** + + ```js + const app = Vue.createApp({}) + + // Array syntax + app.component('todo-item', { + emits: ['check'], + created() { + this.$emit('check') + } + }) + + // Object syntax + app.component('reply-form', { + emits: { + // no validation + click: null, + + // with validation + submit: payload => { + if (payload.email && payload.password) { + return true + } else { + console.warn(`Invalid submit event payload!`) + return false + } + } + } + }) + ``` diff --git a/src/api/options-dom.md b/src/api/options-dom.md new file mode 100644 index 0000000000..a101afa87a --- /dev/null +++ b/src/api/options-dom.md @@ -0,0 +1,65 @@ +# DOM + +## template + +- **Type:** `string` + +- **Details:** + + A string template to be used as the markup for the Vue instance. The template will **replace** the mounted element. Any existing markup inside the mounted element will be ignored, unless content distribution slots are present in the template. + + If the string starts with `#` it will be used as a `querySelector` and use the selected element's innerHTML as the template string. This allows the use of the common `