• 拥抱jsx,开启vue3用法的另一种选择


    📝背景

    公司高级表单组件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
    
    
    
    <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 Worlddiv>

    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
    
     
    // jsx
     render: () => {
        return (
           <span>{ a + b }span>
        )
    }

    条件渲染

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

    样式绑定

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

    列表循环

    // template v-for
    
    
    
    // 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 
    
    
    //jsx
     render: () => {
        return (
          <div {...properties}>div>
        )
      }
    
    

    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
    
    
    // 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
    
    
    // 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组件

    // 父
     
    
     
    <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吧,用久了你就会发现,哎,真香。

  • 相关阅读:
    数字化技术促进传统产业升级,看供应链SCM系统如何赋能锂电池制造业降本增效
    第九章《字符串》第2节:字符串常用操作
    基于PHP+MySQL游戏视频网站的设计与实现
    【从0到1开发一个网关】网关Mock功能的实现
    古代汉语(王力版)笔记 绪论
    一款超强的 Python 分析工具!
    TCP/IP四层模型
    右键菜单和弹出菜单的区别
    GBase 8d的特性-标准化
    天翼云全栈赋能OpenClaw,打造会干活的专属AI!
  • 原文地址:https://www.cnblogs.com/mrwh/p/17469925.html