From 96c72ff90e91dbbdba2e587c33fd0cc5b3ad2d32 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 29 Jun 2020 15:57:59 -0400 Subject: [PATCH 01/20] sfc improvements --- active-rfcs/0000-sfc-component-sugar.md | 128 +++++++++++ active-rfcs/0000-sfc-script-setup.md | 278 ++++++++++++++++++++++++ active-rfcs/0000-sfc-style-variables.md | 82 +++++++ 3 files changed, 488 insertions(+) create mode 100644 active-rfcs/0000-sfc-component-sugar.md create mode 100644 active-rfcs/0000-sfc-script-setup.md create mode 100644 active-rfcs/0000-sfc-style-variables.md diff --git a/active-rfcs/0000-sfc-component-sugar.md b/active-rfcs/0000-sfc-component-sugar.md new file mode 100644 index 00000000..ebafc46b --- /dev/null +++ b/active-rfcs/0000-sfc-component-sugar.md @@ -0,0 +1,128 @@ +- Start Date: 2020-06-29 +- Target Major Version: 2.x & 3.x +- Reference Issues: N/A +- Implementation PR: N/A + +# Summary + +Syntax sugar for reducing component import and registration boilerplate in Single File Components. + +# Basic example + +```html + + + +``` + +# Motivation + +Currently in SFCs you have to import a component and then pass it to the `export default { components: { ... } }` hash, which leads to a lot of redundancy: for a single component we are repeating its name 3 times: in the imported binding, the file name, and the components option. + +The `` sugar requires the name of each component to be specified only once. + +# Detailed design + +## Normal Components + +**Before** + +```html + + + +``` + +**After** + +```html + + + +``` + +## Async Components + +**Before** + +```html + + + +``` + +**After** + +```html + + + +``` + +## Component Renaming + +By default, the component's locally registered name is inferred from its filename. But they can be renamed locally: + +```html + + + +``` + +# Drawbacks + +This would require updates in tools that parse SFC content for template analysis - e.g. Vetur & `@vuedx`. + +However, since this information is going to be provided directly by `@vue/compiler-sfc` in the parsed SFC descriptor, it should remove some extra complexity from these tools as well. + +# Alternatives + +We considered implicitly registering imported components: + +```html + + + +``` + +However, this approach has a few issues: + +- `Foo` is unused in the script scope, making it annoying when using linter rules that check for unused variables. + +- The only safe assumption about an import being a Vue component is the `.vue` extension, which makes it unable to support components authored in non-SFC formats (e.g. `import Foo from './Foo/ts'` can be a component but we can't really be sure). + +# Adoption strategy + +This is a fully backwards compatible new feature. However, we probably want to warn users against mixing the manual imports and `` tags so that tools that rely on the extracted information can make safer assumptions. diff --git a/active-rfcs/0000-sfc-script-setup.md b/active-rfcs/0000-sfc-script-setup.md new file mode 100644 index 00000000..5f4acd01 --- /dev/null +++ b/active-rfcs/0000-sfc-script-setup.md @@ -0,0 +1,278 @@ +- Start Date: 2020-06-29 +- Target Major Version: 2.x & 3.x +- Reference Issues: N/A +- Implementation PR: N/A + +# Summary + +Introduce a compile step for ` +``` + +# Motivation + +When authoring components using the Composition API, very often `setup` is the only option that's being used. This results in some unnecessary boilerplate: + +```js +import { ref } from 'vue' + +export default { + setup() { + const count = ref(0) + const inc = () => count.value++ + + return { + count, + inc + } + } +} +``` + +In addition, one of the most often complained about aspect of the Composition API is the necessity to repeat all the bindings that need to be exposed to the render context using a return object. + +This RFC introduces a compiler-powered alternative for the usage of ` +``` + +This will compile to: + +```js +import { computed } from 'vue' + +const $options = { + props: { + msg: String + }, + inheritAttrs: false +} + +export default { + ...$options, + setup($props) { + const computedMsg = computed(() => $props.msg + '!!!') + + return { + computedMsg + } + } +} +``` + +## With TypeScript + +` +``` + +The above will compile to: + +```vue + +``` + +- Runtime props declaration is automatically generated from TS typing to remove the need of double declaration and still ensure correct runtime behavior. (It is force casted into `undefined` to ensure the user provided type is used in the emitted code) + +- The emitted code is still TypeScript with valid typing, which can be further processed by other tools. + +# Drawbacks + +This is yet another way of authoring components, and it requires understanding the Composition API first. + +# Adoption strategy + +This is a fully backwards compatible new feature. + + +# Unresolved Questions + +## Magic `let` bindings + +It is technically possible to compile `let` bindings in a way so that root level `let` bindings are implicitly reactive: + +```vue + +``` + +can be compiled into: + +```vue + +``` + +This makes the code even more succinct and removes the need to use `ref` in the root scope of the component. However, this may be a bit too magical and there are a number of consistency issues if we enable this behavior: + +- Objects are not implicitly reactive: + + ```js + export const state = { count: 0 } + + export function increment() { + // doesn't work + state.count++ + } + ``` + + Making root scope objects deeply reactive by default can lead to potential performance problems, since the user may declare a 3rd party object of unknown size. + +- Doesn't work inside nested functions: + + ```js + function useFeature() { + // not reactive, since it's not root level + let count = 1 + const inc = () => count++ + + return { + count, + inc + } + } + ``` + + If we also compile functions inside ` + + +``` + +# Motivation + +Vue SFC styles provide straightforward CSS collocation and encapsulation, but it is purely static - which means up to this point we have no capability of dynamically updating the styles at runtime based on the component's state. + +Now with [most modern browsers supporting native CSS variables](https://caniuse.com/#feat=css-variables), we can leverage it to easily connect the component's state and styles. + +# Detailed design + +The ` +``` + +However, this has a few disadvantages: + +- Intention is less explicit; + +- Requires a full PostCSS parse first to extract matching variables before the main script can be processed. With `:vars` being declared on the ` +``` + +The inner CSS will be compiled into: -## Auto Detect Variables +```css +h1 { + color: var(--6b53742-color); +} +``` -One of the considered alternatives is auto detecting variables to inject using a naming convention: +**Note that when `scoped` and `vars` are both present, all CSS variables are considered to be local.** In order to reference a global CSS variable here, use the `global:` prefix: ```html - ``` -However, this has a few disadvantages: +The above compiles into: -- Intention is less explicit; +```css +h1 { + color: var(--6b53742-color); + font-size: var(--fontSize); +} +``` -- Requires a full PostCSS parse first to extract matching variables before the main script can be processed. With `:vars` being declared on the ` ``` @@ -39,61 +45,103 @@ Now with [most modern browsers supporting native CSS variables](https://caniuse. # Detailed design -The ` ``` -## Usage with ` + ``` -```html - ``` -The inner CSS will be compiled into: +...where the `useCssVars` runtime helper sets up a `watchEffect` to reactively apply the variables to the DOM. + +# Drawbacks + +## Compilation cost + +The compilation strategy requires the script compilation to do a parse of the ` ``` -The above compiles into: +Technically, the compilation strategy can handle this just fine by trimming the bound expression, but it can be a bit of an annoyance to users. + +TODO: file prettier issue + +# Alternatives + +A possible alternative syntax is ```css -h1 { - color: var(--6b53742-color); - font-size: var(--fontSize); +.text { + color: var(--{{ color }}); + font-size: var(--{{ font.size }}); } ``` -When there is only `scoped` and no `vars`, CSS variables are untouched. This preserves backwards compatibility. +However, [cssom](https://www.npmjs.com/package/cssom), one of the widely used CSS parsers, will strip away the curly braces inside `var()`. This may not be a real issue since `cssom` is primarily used in `jsdom` and not in IDE tooling. # Adoption strategy From ddeb68f8ea8792f8541d5c0f562a9f3e7a7d00bc Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 16 Nov 2020 13:43:47 -0500 Subject: [PATCH 18/20] edits --- active-rfcs/0000-sfc-style-variables.md | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/active-rfcs/0000-sfc-style-variables.md b/active-rfcs/0000-sfc-style-variables.md index ea040d72..523dd89d 100644 --- a/active-rfcs/0000-sfc-style-variables.md +++ b/active-rfcs/0000-sfc-style-variables.md @@ -107,7 +107,9 @@ export default { ## Compilation cost -The compilation strategy requires the script compilation to do a parse of the ` ``` @@ -45,30 +45,36 @@ Now with [most modern browsers supporting native CSS variables](https://caniuse. # Detailed design -The ` ``` -It should be noted that CSS variables names technically must be valid CSS identifiers which cannot contain characters like `:` or `.`. However, the syntax proposed in this RFC is used purely as a compile-time hint and is **not** included in the final runtime CSS. In terms of tooling integration, all major CSS parsers in the ecosystem can parse `var()` correctly with arbitrary inner content (verified on [ASTExplorer](https://astexplorer.net/)). +As expected, this would bind the `color` declaration's value to the `color` property of the component's state, reactively. + +The `v-bind` function can support arbitrary JavaScript expressions inside, but since JavaScript expressions may contain characters that are not valid in CSS identifiers, they will need to be wrapped in quotes most of the time: + +```css +.text { + font-size: v-bind('theme.font.size'); +} +``` When such CSS variables are detected, the SFC compiler will perform the following: -1. Rewrite the variable to a hashed version. The above will be rewritten to: +1. Rewrite the `v-bind()` to a native `var()` with a hashed variable name. The above will be rewritten to: - ```html - + ```css + .text { + color: var(--6b53742-color); + font-size: var(--6b53742-theme_font_size); + } ``` Note the hashing will be applied in all cases, regardless of whether `