From 705fea0dc6eceb2c9a3f9e9bee3ca70d62acafe9 Mon Sep 17 00:00:00 2001 From: Yotam Date: Sun, 10 Jan 2021 08:49:34 +0200 Subject: [PATCH 001/322] Added slot to todo list component (#763) * Added slot to todo list component Added slot to todo list component to emphasize the fact that you can replace the content with custom content. As the sentence states, this won't work without scoped slots. * fix: changed wording on slot Co-authored-by: Natalia Tepluhina --- src/guide/component-slots.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/component-slots.md b/src/guide/component-slots.md index 85e8c35fea..beb6a8973f 100644 --- a/src/guide/component-slots.md +++ b/src/guide/component-slots.md @@ -247,7 +247,7 @@ app.component('todo-list', { }) ``` -We might want to replace the slot to customize it on parent component: +We might want to replace the `{{ item }}` with a `` to customize it on parent component: ```html From aefafd487fbc6579f0d67ea4cf2be29a00358338 Mon Sep 17 00:00:00 2001 From: Tim Date: Sun, 10 Jan 2021 07:50:38 +0100 Subject: [PATCH 002/322] Update component-slots.md (#792) Make it clearer that you can pass as many attributes as you need per slot. --- src/guide/component-slots.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/guide/component-slots.md b/src/guide/component-slots.md index beb6a8973f..4e9c2a5f2c 100644 --- a/src/guide/component-slots.md +++ b/src/guide/component-slots.md @@ -268,6 +268,16 @@ To make `item` available to the slot content provided by the parent, we can add ``` +You can bind as many attributes to the `slot` as you need: + +```html +
    +
  • + +
  • +
+``` + Attributes bound to a `` element are called **slot props**. Now, in the parent scope, we can use `v-slot` with a value to define a name for the slot props we've been provided: ```html From fe87afe058f88a06828095bc39686150694a60ea Mon Sep 17 00:00:00 2001 From: skirtle <65301168+skirtles-code@users.noreply.github.com> Date: Sun, 10 Jan 2021 06:53:29 +0000 Subject: [PATCH 003/322] fix: try to prevent Carbon Ads from causing problems (#798) --- src/.vuepress/theme/components/CarbonAds.vue | 24 ++++++++------------ src/.vuepress/theme/layouts/Layout.vue | 16 ------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/src/.vuepress/theme/components/CarbonAds.vue b/src/.vuepress/theme/components/CarbonAds.vue index c9c8f1f7f9..98e53cc30d 100644 --- a/src/.vuepress/theme/components/CarbonAds.vue +++ b/src/.vuepress/theme/components/CarbonAds.vue @@ -28,29 +28,28 @@ export default { From 5b9060a91a8f2a67b0f6abb49310c1fff175aed1 Mon Sep 17 00:00:00 2001 From: Ben Hong Date: Sun, 10 Jan 2021 01:54:46 -0500 Subject: [PATCH 004/322] fix: grammar and casing on video title (#795) --- src/guide/component-custom-events.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/guide/component-custom-events.md b/src/guide/component-custom-events.md index 5a7ef9be3c..322aecafa1 100644 --- a/src/guide/component-custom-events.md +++ b/src/guide/component-custom-events.md @@ -18,7 +18,7 @@ As with [props casing](/guide/component-props.html#prop-casing-camelcase-vs-keba ## Defining Custom Events -Watch a free video about Defining Custom Events on Vue School +Watch a free video on how to define custom events on Vue School Emitted events can be defined on the component via the `emits` option. @@ -81,7 +81,7 @@ app.component('my-component', { }, emits: ['update:title'], template: ` - @@ -114,7 +114,7 @@ app.component('user-name', { }, emits: ['update:firstName', 'update:lastName'], template: ` - @@ -153,7 +153,7 @@ app.component('my-component', { }, emits: ['update:modelValue'], template: ` - `, @@ -218,7 +218,7 @@ app.component('my-component', { props: ['description', 'descriptionModifiers'], emits: ['update:description'], template: ` - `, From 3f699dff18569229843691b8946057e7fe0f2ab9 Mon Sep 17 00:00:00 2001 From: Alexander Sokolov Date: Sun, 10 Jan 2021 09:55:32 +0300 Subject: [PATCH 005/322] Update transitions-enterleave.md (#797) --- src/guide/transitions-enterleave.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/transitions-enterleave.md b/src/guide/transitions-enterleave.md index 77ad1e25cc..c180e96d5e 100644 --- a/src/guide/transitions-enterleave.md +++ b/src/guide/transitions-enterleave.md @@ -7,7 +7,7 @@ Vue provides a variety of ways to apply transition effects when items are insert - use JavaScript to directly manipulate the DOM during transition hooks - integrate 3rd-party JavaScript animation libraries -On this page, we'll only cover entering, leaving, and list transitions, but you can see the next section for [managing state transitions](transitions-state.html). +On this page, we'll only cover entering, and leaving, but you can see the next sections for [list transitions](transitions-list.html) and [managing state transitions](transitions-state.html). ## Transitioning Single Elements/Components From 4a333dd299ab82ed1965e2e8fda9dfab5dd70963 Mon Sep 17 00:00:00 2001 From: skirtle <65301168+skirtles-code@users.noreply.github.com> Date: Sun, 10 Jan 2021 15:49:48 +0000 Subject: [PATCH 006/322] fix: wrap unintended template syntax with `v-pre` (#799) --- src/guide/component-slots.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guide/component-slots.md b/src/guide/component-slots.md index 4e9c2a5f2c..154d4f8a8d 100644 --- a/src/guide/component-slots.md +++ b/src/guide/component-slots.md @@ -247,7 +247,7 @@ app.component('todo-list', { }) ``` -We might want to replace the `{{ item }}` with a `` to customize it on parent component: +We might want to replace the `{{ item }}` with a `` to customize it on parent component: ```html @@ -320,7 +320,7 @@ Note that the abbreviated syntax for default slot **cannot** be mixed with named {{ slotProps.item }} - + From 4f4a5e6caf90ea1714e4d295633315271616ff7e Mon Sep 17 00:00:00 2001 From: Natalia Tepluhina Date: Mon, 11 Jan 2021 07:40:43 +0100 Subject: [PATCH 007/322] Add type inference for emitted events (#800) * feat: TS for emitted events * Update src/guide/typescript-support.md Co-authored-by: skirtle <65301168+skirtles-code@users.noreply.github.com> Co-authored-by: skirtle <65301168+skirtles-code@users.noreply.github.com> --- src/guide/typescript-support.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/guide/typescript-support.md b/src/guide/typescript-support.md index 8aa2c28ddb..c1a44de5a8 100644 --- a/src/guide/typescript-support.md +++ b/src/guide/typescript-support.md @@ -212,7 +212,7 @@ const Component = defineComponent({ type: Object as PropType, // Make sure to use arrow functions default: () => ({ - title: "Arrow Function Expression" + title: 'Arrow Function Expression' }), validator: (book: Book) => !!book.title }, @@ -221,7 +221,7 @@ const Component = defineComponent({ // Or provide an explicit this parameter default(this: void) { return { - title: "Function Expression" + title: 'Function Expression' } }, validator(this: void, book: Book) { @@ -232,6 +232,30 @@ const Component = defineComponent({ }) ``` +## Annotating emits + +We can annotate a payload for the emitted event. Also, all non-declared emitted events will throw a type error when called: + +```ts +const Component = defineComponent({ + emits: { + addBook(payload: { bookName: string }) { + // perform runtime validation + return payload.bookName.length > 0 + } + }, + methods: { + onSubmit() { + this.$emit('addBook', { + bookName: 123 // Type error! + }) + + this.$emit('non-declared-event') // Type error! + } + } +}) +``` + ## Using with Composition API On `setup()` function, you don't need to pass a typing to `props` parameter as it will infer types from `props` component option. From 9e3d61a99e97a3c8448b001a71ea5cef97fdb7f9 Mon Sep 17 00:00:00 2001 From: skirtle <65301168+skirtles-code@users.noreply.github.com> Date: Tue, 12 Jan 2021 07:16:17 +0000 Subject: [PATCH 008/322] docs: use destructuring to access createApp, etc. in render-function.md (#803) --- src/guide/render-function.md | 100 ++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/src/guide/render-function.md b/src/guide/render-function.md index 1c9ac18760..edf783e101 100644 --- a/src/guide/render-function.md +++ b/src/guide/render-function.md @@ -21,7 +21,9 @@ Anchored headings are used very frequently, we should create a component: The component must generate a heading based on the `level` prop, and we quickly arrive at this: ```js -const app = Vue.createApp({}) +const { createApp } = Vue + +const app = createApp({}) app.component('anchored-heading', { template: ` @@ -58,11 +60,13 @@ This template doesn't feel great. It's not only verbose, but we're duplicating ` While templates work great for most components, it's clear that this isn't one of them. So let's try rewriting it with a `render()` function: ```js -const app = Vue.createApp({}) +const { createApp, h } = Vue + +const app = createApp({}) app.component('anchored-heading', { render() { - return Vue.h( + return h( 'h' + this.level, // tag name {}, // props/attributes this.$slots.default() // array of children @@ -109,7 +113,7 @@ Or in a render function: ```js render() { - return Vue.h('h1', {}, this.blogTitle) + return h('h1', {}, this.blogTitle) } ``` @@ -120,7 +124,7 @@ And in both cases, Vue automatically keeps the page updated, even when `blogTitl Vue keeps the page updated by building a **virtual DOM** to keep track of the changes it needs to make to the real DOM. Taking a closer look at this line: ```js -return Vue.h('h1', {}, this.blogTitle) +return h('h1', {}, this.blogTitle) ``` What is the `h()` function returning? It's not _exactly_ a real DOM element. It returns a plain object which contains information describing to Vue what kind of node it should render on the page, including descriptions of any child nodes. We call this node description a "virtual node", usually abbreviated to **VNode**. "Virtual DOM" is what we call the entire tree of VNodes, built by a tree of Vue components. @@ -169,7 +173,9 @@ If there are no props then the children can usually be passed as the second argu With this knowledge, we can now finish the component we started: ```js -const app = Vue.createApp({}) +const { createApp, h } = Vue + +const app = createApp({}) /** Recursively get text from children nodes */ function getChildrenTextContent(children) { @@ -192,8 +198,8 @@ app.component('anchored-heading', { .replace(/\W+/g, '-') // replace non-word characters with dash .replace(/(^-|-$)/g, '') // remove leading and trailing dashes - return Vue.h('h' + this.level, [ - Vue.h( + return h('h' + this.level, [ + h( 'a', { name: headingId, @@ -220,8 +226,8 @@ All VNodes in the component tree must be unique. That means the following render ```js render() { - const myParagraphVNode = Vue.h('p', 'hi') - return Vue.h('div', [ + const myParagraphVNode = h('p', 'hi') + return h('div', [ // Yikes - duplicate VNodes! myParagraphVNode, myParagraphVNode ]) @@ -232,9 +238,9 @@ If you really want to duplicate the same element/component many times, you can d ```js render() { - return Vue.h('div', + return h('div', Array.from({ length: 20 }).map(() => { - return Vue.h('p', 'hi') + return h('p', 'hi') }) ) } @@ -246,16 +252,20 @@ To create a VNode for a component, the first argument passed to `h` should be th ```js render() { - return Vue.h(ButtonCounter) + return h(ButtonCounter) } ``` If we need to resolve a component by name then we can call `resolveComponent`: ```js +const { h, resolveComponent } = Vue + +// ... + render() { - const ButtonCounter = Vue.resolveComponent('ButtonCounter') - return Vue.h(ButtonCounter) + const ButtonCounter = resolveComponent('ButtonCounter') + return h(ButtonCounter) } ``` @@ -269,7 +279,7 @@ components: { ButtonCounter }, render() { - return Vue.h(Vue.resolveComponent('ButtonCounter')) + return h(resolveComponent('ButtonCounter')) } ``` @@ -277,7 +287,7 @@ Rather than registering a component by name and then looking it up we can use it ```js render() { - return Vue.h(ButtonCounter) + return h(ButtonCounter) } ``` @@ -300,11 +310,11 @@ This could be rewritten with JavaScript's `if`/`else` and `map()` in a render fu props: ['items'], render() { if (this.items.length) { - return Vue.h('ul', this.items.map((item) => { - return Vue.h('li', item.name) + return h('ul', this.items.map((item) => { + return h('li', item.name) })) } else { - return Vue.h('p', 'No items found.') + return h('p', 'No items found.') } } ``` @@ -319,7 +329,7 @@ The `v-model` directive is expanded to `modelValue` and `onUpdate:modelValue` pr props: ['modelValue'], emits: ['update:modelValue'], render() { - return Vue.h(SomeComponent, { + return h(SomeComponent, { modelValue: this.modelValue, 'onUpdate:modelValue': value => this.$emit('update:modelValue', value) }) @@ -332,7 +342,7 @@ We have to provide a proper prop name for the event handler, e.g., to handle `cl ```js render() { - return Vue.h('div', { + return h('div', { onClick: $event => console.log('clicked', $event.target) }) } @@ -346,7 +356,7 @@ For example: ```javascript render() { - return Vue.h('input', { + return h('input', { onClickCapture: this.doThisInCapturingMode, onKeyupOnce: this.doThisOnce, onMouseoverOnceCapture: this.doThisOnceInCapturingMode @@ -368,7 +378,7 @@ Here's an example with all of these modifiers used together: ```js render() { - return Vue.h('input', { + return h('input', { onKeyUp: event => { // Abort if the element emitting the event is not // the element the event is bound to @@ -394,7 +404,7 @@ We can access slot contents as arrays of VNodes from [`this.$slots`](../api/inst ```js render() { // `
` - return Vue.h('div', this.$slots.default()) + return h('div', this.$slots.default()) } ``` @@ -402,7 +412,7 @@ render() { props: ['message'], render() { // `
` - return Vue.h('div', this.$slots.default({ + return h('div', this.$slots.default({ text: this.message })) } @@ -413,14 +423,14 @@ For component VNodes, we need to pass the children to `h` as an object rather th ```js render() { // `
{{ props.text }}
` - return Vue.h('div', [ - Vue.h( - Vue.resolveComponent('child'), + return h('div', [ + h( + resolveComponent('child'), null, // pass `slots` as the children object // in the form of { name: props => VNode | Array } { - default: (props) => Vue.h('span', props.text) + default: (props) => h('span', props.text) } ) ]) @@ -433,10 +443,10 @@ The slots are passed as functions, allowing the child component to control the c // `{{ text }}` render() { // Calls to resolveComponent should be outside the slot function - const Button = Vue.resolveComponent('MyButton') - const Icon = Vue.resolveComponent('MyIcon') + const Button = resolveComponent('MyButton') + const Icon = resolveComponent('MyIcon') - return Vue.h( + return h( Button, null, { @@ -445,7 +455,7 @@ render() { // Reactive properties should be read inside the slot function // so that they become dependencies of the child's rendering return [ - Vue.h(Icon, { name: this.icon }), + h(Icon, { name: this.icon }), this.text ] } @@ -458,7 +468,7 @@ If a component receives slots from its parent, they can be passed on directly to ```js render() { - return Vue.h(Panel, null, this.$slots) + return h(Panel, null, this.$slots) } ``` @@ -466,7 +476,7 @@ They can also be passed individually or wrapped as appropriate: ```js render() { - return Vue.h( + return h( Panel, null, { @@ -478,7 +488,7 @@ render() { default: (props) => { const children = this.$slots.default ? this.$slots.default(props) : [] - return children.concat(Vue.h('div', 'Extra child')) + return children.concat(h('div', 'Extra child')) } } ) @@ -490,10 +500,14 @@ render() { Behind the scenes, templates use `resolveDynamicComponent` to implement the `is` attribute. We can use the same function if we need all the flexibility provided by `is` in our `render` function: ```js +const { h, resolveDynamicComponent } = Vue + +// ... + // `` render() { - const Component = Vue.resolveDynamicComponent(this.name) - return Vue.h(Component) + const Component = resolveDynamicComponent(this.name) + return h(Component) } ``` @@ -508,7 +522,7 @@ If the VNode is always an HTML element then we can pass its name directly to `h` ```js // `` render() { - return Vue.h(this.bold ? 'strong' : 'em') + return h(this.bold ? 'strong' : 'em') } ``` @@ -521,13 +535,13 @@ Much like a `