背景

公司高级表单组件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. 递推求解DAG最长路径长度及最长路径条数

    说明 在一般图中,求解最长路或最短路只能通过最短路算法解决 但是在DAG中,由于不存在环,因此可以通过递推,以线性复杂度计算处最长路或最短路.当然需要首先对有向图进行Tarjan缩点转化为DAG 例题 ...

  2. 第四部分:Spdlog日志库的核心组件分析-logger

    Spdlog是一个快速且可扩展的C++日志库,它支持多线程和异步日志记录.在本文中,我们将分析Spdlog日志库的核心代码,探究其实现原理和代码结构. Spdlog的基本架构 上一篇文章介绍了spdl ...

  3. 微软 New Bing AI 申请与使用保姆级教程(免魔法)

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 最近的 AI 技术实在火爆,从 OpenAI 的 ChatGPT,到微软的 New Bi ...

  4. Vue2依赖收集原理

    观察者模式定义了对象间一对多的依赖关系.即被观察者状态发生变动时,所有依赖于它的观察者都会得到通知并自动更新.解决了主体对象和观察者之间功能的耦合. Vue中基于 Observer.Dep.Watch ...

  5. Bean的自动装配(Autowired)

    Bean的自动装配(Autowired) 自动装配是Spring满足bean依赖的一种方式 Spring会在上下文中自动寻找,并自动给bean装配属性 在Spring中有三种自动装配的方式 在xml中 ...

  6. easy-excel读取远程地址获得文件进行上传

    背景 作为一个快五年的程序员,一直以来还没有自己维护过自己的技术栈,最近也是有时间,所以也是下定决心,从头开始,一步一步的夯基础.最近在系统化的学习easy-excel,今天遇到了一个问题,特意记录一 ...

  7. Web前端开发必看的100道大厂面试题

    1. 说说gulp和webpack的区别 开放式题目 Gulp强调的是前端开发的工作流程.我们可以通过配置一系列的task,定义task处理的事务(例如文件压缩合并.雪碧图.启动server.版本控制 ...

  8. Docker容器内不能联网的6种解决方案

    Docker容器内不能联网的6种解决方案 注:下面的方法是在容器内能ping通公网IP的解决方案,如果连公网IP都ping不通,那主机可能也上不了网(尝试ping 8.8.8.8) 1.使用–net: ...

  9. kube-apiserver启动命令参数解释

    在apiserver启动时候会有很多参数来配置启动命令,有些时候不是很明白这些参数具体指的是什么意思. 我的kube-apiserver启动命令参数: cat > /usr/lib/system ...

  10. kubernetes(k8s) 存储动态挂载

    使用 nfs 文件系统 实现kubernetes存储动态挂载 1. 安装服务端和客户端 root@hello:~# apt install nfs-kernel-server nfs-common 其 ...