背景

公司高级表单组件ProForm高阶组件都建立在jsx的运用配置上,项目在实践落地过程中积累了丰富的经验,也充分感受到了jsx语法的灵活便捷和可维护性强大,享受到了用其开发的乐趣,独乐乐不如众乐乐,为了帮助大家更好的运用jsx,开发提效,特此总结分享。

效果对比

以前

以往我们开发一个列表的增加、编辑、查看详情三个操作要准备3个form表单文件,表单中ui元素共性部分我们要复制三次,例如:

// addForm.vue
<template>
<el-form :model="form" >
<el-form-item label="活动名称">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="活动区域">
<el-input v-model="form.region" />
</el-form-item>
<el-form-item label="活动形式">
<el-input v-model="form.type" />
</el-form-item>
</el-form>
<el-form-item>
<el-button >新增</el-button>
</el-form-item>
</template> <script setup>
import { reactive } from 'vue'
const form = reactive({
name: '',
region: '',
type: '',
})
...
</script>

// editForm.vue

<template>
<el-form :model="form" >
<el-form-item label="活动名称">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="活动区域">
<el-input v-model="form.region" />
</el-form-item>
<el-form-item label="活动形式">
<el-input v-model="form.type" />
</el-form-item>
<el-form-item>
<el-button >编辑</el-button>
</el-form-item>
</el-form>
</template> <script setup>
import { reactive,inject } from 'vue'
const form = reactive({})
form=inject('detailData')
...
</script>

// detailForm.vue

<template>
<el-form :model="form" :disabled="true" >
<el-form-item label="活动名称">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="活动区域">
<el-input v-model="form.region" />
</el-form-item>
<el-form-item label="活动形式">
<el-input v-model="form.type" />
</el-form-item>
<el-form-item>
<el-button >关闭详情</el-button>
</el-form-item>
</el-form>
</template> <script setup>
import { reactive,inject } from 'vue'
const form = reactive({})
form=inject('detailData')
...
</script>

如果遇到改字段名的情况,如把活动名称的name改成activityName,对应的3个文件都得去修改,表单改动多的话还有可能存在有得文件改漏的情况。

现在

现在使用ProForm结合jsx配置,如下



// add.vue
<template>
<activity-form
ref="formRef"
mode="Add"
/>
<el-button >新增</el-button>
</template> <script setup>
Import ActivityForm from './form.vue'
...
</script> // edit.vue
<template>
<activity-form
ref="formRef"
mode="Edit"
/>
<el-button >编辑</el-button>
</template> <script setup>
Import ActivityForm from './form.vue'
...
</script>
// detail.vue
<template>
<activity-form
ref="formRef"
mode="detail"
/>
<el-button >关闭详情</el-button>
</template> <script setup>
Import ActivityForm from './form.vue'
...
</script>

Form.vue

<template>
<yun-pro-form
ref="formRef"
:form="form"
:columns="columns"
:form-props="{ labelPosition: 'top',disabled:mode==='detail' }"
/>
</template> <script lang="jsx" setup>
import { reactive, ref, computed } from 'vue'
...
const formRef = ref()
const form = reactive({
name: '',
region: '',
type: '',
})
const props = defineProps({
mode: {
type: String,
default: 'Add',
},
});
const columns = [
{
prop: 'name',
label: '活动名称',
type: 'input',
},
{
prop: 'region',
label: '活动区域',
// jsx部分
render: (form) => (
<el-input clearable v-model={form.region} />
),
},
{
prop: 'type',
label: '活动形式',
// jsx部分
render: (form) => (
<el-input clearable v-model={form.type} />
),
},
] ...
</script>

改成上面的形式后,一处改动,3处对应生效,开发的重点也转移到form表单中对columns的配置上,columns配置则建立在对jsx的运用,当然举的这个例子只是一个简单案例,复杂的例子在项目中,下面我们正式开启jsx之旅吧

JSX是什么

JSX(JavaScript 和 XML),是一个 HTML-in-JavaScript 的语法扩展,首先在 React 中被进入,它允许我们在JavaScript中编写类似HTML的代码,并将其转换为JavaScript对象。Vue3中引入了对JSX的支持,使得我们可以更加灵活地编写组件模板,不再局限于Vue2.x中的模板语法。JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。

形如:

const element = <div id="root">Hello World</div>

function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}

JSX 和 template 的区别

--语法上有很大区别:

•JSX 本质就是 js 代码,可以使用 js 的任何能力
•template 只能嵌入简单的 js 表达式,其他需要指令,如 v-if
•插值不同,jsx 使用单括号 { },template 使用双括号 {{ }}
•JSX 已经成为 ES 规范,template 还是 Vue 自家规范
•--本质是相同的:
•都会被编译为 js 代码(render 函数)

基本用法

安装使用

vite 官方提供了官方的插件来支持在 vue3 中使用 jsx

yarn add @vitejs/plugin-vue-jsx

安装完之后在 vite.config.js 文件中的 plugins 字段中添加 jsx 支持:


import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({
plugins: [
vueJsx(),
]
})

插值表达式

// template
<template>
<span>{{ a + b }}</span>
</template> // jsx
render: () => {
return (
<span>{ a + b }</span>

}

条件渲染

•jsx只保留了 v-show指令,没有 v-if指令
•使用 if/else和三目表达式来实现判断
// template v-if  v-show
<template>
<div v-if="show">是</div>
<div v-else>否</div>
<div v-show="show">我是v-show</div>
</template> // jsx
render: () => {
const show = ref(true);
return (
<div>
<div>{show.value ? <div>是</div> : <div>否</div>}</div>
<div v-show={!show.value}>我是v-show</div>
</div>

}
// jsx if-else render: () => {
const isShow = false
const element = () => {
if (isShow) {
return <div>是</div>
} else {
return <div>否</div>
}
}
return (
<div>
{
element()
}
<div v-show={!show.value}>我是v-show</div>
</div>

}

样式绑定

•class类名绑定有两种方式,使用模板字符串或者使用数组。
•style绑定需要使用 双大括号
// template
<template>
<div class="static" :class="{ active: isActive }"></div>
<div :class="[isActive ? activeClass : '', static]"></div>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
</template> // jsx
// jsx 模板字符串
<div className={`static ${ isActive ? 'active' : '' }`}>header</div>
//jsx 数组
<div class={ [ 'static', isActive && 'active' ] } >header</div> // jsx 样式绑定需要使用双大括号。
render: () => {
const width = '100px'
return (
<button style={{ width, fontSize: '16px' }}></button>

}

列表循环

// template v-for

<template>
<ul>
<li v-for="item in list" :key="item">{{ item }}</li>
</ul>
</template> // jsx 数组 .map 函数 render: () => {
return <>
<ul>
{ list.map(item => <li>{item}</li>)}
</ul>
</>
}

事件处理

•jsx绑定事件使用的是 单大括号 {},事件绑定不是以 @为前缀了,而是改成了 on,例如:click 事件是 onClick
•如果需要使用事件修饰符,就需要借助withModifiers方法啦,withModifiers 方法接收两个参数,第一个参数是绑定的事件,第二个参数是需要使用的事件修饰符,或者在有些修饰符不生效的情况下采用链式驼峰的形式进行设置,如@click.once变为 onClickOnce
// template 

<template>
<button @click="clickButton('button1')"> 点击</button>
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
</template> // jsx 数组 .map 函数 render: () => {
const clickButton = val => {
console.log(val)
}
return (
<div>
<button onClick={() =>clickButton('button1')}>点击</button>
<a onClick={withModifiers(() => doThis(), ['stop'])}></a>
<a onClickOnce={doThis}></a>
</div>

}

标签属性绑定

// template
<template>
<div v-bind="properties"></div>
</template> //jsx
render: () => {
return (
<div {...properties}></div>
)
}

V-model


// 一般用法
<input v-model="value" /> // template
<input v-model={value} /> // jsx // 指定绑定值写法
<input v-model:modelValue="value" /> // template
<input v-model={[value,'modelValue']} /> // jsx // 修饰符写法
<input v-model:modelValue.trim="value" /> // template
<input v-model={[value,'modelValue',['trim']]} /> // jsx

插槽

定义插槽

jsx/tsx中是没有 slot 标签的,定义插槽需要使用{}或者使用renderSlot函数

setup 函数默认接收两个参数 1. props 2. ctx 上下文 其中包含 slots、attrs、emit 等


// template
<template>
<div>
<slot></slot>
<slot name="title"></slot>
</div>
</template> // jsx
import { renderSlot } from "vue"
export default defineComponent({
// 从ctx中解构出来 slots
setup(props, { slots }) {
return () => (
<div>
{ renderSlot(slots, 'default') } // 等价于 { slots.default?.() }
{ slots.title?.() }
</div>
)
}
})

使用插槽

通过 v-slots 来使用插槽

// template
<template>
<yun-table
>
<template #action="{ row }">
<el-button type="action" @click="handleDel(row)">
删除
</el-button>
</template>
</yun-table>
</template> // jsx
render: (form) => {
const slots = {
action: ({ row }) => <el-button type="action" onClick={() => handleDel(row)}>
删除
</el-button>,
};
return (
<yun-table
v-slots={slots}
>
</yun-table> );
},

jsx基础模板

import { defineComponent, onMounted, ref } from 'vue';

export default defineComponent({
// props: ['xx'],
setup(props,{ emit }) {
onMounted(() => {
// ...
})
return () => (
<div></div>
)
}
})

组件运用

在 .vue 文件中使用 jsx组件

// 父

<template>
<div class="home">
<JSXDemo />
</div>
</template> <script setup lang="jsx">
import JSXDemo from '@/components/JSXDemo.vue' </script> // JSXDemo.vue <script>
import { ref } from 'vue'
export default {
setup () {
const countRef = ref(200) const render = () => {
return <p>DEMO1--{countRef.value}</p>
}
return render
}
}
</script>

在.jsx文件格式中父子组件属性传递

// 父组件

import { defineComponent, ref } from 'vue'
import JSXChild from './JSXChild.jsx' export default defineComponent(() => { // 传入 setup 函数
const countRef = ref(360) const render = () => {
return <>
<p>数量--{countRef.value}</p>
<JSXChild a={countRef.value + 100}></JSXChild> // vue3的template会自动解析ref的.value,在jsx中ref的.value是不会被自动解析的
</>
}
return render
}) // 子组件 JSXChild.jsx import { defineComponent } from 'vue' export default defineComponent({ // 传入组件配置
props: ['a'],
setup (props) {
const render = () => {
return <>
<p>child {props.a}</p>
</>
}
return render
}
})

经验总结

我们该怎么选择 JSX 和 template ?

template优势:template 的语法是固定的,有 v-if、v-for 等等语法。按照这种固定格式的语法书写的代码, Vue3 在编译层面就可以很方便地去做静态标记的优化,减少Diff过程。比如静态提升,类型标记,树结构打平等来提高虚拟 DOM 运行时性能。这也是 Vue 3 的虚拟 DOM 能够比 Vue 2 快的一个重要原因。

JSX优势:template 因为语法限制原因,不能够像 JSX 那样可以支持更动态的需求。每一个 .vue 文件结尾的文件都是一个组件,而且只能 export default 出一个组件,JSX 则不同 ,是可以在一个文件内返回多个组件的,比如我们写一个页面的时候其实可能会需要把一些小的节点片段拆分到小组件里面进行复用,这些小组件在jsx里面,写个简单的函数组件就能搞定,例如:

那如何选择呢?

在实现业务需求的时候,优先使用 template,尽可能地利用 Vue 本身的性能优化,如列表、弹窗和抽屉。而对于动态性要求较高的组件可以使用 JSX 来实现,比如动态表单,封装动态递归组件。而对于公司项目来说,大多数业务需求都是表单类的,那就赶紧用上jsx吧,用久了你就会发现,哎,真香。

拥抱jsx,开启vue3用法的另一种选择🔥🔥的更多相关文章

  1. day 32 子进程的开启 及其用法

    开启两种子进程的两种方式# # # 1 传统方式# from multiprocessing import Process# import time# def task(name):# print ( ...

  2. MySQL做为手动开启事务用法

    START TRANSACTION;INSERT INTO `t1` (t, t1) VALUES('124', NOW());ROLLBACK;COMMIT;

  3. 在Vue中使用JSX,很easy的

    摘要:JSX 是一种 Javascript 的语法扩展,JSX = Javascript + XML,即在 Javascript 里面写 XML,因为 JSX 的这个特性,所以他即具备了 Javasc ...

  4. Vue3从基础到精通

    目录 一.Vue3介绍 1. Vue3的变化 2. Vue2和Vue3的不同之处 二.Vue3创建项目 1.用vue-lci创建步骤 2.用vite创建步骤 三.setup函数 四.ref.react ...

  5. Vue相关,Vue JSX

    JSX简介 JSX是一种Javascript的语法扩展,JSX = Javascript + XML,即在Javascript里面写XML,因为JSX的这个特性,所以他即具备了Javascript的灵 ...

  6. vue3 + element plus 使用字节跳动图标

    使用场景: 提一下vue2 用法>> 下面回到正题 vue3 用法 1  安装包: npm install @icon-park/vue-next --save 2  字节跳动图标库取图地 ...

  7. React + Reflux

    React + Reflux 渲染性能优化原理   作者:ManfredHu 链接:http://www.manfredhu.com/2016/11/08/23-reactRenderingPrinc ...

  8. ssh架构之hibernate(一)简单使用hibernate完成CRUD

    1.Hibernate简介   Hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,h ...

  9. React + Reflux 渲染性能优化原理

    作者:ManfredHu 链接:http://www.manfredhu.com/2016/11/08/23-reactRenderingPrinciple 声明:版权所有,转载请保留本段信息,否则请 ...

  10. 如何down掉IB交换机口

    服务器上找到需down的网络:ip a 通过ib命令iblinkinfo找到对应交换机以及在ib交换机上对应端口号 登录IB交换机,并通过命令:config进入配置模式 通过命令:port进入端口配置 ...

随机推荐

  1. IDEA2022中部署Tomcat Web项目

    使用工具: IDEA2022 Tomcat9.0.4 1.下载Tomcat: 官网:https://tomcat.apache.org/ 找到需要的版本下载即可,下载完成解压即可用: Tomcat目录 ...

  2. flutter---->阿里云oss的插件

    目前为止,阿里云官方并没有dart版本的oss sdk,所以才开发了这个插件flutter_oss_aliyun提供对oss sdk的支持. flutter_oss_aliyun 一个访问阿里云oss ...

  3. scoket用法

    一.scoket基本介绍 1.scoket简介(以下是来自chatgpt回答) 1)Socket(套接字)是计算机网络中用于描述主机之间通信的一种机制.它定义了一种标准的接口, 使得应用程序可以利用网 ...

  4. 最新版本 Stable Diffusion 开源 AI 绘画工具之使用篇

    目录 界面参数 采样器 文生图(txt2img) 图生图(img2img) 模型下载 界面参数 在使用 Stable Diffusion 开源 AI 绘画之前,需要了解一下绘画的界面和一些参数的意义 ...

  5. 逍遥自在学C语言 | 逻辑运算符

    前言 一.人物简介 第一位闪亮登场,有请今后会一直教我们C语言的老师 -- 自在. 第二位上场的是和我们一起学习的小白程序猿 -- 逍遥. 二.构成和表示方式 逻辑运算符是用来比较和操作布尔值的运算符 ...

  6. 强大的 apt-get 命令

    强大的 apt-get 命令(小结) 一.ubuntu下管理软件最方便的非 apt-get 工具莫属了,它的常见用法稍微整理一下供以后参考(详细见 man apt-get ): 1.更新源,升级软件和 ...

  7. [Linux]Xshell连接Centos7能Ping通但无法连接问题[ssh(d)+firewalld/iptables+chkconfig]

    一 方案与思路 0 xshell客户端监测是否能够ping通目标服务器. 前提:知晓目标服务器IP地址 Linux: ifconfig -a Windows: ipconfig -a 1 利用fire ...

  8. CI框架调用第三方类库

    public function index() { //调用第三方类库 /* * 注意事项: * library 里面调用的名字首字母必须是 大写 * 使用它的方法时 使用小写 */ $this-&g ...

  9. 【SSM项目】尚筹网(五)项目改写:使用前后端分离的SpringSecurityJWT认证

    在项目中加入SpringSecurity 1 加入依赖 <!-- SpringSecurity --> <dependency> <groupId>org.spri ...

  10. javasec(八)jndi注入

    JNDI JNDI(全称Java Naming and Directory Interface)是用于目录服务的Java API,它允许Java客户端通过名称发现和查找数据和资源(以Java对象的形式 ...