Skip to content

Vue 3 JSX Design #141

Closed
Closed
@Amour1688

Description

@Amour1688
Member

Edit by @yyx990803 : there are currently two JSX transform implementations for Vue 3 with slightly differing syntax (for Vue specific features). We are using this thread to unify the design and land on an official specification of how Vue features should be handled in JSX.

Babel JSX Transform [Github]
alpha

  • patchFlags
  • same as Vue 3 Compiler
  • compatible with Vue 2.x

Syntax

Content

functional component

const App = () => <div></div>

with render

const App = {
  render() {
    return <div>Vue 3.0</div>
  }
}
const App = defineComponent(() => {
  const count = ref(0);

  const inc = () => {
    count.value++;
  };

  return () => (
    <div onClick={inc}>
      {count.value}
    </div>
  )
})

Fragment

const App = () => (
  <>
    <span>I'm</span>
    <span>Fragment</span>
  </>
)

Attributes/Props

const App = () => <input type="email" />

with a dynamic binding:

const placeholderText = 'email'
const App = () => (
  <input
    type="email"
    placeholder={placeholderText}
  />
)

Directives

It is recommended to use camelCase version of it (vModel) in JSX, but you can use kebab-case too (v-model).

v-show

const App = {
  data() {
    return { visible: true };
  },
  render() {
    return <input vShow={this.visible} />;
  },
};

v-model

  • You should use underscore (_) instead of dot (.) for modifiers (vModel_trim={this.test})
export default {
  data: () => ({
    test: 'Hello World',
  }),
  render() {
    return (
      <>
        <input type="text" vModel_trim={this.test} />
        {this.test}
      </>
    )
  },
}

custom directive

const App = {
  directives: { custom: customDirective },
  setup() {
    return () => (
      <a
        vCustom={{
          value: 123,
          arg: 'arg',
        }}
      />
    );
  },
}

Activity

tangjinzhou

tangjinzhou commented on Jul 3, 2020

@tangjinzhou

Compatible with Vue2 should give a deprecated warning.
I prefer not to support in future, if compatible, we need to add runtime, performance will be lost.

tangjinzhou

tangjinzhou commented on Jul 3, 2020

@tangjinzhou

About slots,proposed API look like:

<com vSlots={{xxx: ({val})=>[<div>{val}</div>]}}>
    <span></span>
</com>

children has higher priority than vSlots.default or does not support vSlots.default.

HcySunYang

HcySunYang commented on Jul 3, 2020

@HcySunYang
Member

@Amour1688 Great job, in addition to the above, I have some same or different proposals:

Experimental project: vue-next-jsx

Unified syntax

Since tsx does not support JSXNamespacedName and the . cannot appear in the attribute name, it is necessary to unify how to write directives in jsx.

Proposed syntax:

  • Don't support directives shorthand in jsx
  • Use - instead of :
  • and use _ instead of .

Examples

  • v-on
<p v-on-click_stop={ handler }></p>
<Comp v-on-myevent_a_b={ handler } />
  • v-model
    Note that: in Vue3, we use v-model:foo instead of :foo.sync modifier
<input v-model={ refVal.value }></p>
<Comp v-model-foo_a_b={ refVal.value } />

pros

  • No mixing/confusion using camel case and hyphen
  • Can be used for both jsx and tsx

cons

  • Not compatible with vue2's jsx syntax

Restricted slot

Since the scoped slot is still manually built in the Vue2' jsx, so I propose the only way to provide slots for components is:

<Comp>{ mySlots }</Comp>
const mySlots = {
    default: () => [ <p>default</p> ],
    foo: (obj) => [ <p>{ obj.somePorp }</p> ]
}

pros

  • Unified use of named slots and scoped slots
  • Avoid v-slot="props" resulting in type loss in ts

cons

  • Not compatible with vue2's jsx syntax

KeepAlive And Teleport

In Vue3, the children of KeepAlive and Teleport components will not be built as slots, so we need to handle it.

Fragment

Although we don't must to support fragment in jsx plugin, because Vue3 will handle it automatically, but it does bring development convenience:

render() {
    return (
        <>
            <p>Foo</p>
            <div>Bar</div>
        </>
    )
}

Optimization mode

Vue3 makes full use of compile-time information to generate PatchFlags for runtime update performance improvement, Maybe the jsx plugin can also do some similar work

Specify source

Some people install vue, but some install @vue/runtime-dom, so this should be configurable:

{
  "presets": [
    "@babel/env"
  ],
  "plugins": [
    ["@hcysunyang/vue-next-jsx", {
      // Specify source
      "source": "@vue/runtime-dom"
    }]
  ]
}

It affects the import statement:

import { .. } from 'vue'
import { .. } from '@vue/runtime-dom'

v-html / v-text

In Typescript, we must use domPropsInnerHTML, if we support v-html it will be more friendly.

tangjinzhou

tangjinzhou commented on Jul 3, 2020

@tangjinzhou

@HcySunYang

Don't support directives shorthand in jsx
Use - instead of :
and use _ instead of .

We should declare events instead of skipping the check. In fact,when the event is declared in the props, we can still trigger by emit.
I prefer the camel case.

tangjinzhou

tangjinzhou commented on Jul 3, 2020

@tangjinzhou

@HcySunYang

Don't support directives shorthand in jsx
Use - instead of :
and use _ instead of .

We should declare events instead of skipping the check. In fact,when the event is declared in the props, we can still trigger by emit.
I prefer the camel case.

Directives

<input vModel={this.newTodoText} />

with a modifier:

<input vModel_trim={this.newTodoText} />

with an argument:

<input onClick={this.newTodoText} />

with an argument and modifiers:

<input onClick_stop_prevent={this.newTodoText} />

v-html:

<p domPropsInnerHTML={html} />

If support modifiers, we can also declare it.
I am not familiar with ts, is there any other better way.

HcySunYang

HcySunYang commented on Jul 3, 2020

@HcySunYang
Member

@tangjinzhou

In Typescript, if you do this:

<p onClick_stop={ handler }>text</p>

will get an error:

image

This is because onClick is treated as a standard html attribute.

In fact, users can still use onClick, but it is not allowed to add modifiers on it, if you want to add modifiers, please use:

<p v-on-click_stop={ handler }></p>
Amour1688

Amour1688 commented on Jul 3, 2020

@Amour1688
MemberAuthor

In Typescript, if you do this:

<p onClick_stop={ handler }>text</p>

will get an error:

image

This is because onClick is treated as a standard html attribute.

In fact, users can still use onClick, but it is not allowed to add modifiers on it, if you want to add modifiers, please use:

<p v-on-click_stop={ handler }></p>

If it's possible to extend types.

v-on-click It doesn't look good.

I think use props.onXX may be a better method

tangjinzhou

tangjinzhou commented on Jul 3, 2020

@tangjinzhou

@tangjinzhou

In Typescript, if you do this:

<p onClick_stop={ handler }>text</p>

will get an error:

image

This is because onClick is treated as a standard html attribute.

In fact, users can still use onClick, but it is not allowed to add modifiers on it, if you want to add modifiers, please use:

<p v-on-click_stop={ handler }></p>

@HcySunYang

maybe we should try to resolve it. If skip the check, the effect of using ts will no longer exist.

We can declare it like react :

declare module 'react' {
  interface Attributes {
    vModel?: any;
    // onClick
    onClick_stop?: any;
  }
}
fgr-araujo

fgr-araujo commented on Jul 3, 2020

@fgr-araujo

Dots to separate is more readable. We know that dot is a layer inside something
Unserscore means compound names.

fgr-araujo

fgr-araujo commented on Jul 3, 2020

@fgr-araujo

I love to use dash in HTML but in JSX can be confused with a minus sign.

tangjinzhou

tangjinzhou commented on Jul 3, 2020

@tangjinzhou

We are now using the vueComponent/jsx to refactor ant-design-vue. I expect a smaller change cost, so I hope to be compatible with the syntax in vue2. Even if it may cause a performance loss, we should give the old project enough time to migrate.

sonicoder86

sonicoder86 commented on Jul 3, 2020

@sonicoder86

Why not use JSX as is and use event handlers like this?

<button onClick={activateLasers}>
  Activate Lasers
</button>

I feel it more intuitive for devs coming with React background.

HcySunYang

HcySunYang commented on Jul 3, 2020

@HcySunYang
Member

@BlackSonic, Of course, you can.

One of the purposes of the jsx plugin is to bring the convenience of the template, such as event modifiers, but this does not prevent you from directly using onClick.

74 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @dsonet@yyx990803@sonicoder86@wonderful-panda@lhk

        Issue actions

          Vue 3 JSX Design · Issue #141 · vuejs/jsx-vue2