From f06663845d731ad767e53047a81eabb611b1bea5 Mon Sep 17 00:00:00 2001 From: Stas Lashmanov Date: Thu, 27 Feb 2020 02:18:06 +0300 Subject: [PATCH 1/8] Add component's public API RFC --- active-rfcs/0000-component's-public-api.md | 307 +++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 active-rfcs/0000-component's-public-api.md diff --git a/active-rfcs/0000-component's-public-api.md b/active-rfcs/0000-component's-public-api.md new file mode 100644 index 00000000..5a44e60f --- /dev/null +++ b/active-rfcs/0000-component's-public-api.md @@ -0,0 +1,307 @@ +- Start Date: (fill me in with today's date, 2020-02-27) +- Target Major Version: 3.x +- Reference Issues: (fill in existing related issues, if any) +- Implementation PR: (leave this empty) + +# Summary + +Use `provide` to define component's public API available for parent components via `refs`. + +# Basic example + +Right now you can access the whole component's context without any limitations when using `refs`. + +```html + + + +``` + +```html + + + +``` + +After the change you'll be required to expose your data and methods explicitly. + +```html + + + +``` + +```html + + + +``` + +# Motivation + +Right now component's context is free to access by anyone and that brings up a number of issues: + +1. You can not guarantee consistent component's behaviour due to external modifications. You can modify components data and won't be able to tell where that change came from. +2. There's no contract between receiver and provider components. This could lead to refactoring problems when there's an unused method within the component, but it's required by another component and there's no easy way to tell that. +3. No clear separation of data that is required by the component itself and other components. + +To fix these issues components would be required to explicitly declare their public interface. + +# Detailed design + +Components should not be able to directly access other components context anymore. To declare component's public interface use `provide` and pass any data that should be exposed. The object returned in `provide` would serve as a main access point for the accessor component. To eliminate conflicts and provide better cohesion use `Symbol` for exposed data property names. + +A component with such an interface could look like this: + +```html + + + +``` + +This interface could be utilized as following: + +```html + + + +``` + +To expose refs use `mounted` hook and object as a wrapper to preserve reactivity. + +```html + + + +``` + +Or function refs: + +```html + + + +``` + +Example using Composition API: + +```html + + + +``` + +You'll then be able to use it as a ref: + +```html + + + +``` + +# Drawbacks + +* Should be triggered after data init, `refs` then would require a lot of fiddling around to preserve reactivity (wrapping exposed values inside an object at a minimum) +* Provides value down the render tree as a side-effect, which may not be the desired behaviour + +# Alternatives + +* Use dedicated hook instead of `provide` but with exactly the same API. Could be called `expose`. + + **Pros**: + + * Does not cause conflicts with other provisions + * Clear separation of concerns + + **Cons**: + + * One more option to learn about + * Could be confusing to pick one between the `provide` and `expose` + * Does not fix the issue with `refs` + +* Use events to expose your component's public interface: + ```html + + + + ``` + + **Pros**: + + * Does not require any API change + * Easy to implement + * Fixes `refs` issue + + **Cons**: + + * Can not be enforced by the framework (unless context is always unavailable in refs) + * Forces to be extra careful with context + * Creates an unnecessary data flow + + +# Adoption strategy + +Components containing any data required by the parent component via `refs` would be required to explicitly declare their public interface. \ No newline at end of file From 08f036ef78f98311ad0719978a332b0750f0e637 Mon Sep 17 00:00:00 2001 From: Stas Lashmanov Date: Thu, 27 Feb 2020 03:15:20 +0300 Subject: [PATCH 2/8] Add alternative ref example --- active-rfcs/0000-component's-public-api.md | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/active-rfcs/0000-component's-public-api.md b/active-rfcs/0000-component's-public-api.md index 5a44e60f..fa70aa9a 100644 --- a/active-rfcs/0000-component's-public-api.md +++ b/active-rfcs/0000-component's-public-api.md @@ -199,6 +199,45 @@ Or function refs: ``` +Alternative way of accessing refs: + +```html + + + +``` + +```html + + + +``` + Example using Composition API: ```html From 508b4ff03ae5b2a391f28c69e6356f1e4e751958 Mon Sep 17 00:00:00 2001 From: Stas Lashmanov Date: Thu, 27 Feb 2020 03:27:06 +0300 Subject: [PATCH 3/8] Typo --- active-rfcs/0000-component's-public-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active-rfcs/0000-component's-public-api.md b/active-rfcs/0000-component's-public-api.md index fa70aa9a..0f2ba91d 100644 --- a/active-rfcs/0000-component's-public-api.md +++ b/active-rfcs/0000-component's-public-api.md @@ -1,4 +1,4 @@ -- Start Date: (fill me in with today's date, 2020-02-27) +- Start Date: 2020-02-27 - Target Major Version: 3.x - Reference Issues: (fill in existing related issues, if any) - Implementation PR: (leave this empty) From 3cc7f14e8c5c6b15bbbbaccb08399cba43a64414 Mon Sep 17 00:00:00 2001 From: Stas Lashmanov Date: Thu, 27 Feb 2020 03:56:25 +0300 Subject: [PATCH 4/8] Add performance penalty to drawbacks section --- active-rfcs/0000-component's-public-api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/active-rfcs/0000-component's-public-api.md b/active-rfcs/0000-component's-public-api.md index 0f2ba91d..f32facbf 100644 --- a/active-rfcs/0000-component's-public-api.md +++ b/active-rfcs/0000-component's-public-api.md @@ -292,6 +292,7 @@ You'll then be able to use it as a ref: * Should be triggered after data init, `refs` then would require a lot of fiddling around to preserve reactivity (wrapping exposed values inside an object at a minimum) * Provides value down the render tree as a side-effect, which may not be the desired behaviour +* This could cause performance penalty if any injections are used since we have to cycle through every provision in the vnode tree from bottom to top # Alternatives From 793f37d3e336db0cb85a60f8f13a02d28e405ca3 Mon Sep 17 00:00:00 2001 From: Stas Lashmanov Date: Fri, 28 Feb 2020 03:09:28 +0300 Subject: [PATCH 5/8] Introduce expose api, scrap out provide overload --- active-rfcs/0000-component's-public-api.md | 282 +++++++++++++-------- 1 file changed, 171 insertions(+), 111 deletions(-) diff --git a/active-rfcs/0000-component's-public-api.md b/active-rfcs/0000-component's-public-api.md index f32facbf..12395110 100644 --- a/active-rfcs/0000-component's-public-api.md +++ b/active-rfcs/0000-component's-public-api.md @@ -5,22 +5,22 @@ # Summary -Use `provide` to define component's public API available for parent components via `refs`. +Introduce a new `expose` option to declare component's public API available for parent components via `refs`. Restrict access to component's context by default. # Basic example -Right now you can access the whole component's context without any limitations when using `refs`. +At the moment you can access the whole component's context without any limitations when using `refs`. ```html @@ -28,14 +28,14 @@ Right now you can access the whole component's context without any limitations w ```html @@ -45,21 +45,16 @@ After the change you'll be required to expose your data and methods explicitly. ```html @@ -67,15 +62,15 @@ After the change you'll be required to expose your data and methods explicitly. ```html @@ -93,48 +88,147 @@ To fix these issues components would be required to explicitly declare their pub # Detailed design -Components should not be able to directly access other components context anymore. To declare component's public interface use `provide` and pass any data that should be exposed. The object returned in `provide` would serve as a main access point for the accessor component. To eliminate conflicts and provide better cohesion use `Symbol` for exposed data property names. +Components should not be able to directly access other components context anymore. To declare component's public interface authors must use a new `expose` options and describe the data that should be exposed. + +## `expose` option + +A several `expose` configurations should be supported. -A component with such an interface could look like this: +### Array of strings ```html - + +``` + +### Object syntax +```html +``` + +### Function + +Functional configuration is more complicated because you can easily loose reactivity there. + +```html + +``` + +To solve this issue you could either go with the Composition API or wrap your data inside an object. + +```html + ``` -This interface could be utilized as following: +**Composition API example** + +```html + +``` + +The exposed interface above could be utilized as following: ```html @@ -151,7 +245,7 @@ To expose refs use `mounted` hook and object as a wrapper to preserve reactivity export const INPUT_EXPOSED = Symbol() export default { - provide() { + expose() { const { exposed } = this return { [INPUT_EXPOSED]: exposed @@ -182,7 +276,7 @@ Or function refs: export const INPUT_EXPOSED = Symbol() export default { - provide() { + expose() { const { exposed } = this return { [INPUT_EXPOSED]: exposed @@ -211,7 +305,7 @@ Alternative way of accessing refs: export default { name: 'MyInput', - provide() { + expose() { return { [GET_INPUT_REF]: () => { return this.$refs.input @@ -238,78 +332,15 @@ Alternative way of accessing refs: ``` -Example using Composition API: - -```html - - - -``` - -You'll then be able to use it as a ref: - -```html - - - -``` - # Drawbacks -* Should be triggered after data init, `refs` then would require a lot of fiddling around to preserve reactivity (wrapping exposed values inside an object at a minimum) -* Provides value down the render tree as a side-effect, which may not be the desired behaviour -* This could cause performance penalty if any injections are used since we have to cycle through every provision in the vnode tree from bottom to top +* `expose` should be executed after data init, `refs` then would require a lot of fiddling around to preserve reactivity (wrapping exposed values with an object at a minimum) +* Could be difficult to implement +* Not backwards compatible (but could only warn in compatibility build for example) # Alternatives -* Use dedicated hook instead of `provide` but with exactly the same API. Could be called `expose`. - - **Pros**: - - * Does not cause conflicts with other provisions - * Clear separation of concerns - - **Cons**: - - * One more option to learn about - * Could be confusing to pick one between the `provide` and `expose` - * Does not fix the issue with `refs` - -* Use events to expose your component's public interface: +Using events to expose your component's public interface: ```html