diff --git a/active-rfcs/0000-sfc-script-setup.md b/active-rfcs/0000-sfc-script-setup.md new file mode 100644 index 00000000..e42e85bb --- /dev/null +++ b/active-rfcs/0000-sfc-script-setup.md @@ -0,0 +1,342 @@ +- 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 ` +``` + +will be compiled into: + +```js +import { watchEffect } from 'vue' + +// setup is exported as a named export so it can be imported and tested +export function setup(props, { emit }) { + watchEffect(() => console.log(props.msg)) + emit('foo') + return {} +} + +export default { + setup, +} +``` + +## Exposing Components + +Exports from ` + + +``` + +## Declaring props or additional options + +One problem with ` +``` + +This will compile to: + +```js +import { computed } from 'vue' + +const __default__ = { + props: { + msg: String, + }, + inheritAttrs: false, +} + +export function setup(props) { + const computedMsg = computed(() => props.msg + '!!!') + + return { + computedMsg, + } +} + +__default__.setup = setup +export default __default__ +``` + +Since `export default` is hoisted outside of `setup()`, it cannot reference variables declared inside. For example, if the default export object references `computedMsg`, it will result in a compile-time error. + +## 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. + + - In dev mode, the compiler will try to infer corresponding runtime validation from the types. For example here `msg: String` is inferred from the `msg: string` type. + + - In prod mode, the compiler will generate the array format declaration to reduce bundle size (the props here will be compiled into `['msg']`) + + - The generated props declaration 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. + +Note that the `props` type declaration value cannot be an imported type, because the SFC compiler does not process external files to extract the prop names. + +## Usage alongside normal ` + + +``` + +the above will compile to: + +```js +import { ref } from 'vue' + +performGlobalSideEffect() + +export const named = 1 + +export function setup() { + const count = ref(0) + return { + count + } +} + +export default { setup } +``` + +## Transform API + +The `@vue/compiler-sfc` package exposes the `compileScript` method for processing ` +``` + +The `bindings` object will be: + +```js +{ + foo: 'setup', + bar: 'props' +} +``` + +This object can then be passed to the template compiler: + +```js +import { compile } from '@vue/compiler-dom' + +compile(template, { + bindingMetadata: bindings +}) +``` + +With the binding metadata available, the template compiler can generate code that directly access template variables from the corresponding source, without having to go through the render context proxy: + +```html +
{{ foo + bar }}
+``` + +```js +// code generated without bindingMetadata +// here _ctx is a Proxy object that dynamically dispatches property access +function render(_ctx) { + return createVNode('div', null, _ctx.foo + _ctx.bar) +} + +// code generated with bindingMetadata +// bypasses the render context proxy +function render(_ctx, _cache, $setup, $props, $data) { + return createVNode('div', null, $setup.foo + $props.bar) +} +``` + +## Usage Restrictions + +Due to the difference in module execution semantics, code 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 ` +``` + +The inner CSS will be compiled into: + +```css +h1 { + color: var(--6b53742-color); +} +``` + +**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 + +``` + +The above compiles into: + +```css +h1 { + color: var(--6b53742-color); + font-size: var(--fontSize); +} +``` + +When there is only `scoped` and no `vars`, CSS variables are untouched. This preserves backwards compatibility. + +# Adoption strategy + +This is a fully backwards compatible new feature. However, we should make it clear that it relies on native CSS variables so the user needs to be aware of the browser support range.