Skip to content

apades/make-vue-coding-more-like-react

Repository files navigation

make Vue coding more like React

As the title says

Why this ?

If you have used Vue's official jsx plugin, you will get how strange the syntax is, especially when you switch from React. You need to manually define props + defineComponent.setup and feel uncomfortable writing it.

import { ref, type VNode, defineComponent } from 'vue'
type Props = {
  name: string
  header: (count: number) => VNode
}
type Handler = {
  addCount: () => void
}
const Child = defineComponent<Props>({
  props: ['name', 'header'],
  setup(props, ctx) {
    const innerCount = ref(0)
    ctx.expose<Handler>({
      addCount() {
        innerCount.value++
      },
    })
    return () => (
      <>
        <div class="mb-2 border-b-[1px]">{props.header(innerCount.value)}</div>
        <div class="cursor-pointer" onClick={() => innerCount.value++}>
          {props.name} count: {innerCount.value}
        </div>
        {ctx.slots.default?.()}
      </>
    )
  },
})

This project is to solve the above verbose writing method and turn to React to use only one function to define jsx component writing method, example

How to use

Warning

THIS PLUGIN ONLY SUPPORT WITH Vue3 + Typescript + setup

npm i @mvcmlr/plugin-vue-jsx -D

vite.mts

import { defineConfig } from 'vite'
import vueJsx from '@mvcmlr/plugin-vue-jsx'

export default defineConfig({
  plugins: [vueJsx()],
})

tsconfig.json

{
  "compilerOptions": {
    // ...
    "jsx": "preserve",
    "jsxImportSource": "vue",
  },
}

Example

import { effect, ref, type VNode } from 'vue'
type Props = {
  name: string
  header: (count: number) => VNode
  children?: VNode
}
type Handler = {
  addCount: () => void
}
function ChildComp(props: Props) {
  const innerCount = ref(0)
  defineExpose<Handler>({
    addCount() {
      innerCount.value++
    },
  })
  return (
    <>
      <div class="mb-2 border-b-[1px]">{props.header(innerCount.value)}</div>
      <div class="cursor-pointer" onClick={() => innerCount.value++}>
        {props.name} count: {innerCount.value}
      </div>
      {props.children}
    </>
  )
}

function App() {
  const count = ref(0)
  const childCompRef = ref<Handler>()
  effect(() => {
    console.log('count change', count.value)
  })

  return (
    <div>
      <div class="cursor-pointer" onClick={() => count.value++}>
        app count: {count.value}
      </div>
      <div
        class="cursor-pointer"
        onClick={() => childCompRef.value?.addCount()}
      >
        click to add count in child comp
      </div>

      <ChildComp
        name="hello"
        header={(count) => (
          <>
            <div>header </div>
            <p>from ChildComp count :{count}</p>
          </>
        )}
        ref={childCompRef}
      >
        <div>
          <div>child app count: {count.value}</div>
        </div>
      </ChildComp>
    </div>
  )
}

export default App

Syntax warning

  • Define jsx function
// ✔ return jsx element
const App = () => <div></div>
// ✔
function App() {
  return <div></div>
}
// 🚫 without return jsx element
const App = () => '1111'
// 🚫 reference jsx variable
const Child = <div></div>
const App = () => Child
  • Nested jsx function
const App = () => {
  const Child = (props: { a: number }) => {
    // 🚫 🤔 Not support yet, maybe in the future
    defineExpose({})
    return <div>{props.a}</div>
  }

  // 🚫 🤔 Not support yet, maybe in the future
  return <Child a={1} />
  // ✔
  return Child({ a: 1 })
}
  • Multiple return
const App = (props: { a: number }) => {
  // ✔ use hook before return
  const count1 = ref(0)
  if (props.a == 1) return <div>1</div>
  // 🚫 don't use hook after return
  const count2 = ref(0)
  if (props.a == 2) return <div>2</div>
  return <div>3</div>
}

Credit

Code based on:

Inspired:

About

As the title says

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages