Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/lancercomet/vue2-jsx-runtime
JSX runtime for Vue 2.
https://github.com/lancercomet/vue2-jsx-runtime
jsx jsx-renderer jsx-runtime tsx tsx-renderer typescript vue vuecompositionapi vuejs2
Last synced: 23 days ago
JSON representation
JSX runtime for Vue 2.
- Host: GitHub
- URL: https://github.com/lancercomet/vue2-jsx-runtime
- Owner: LancerComet
- License: apache-2.0
- Created: 2022-05-17T20:17:20.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2023-11-18T14:52:04.000Z (12 months ago)
- Last Synced: 2024-09-30T12:01:40.330Z (about 1 month ago)
- Topics: jsx, jsx-renderer, jsx-runtime, tsx, tsx-renderer, typescript, vue, vuecompositionapi, vuejs2
- Language: TypeScript
- Homepage:
- Size: 573 KB
- Stars: 22
- Watchers: 4
- Forks: 6
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Vue 2 JSX Runtime
[![npm version](https://badge.fury.io/js/@lancercomet%2Fvue2-jsx-runtime.svg)](https://badge.fury.io/js/@lancercomet%2Fvue2-jsx-runtime)
![Testing](https://github.com/LancerComet/vue2-jsx-runtime/workflows/Test/badge.svg)This package is designed for handling Vue 2 JSX.
## What's the different between this and the official solution?
The official solution is a set of Babel plugins which convert JSX to Vue render function, and this package is the [New JSX Transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) implement for Vue 2. They are just two different ways to achieve the goal.
For TypeScript users, when you use the official solution your workflow would be like:
```
TSX -> Babel -> Vite (ESBuild) / TSC / SWC -> JS
```The Babel just slows down the whole process, and we all know that these compilers actually support JSX transforming out of box. So if we have a Vue 2 New JSX Transform runtime for those compilers, we can just get rid of Babel.
For JavaScript users, you have to use Babel with it to transform JSX into JavaScript codes. [This example](https://github.com/LancerComet/vue2-jsx-runtime-webpack) shows how to use it with Babel and Webpack.
The reasons I developed this package:
1. I want to use Vite (it's fast) without ESBuild (doesn't support EmitDecoratorMetadata), so I have to use SWC + Vite, and I also need Vue 2 JSX support, but I don't want to bring Babel in.
3. Using `v-model` in `JSX-returing-setup()` with the official solution will break the Vue 2 app. It has been a long time but still not being fixed yet.## Setup
First, please make sure `Vue@2` has been installed in your project, then
```
npm install @lancercomet/vue2-jsx-runtime --save
```### Using TSC
Update your `tsconfig.json` with:
```js
{
"compilerOptions": {
...
"jsx": "react-jsx", // Please set to "react-jsx".
"jsxImportSource": "@lancercomet/vue2-jsx-runtime" // Please set to package name.
}
}
```> The reason why "jsx" should be set to "react-jsx" is this plugin has to meet the new JSX transform.
### Using SWC
In `tsconfig.json`:
```js
{
"compilerOptions": {
...
"jsx": "preserve" // Please set to "preserve".
}
}
```And in `.swcrc`:
```js
{
"jsc": {
"transform": {
"react": {
"runtime": "automatic", // Please set to "automatic" to enable new JSX transform.
"importSource": "@lancercomet/vue2-jsx-runtime", // Please set to package name.
"throwIfNamespace": false
}
}
}
}
```### For JavaScript users
You can use it with [@babel/plugin-transform-react-jsx](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx). You can [check this out](https://github.com/LancerComet/vue2-jsx-runtime-webpack) to see how to use it with Babel and Webpack.
### For Vite
Please read the section below.
## Usage
### Passing Value
#### Setup
```tsx
defineComponent({
setup () {
const isDisabledRef = ref(false)
return () => (
Wow such a button
)
}
})
```#### Render function
```tsx
Vue.extend({
data () {
return {
isDisabled: false
}
},
render () {
return (
Very button
)
}
})```
### On
#### Setup
```tsx
setup () {
const onClick = () => {}
return () => (
Click me
)
}
```#### Render function
```tsx
Vue.extend({
methods: {
onClick () {}
},
render () {
return Click me
}
})
```#### Using "on" object to assign multiple events for once
```tsx
```### Native on
```tsx
```
Native is only available for Vue components.
### Rendering HTML or text
```tsx
// Using Vue directive.
// Setting HTML.
// Using dom prop.// Setting text.
// Using Vue directive.
// Using dom prop.
```### HTML / Component ref
#### Vue ≤ 2.6
Due to the limitation, using ref is a little different from to Vue 3.
You can check [this](https://github.com/vuejs/composition-api#limitations) out for more information.
```tsx
import { ComponentPublicInstance, defineComponent, onMounted } from '@vue/composition-api'const Example = defineComponent({
setup () {
return () => (
Example goes here
)
}
})const Wrapper = defineComponent({
setup (_, { refs }) {
onMounted(() => {
const div = refs.doge as HTMLElement
const example = refs.example as ComponentPublicInstance
})return () => (
Wow very doge
)
}
})
```#### Vue 2.7+
Vue 2.7 has its built-in composition API support, and the behavior acts as the same as Vue 3.
```tsx
import { ref, defineComponent } from 'vue'const Example = defineComponent({
setup () {
const dogeRef = ref()
onMounted(() => {
console.log(dogeRef.value)
})return () => (
Wow very doge
)
}
})
```### Slot
```tsx
const Container = defineComponent({
setup (_, { slots }) {
return () => (
{ slots.default?.() }
{ slots.slot1?.() }
{ slots.slot2?.() }
)
}
})const Example = defineComponent({
name: 'Example',
setup (_, { slots }) {
return () => (
{ slots.default?.() }
)
}
})
``````tsx
Default
Slot1
Slot2```
### ScopedSlots
```tsx
const MyComponent = defineComponent({
props: {
name: String as PropType,
age: Number as PropType
},setup (props, { slots }) {
return () => (
{ slots.default?.() }
{ slots.nameSlot?.(props.name) }
{ slots.ageSlot?.(props.age) }
)
}
})
``````tsx
Default,
nameSlot: (name: string) =>Name: {name},
ageSlot: (age: number) => {
returnAge: {age}
}
}}
/>
```Output:
```html
Default
Name: John Smith
Age: 100
```### Built-in directives
#### Setup
```tsx
defineComponent({
setup () {
const isDisplayRef = ref(false)
const textContentRef = ref('John Smith')
const htmlContentRef = ref('John Smith
')return () => (
Page content
)
}
})
```#### Render function
```tsx
Vue.extend({
data () {
return {
isDisplay: false,
textContent: 'John Smith',
htmlContent: 'John Smith
'
}
},
render () {
return (
Page content
)
}
})
```### v-model
#### Regular usage
```tsx
import ref from '@vue/composition-api'
import Vue from 'vue'// Setup.
const Example = defineComponent({
setup () {
const nameRef = ref('')
return () => (
)
}
})// In render function.
const Example = Vue.extend({
data: () => ({
name: ''
}),
render: () =>
})
```#### With modifiers
You can use modifiers to add some extra features:
- `lazy, number, trim`: These are the built-in modifiers from Vue.
- `direct`: See **"About IME"** section below.```tsx
const Example = Vue.extend({
data: () => ({
name: '',
age: 0
}),
render: () => (
)
})const Example = defineComponent({
setup () {
const nameRef = ref('')
const ageRef = ref(0)
return () => (
)
}
})
```#### With argument
Argument of v-model is designed for binding properties.
Due to limitation, binding properties in Vue 2 isn't that kinda convenient:
```tsx
const userRef = ref({
detail: {
address: ''
}
})// This works in Vue 3 but doesn't work in Vue 2.
```
We have to use v-model like:
```tsx
const Example = defineComponent({
setup () {
const userRef = ref({
username: '',
age: 0,
detail: {
address: ''
}
})
return () => (
)
}
})
```#### About IME
By default, `v-model` will only assign what you have selected from IME. If you were typing in IME, `v-model` would do nothing.
If you want to disable this behavior, add `direct` modifier:
```tsx
{/* It will sync everything you have typed in IME. */}{/* By default, it will only assign what you have selected from IME. */}
```
### Key
Due to the limitation, we have to use `v-bind:key`:
```tsx
{
userList.map(item => (
{item.name}
))
}
```### Transition / TransitionGroup
```tsx
import Vue from 'vue'const Transition = Vue.component('Transition')
const TransitionGroup = Vue.component('TransitionGroup')setup () {
return () => (
Some element
Some element
Some element
)
}
```or
```tsx
setup () {
return () => (
Some element
Some element
Some element
)
}
```## Compatibility
These format below are also available, but they are NOT recommended, just for compatibility.
### On
```tsx
```### v-model
```tsx
```
### Key
```tsx
```## For Vite users
For Vite users, it's better to use TSC or SWC instead of built-in ESBuild. Because ESBuild is very finicky at handling JSX for now, and it gives you no room to change its behavior.
For faster compilation, SWC is recommended. You can use [unplugin-swc](https://github.com/egoist/unplugin-swc) to make Vite uses SWC.
Once you have switched to SWC (TSC) from ESBuild, you will not only be able to use JSX, but also get more features like `emitDecoratorMetadata` which is not supported by ESBuild, and the whole process is still darn fast.
### Configuration
After you have configured SWC (see Setup section above):
1. Install [unplugin-swc](https://github.com/egoist/unplugin-swc).
```
npm install unplugin-swc --save-dev
```2. Update `vite.config.ts`:
```ts
import { defineConfig } from 'vite'
import swc from 'unplugin-swc'
import { createVuePlugin } from 'vite-plugin-vue2'export default defineConfig({
plugins: [
swc.vite(),
createVuePlugin(),
...
]
})
```## Mixing usage
If you have to use `JSX` and `SFC` together in Vite, you need to update your Vite config:
```ts
import { defineConfig } from 'vite'
import swc from 'unplugin-swc'
import { createVuePlugin } from 'vite-plugin-vue2'const swcPlugin = swc.vite()
export default defineConfig({
plugins: [
{
...swcPlugin,
transform (code, id, ...args) {
if (
id.endsWith('.tsx') || id.endsWith('.ts') ||
(id.includes('.vue') && id.includes('lang.ts'))
) {
return swcPlugin.transform.call(this, code, id, ...args)
}
}
},createVuePlugin(),
...
]
})
```This will make SWC to skip compiling Non-Typescript codes in Vue SFC.
## Hot Reload
Use [vite-plugin-vue2-hmr](https://github.com/LancerComet/vite-plugin-vue2-hmr) to enable Vue2 JSX hot reload in Vite.
## Contributing
Feel free to open issue or pull request to make it better.
## References
- [Introducing the New JSX Transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html)
- [Render Function](https://vuejs.org/guide/extras/render-function.html)
- [vue-jsx-runtime (Vue 3)](https://github.com/dolymood/vue-jsx-runtime)
- [@vue/composition-api](https://github.com/vuejs/composition-api)
- [@vue/babel-plugin-jsx](https://github.com/vuejs/babel-plugin-jsx)