Vue3 & JSX 插槽、v-model 的用法以及 React JSX 的区别

前言

写这篇文章的初衷是,Vue3 JSX 部分与 React JSX 容易混淆,比如如本文所说的 slot & v-model,
如果你是第一次接触 JSX,可先阅读前面写过的 React & JSX 日常用法与基本原则 来对 JSX 有一个整体的认知以及比较两者间的差异。

一、什么是 JSX

JSX 是用一种可以将 HTML + JS 混合写的一种语法糖,最初由 React 率先研发,后来 Vue3 也借鉴了这种写法灵感实现自家的 JSX

实际上,能够快速研发 JSX 还得得益于一款叫做由 @babel/plugin-syntax-jsx 的插件,
是由 Babel 研发的,它只负责解析但不负责转换,于是 ReactVue3 基于此插件实现了自家的 JSX

举个例子

const Component = () => <div> {[1,2,3].map((num) => <span>{num}</span>)} </div>
// 等价于 
122

这种写法就叫 JSX,换句话说,只要你用过 React/Vue3 其中的 JSX ,你就同时掌握了两种框架的 JSX 。当然,每个框架对自己的 JSX 都喜欢添砖加瓦,如独特的 API,独特的属性修饰符;
但万变不离其宗, HTML + JS 写法是任何 JSX 框架统一遵循规范的不变事实。

二、Vu3 使用 JSX

2.1 准备工作

a) 针对新项目

使用官方提供的安装命令,里面内置了 JSX、TypeScript 等插件,无须手动配置

npm init vue@latest

按照提示选项勾选即可,Vue3 & JSX 插槽、v-model 的用法以及 React JSX 的区别_第1张图片
接下来就可以愉快的使用 JSX 和 TypeScript ~

b) 针对现有项目

  1. 安装 JSX 依赖包:
npm install @vue/babel-plugin-jsx -D
  1. 新建 Babel 配置文件: babel.config.json ,内容如下:
{
  "plugins": ["@vue/babel-plugin-jsx"]
}

然后就可以愉快的使用 JSX 啦~

对有关 Babel 的概念感兴趣的可参考前面写过的:
JS & 介绍 Babel 的使用及 presets & plugins 的概念

2.2 使用 slot 插槽

定义插槽方式1

// NavBar.jsx
import { defineComponent } from 'vue'
const NavBar = defineComponent({
	setup(props, { slots }) {
		return () => {
			return (
				<>
					<div>Simple</div>
					<div> { slots?.default() } </div>
					<header> { slots?.header() } </header>
				</>
			)
		}
	}
})

注意,使用 slot 时,setup return 必须是一个函数,实际上就是 render() 。

定义插槽方式2

// NavBar.jsx
const NavBar = (props, { slots }) => {
  return (
    <>
      <div>Simple</div>
      <div> { slots?.default() } </div>
      <header> { slots.header() } </header>
    </>
  )
}

这两种定义方式的区别取决于你是否需要用到 Vue 提供的一些钩子函数或响应式 API,比如第一种有 setup ,如果是纯普素的组件,使用第二种方式即可。

使用插槽方式1:双括号

// App.jsx
import NavBar from '@/components/nav-bar'
const App = () => {
  return (
    <>
      <NavBar>
        {{
          default: () => 'Hello,world.',
          header: () => 'Hello, header.'
        }}
      </NavBar>
    </>
  )
}
export default App

使用插槽方式2:v-slots

// App.jsx
import NavBar from '@/components/nav-bar'
const App = () => {
  const slots = {
    default: () => 'Hello,world.',
    header: () => 'Hello, header.'
  }
  return (
    <>
      <NavBar v-slots={slots}>
      </NavBar>
    </>
  )
}
export default App

使用插槽方式3:把它当成 .vue 文件使用即可

// App.vue
<template>
  <div>
    <NavBar>
      Hello, world.
      <template #header>
        Hello, header.
      template>
    NavBar>
  div>
template>

<script lang="ts">
import { defineComponent } from 'vue';
import NavBar from '@/components/nav-bar'
export default defineComponent({
  components: {
    NavBar
  },
  props: {},
  setup(props) {
    return {}
  }
})
script>

提示:.vue 组件可以引入 .jsx/tsx 组件,反之 .jsx/tsx 也可以引入 .vue 组件,它们本质上就是一个 Object 。

2.3 使用 v-model

使用方式1:标签绑定 v-model

// NavBar.jsx
import { ref, defineComponent } from 'vue'
const NavBar = defineComponent({
  setup() {
    const phone = ref()
    return () => {
      return (
          <input type="text" v-model={phone.value} />
      ) 
    }
  }
})
export default NavBar;

使用方式2:组件绑定 v-model

// Card.jsx 引入 component 组件。
import { defineComponent, ref } from 'vue'
import Component from './component'

const CardComponent = defineComponent({
  setup() {
    const componentName = ref()
    return () => {
      return (
        <main>
          <Component 
            v-model={[componentName.value, 'modelValue']} 
          />
        </main>
      )
    }
  }
})
export default CardComponent

// 定义组件 Component.jsx 。
import { defineComponent, toRefs } from 'vue'
const Component = defineComponent({
  emits: ['update:modelValue'],
  setup(props, { emit }) {
    return () => {
      return (
        <input type="text" onInput={($event) => emit('update:modelValue', $event.target.value)} />
      )
    }
  }
})
export default Component

解释:

  1. Card.jsx 引入 Component 组件并绑定 v-model
  2. v-model 的第二个参数 modelValue 与 Component 组件的 update:modelValue 成对应关系,是双向更新的必备定义条件。

使用方式3:组件绑定多个 v-model

// Card.jsx 引入 component 组件。
import { defineComponent, ref } from 'vue'
import Component from './component'

const CardComponent = defineComponent({
  setup() {
    const componentName = ref()
    const modelPhoneNumber = ref()
    return () => {
      return (
        <main>
          <Component 
            v-model={[componentName.value, 'modelValue']} 
            v-model={[modelPhoneNumber.value, 'modelPhoneNumber']} 
            // 上面等同于下面。
            // v-model:modelValue={modelValue.value} 
            // v-model:modelPhoneNumber={modelPhoneNumber.value} 
          />
        </main>
      )
    }
  }
})
export default CardComponent

// 定义组件 Component.jsx 。
import { defineComponent, toRefs } from 'vue'
const Component = defineComponent({
  emits: ['update:modelValue', 'update:modelPhoneNumber'],
  setup(props, { emit }) {
    return () => {
      return (
        <input type="text" onInput={($event) => emit('update:modelValue', $event.target.value)} />
        <input type="text" onInput={($event) => emit('update:modelPhoneNumber', $event.target.value)} />
      )
    }
  }
})
export default Component

提示:对于绑定 v-model 的组件来说,组件内无需再自定义 props: {},可别跟平时的传参给混淆了,如果组件内部确实要访问,则给组件加上 x={x} 然后用 props 接受即可。

2.4 支持 TSX 写法

TSX 指的是能够在 TypeScript 中写 JSX,在 tsconfig.json 中加以下选项:

"compilerOptions": {
  "jsx": "preserve"
}

接下来只需将文件后缀由 .jsx 改成 .tsx 即可。

2.5 注意事项

  1. 使用 JSX 语法时,文件名必须为 .jsx.tsx ,不支持 .vue
  2. 在 JSX 里的标签内访问响应式数据时,须带上 .value 访问。

三、Vue3 JSX 与 React JSX 的差别

  1. 绑定数据方式不同,Vue3 JSX 采用 v-model 双向绑定数据,React 采用 setState 。
  2. 生命周期函数不同。
  3. 插槽用法不同。
  4. 有各自的 API。

其实不管是 Vue3 or React,它们最大的特点就是 JSX ,本质上就是 HTML & JS 可以混合写,这才是它们的共同之处。

文献:
https://juju.one/using-jsx-with-vue3/
https://github.com/vuejs/babel-plugin-jsx

你可能感兴趣的:(Vue,React,JavaScript,react.js,vue.js,前端)