JSX简介以及在Vue3中使用JSX+antd开发组件

一、什么是JSX?

就像这样:

let jsx = <h1>hello world</h1>;

从表面上来看这和html没啥区别,但是请看左边;我们把一段html代码直接赋值给了一个变量。
JSX=javascript xml就是Javascript和XML结合的一种格式。是 JavaScript 的语法扩展,只要你把HTML代码写在JS里,那就是JSX

二、书写规范

JSX支持换行
let jsx = (
    <div>
        <h1>hello world</h1>
        <button/>
    </div>
)

1、jsx顶部只能有一个根元素,通常我们用<div></div>包起来(在Vue3中也可以使用和React一样不占据Dom结构的Fragment<></>空标签)。
2、为了方便阅读,jsx外面需要包上一层小括号()
3、标签要闭合(单边标签得有斜杠)

jsx的注释都需要用花括号包起来
{
    //我是单行注释
}

{/*我是一段注释*/}
jsx插入变量
const _c = 'hello world';
let jsx = (
    <div>
        <h1>{ _c }</h1>
    </div>
)
jsx表达式嵌入

1.运算表达式

const a = 1;
const b = 1;
let jsx = (
  <div>{ a + b }</div>
)

2.三元表达式

let _t = 'hello world';
let a = 1;
let b = 2;
let hasButton = true;
let jsx = (
  <>
    <h1>{ _t === 'hello world' ? a :b }</h1>
    {
      //如果hasButton为true,则渲染button组件
      hasButton && <button/>
     }
  </>
)

3.函数调用

const func1 = ()=>{ return (<div>func1</div>) }
let jsx = (
    <div>
        <h1>
        {
            //如果在render外定义的函数,请注意加this:this.func1()
            func1()
        }
        </h1>
    </div>
)
JSX 绑定属性

1.绑定普通属性

let jsx = (
    <>
        <h1 title={title}>hello world</h1>
    </>
)

2.绑定style

在jsx中,windows风格的命名方式(属性、style、方法、event)都需要转换成驼峰式的写法,比如,正常写一个style指定左边的外边距:margin-left:‘10px’,需要换成:marginLeft: '10px'

let jsx = (
    <>
        <h1 style={{ marginLeft:'10px',width:'100%' }}>hello world</h1>
    </>
)

3.绑定class

在jsx中,class属性需要指定为className,因为class在JavaScript中是保留字段

const hasCss = true;
const h1Css = [
    'flex',
    hasCss ? 'css' : 'noCss',
]
let jsx = (
    <>
        <h1 className='flex'>hello world</h1>
        <h1 className={h1Css}>hello world</h1>
    </>
)

在vue3+jsx中,jsx文件里面可以用css模块化的方式进行样式导入后绑定。即在vue.config.js文件中css配置项中开启css模块化(requireModuleExtension: true),然后把css文件命名设置成***.module.less

import style from './style.module.less'
let jsx = (
    <>
        <h1 className={style.h1Css}>hello world</h1>
    </>
)
JSX 绑定事件

JSX中绑定事件类似在HTML原生中绑定事件,只不过React中事件命名采用小驼峰(camelCase),而不是纯小写;(Vue3中经过测试也通用)

function fnc(){}
let jsx = (
  <>
     <button onClick={this.fnc}/>
  </>
)
JSX 条件渲染

在jsx中,不允许使用if、if-else,请使用三元运算符或者逻辑与&&
同样,也允许使用for循环,请使用JS中的高阶函数map、filter……

const t = 'hello world';
const arg1 = 1;
const arg2 = 2;
const hasButton = true;
const list = [1,2,3,4,5,6,7,8,9];
let jsx = (
    <div>
        <h1>
        {
            t === 'hello world' ?  arg1 : arg2
        }
        </h1>
    {
            //如果hasButton为true,则渲染button组件
            hasButton && <button/>
        }
        <ul>
        {
            list.map((item) => <li>{item}</li>)
        }
        </ul>
    </div>
)

二、为什么我们要抛弃Vue的优势和各种指令去使用JSX

当出现以下场景,虽然下列写法也能实现想要的效果,但是他不仅冗长,而且我们为每个级别标题重复书写了 。当我们添加锚元素时,我们必须在每个 v-if/v-else-if 分支中再次重复它

<script type="text/x-template" id="anchored-heading-template">
  <h1 v-if="level === 1">
    <slot></slot>
  </h1>
  <h2 v-else-if="level === 2">
    <slot></slot>
  </h2>
  <h3 v-else-if="level === 3">
    <slot></slot>
  </h3>
  <h4 v-else-if="level === 4">
    <slot></slot>
  </h4>
  <h5 v-else-if="level === 5">
    <slot></slot>
  </h5>
  <h6 v-else-if="level === 6">
    <slot></slot>
  </h6>
</script>
Vue.component('anchored-heading', {
  template: '#anchored-heading-template',
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

这里用模板并不是最好的选择:不但代码冗长,而且在每一个级别的标题中重复书写了 <slot></slot>,在要插入锚点元素时还要再次重复。
虽然模板在大多数组件中都非常好用,但是显然在这里它就不合适了。那么,我们来尝试使用 render 函数重写上面的例子:

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // 标签名称
      this.$slots.default // 子节点数组
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

三、如何在vue3中使用JSX

vue3中jsx的两种写法

1.在vue3中,可以直接使用render选项编写。

import { defineComponent } from "vue";
export default defineComponent({
  name: "Jsx",
  render() {
    return <div>我是一个div</div>;
  },
});

2.也可以在setup中返回

import { defineComponent } from "vue";
export default defineComponent({
  name: "Jsx",
  setup() {
    return () => <div>我是div</div>;
  },
});

在项目目录中新建***.jsx文件;
jsx文件中从vue中导入defineComponent

如何导入JSX导入的组件以及向子组件传值
<template>
  <Demo3  name="我是由父组件传向子组件的"/>
</template>
<script>
import Demo3 from'./components/Demo3.jsx'
export default {
  name: 'App',
  components: {
    Demo3
  },
  setup(){
    return{
    }
  }
}
</script>
子组件接收并使用父组件传过来的值
import { defineComponent } from 'vue'
export default defineComponent ({
    props:{
        name:{
            type:String
        }
    },
    setup(props) {
        const render = () =>{
            return (
                <div>
                    hello {props.name}
                </div>
            );
        }
        return render
    },
})
子组件传值给父组件
import { defineComponent } from 'vue'
export default defineComponent ({
    setup({emit}) {
        let childNode = '不是老王';
        emit('clickMe',childNode )
        const render = () =>{
            return (
                <>
                  请无视我
                </>
            );
        }
        return render
    },
})
父组件接收子组件传值
<template>
  <Demo3  @clickMe="clickMe"/>
</template>
<script>
import Demo3 from'./components/Demo3.jsx'
export default {
  name: 'App',
  components: {
    Demo3
  },
  setup(){
    const clickMe = (val) => {
      console.log(val)
    }
    return{
      locale: zhCN,
      clickMe
    }
  }
}
</script>

vue3+jsx+antd--Demo

import { defineComponent, ref, reactive } from 'vue'
import { Form, Input, Button  } from 'ant-design-vue';
export default defineComponent({
    setup() {
        let formRef = ref();// 绑定表单,对提交时触发表单验证规则
        let formState = reactive({
            name: '',
            password: '',
            type: null // 下拉下选择默认值 设置为null  默认展示placeholder的值 设置为1 展示typeArr  id为1 的 ‘类型1’
        });
        let typeArr = ref([
            {
                id: 1,
                type: '类型1'
            },
            {
                id: 2,
                type: '类型2'
            }
        ]);
        const validatePassword = () => {
            if (formState.password == '123456789') {
                return Promise.reject('密码不能是123456789');
            } else {
                return Promise.resolve();
            }
        };
        let rules = {
            name: [
                {
                    required: true,
                    message: '请输入账号',
                    trigger: 'blur'
                },
            ],
            password: [
                {
                    required: true,
                    message: '请输入密码',
                    trigger: 'change',
                    // 触发方式   ['change', 'blur']  可以这样多种写法
                    // type:'string|array|number' //这是规定了类型  字符串  数组   数字 
                },
                {
                    validator: validatePassword,//自定义规则
                    trigger: 'blur'
                }
            ],
            type: [
                {
                    required: true,
                    message: '请选择类型',
                    trigger: 'blur'
                }
            ]
        };

        const onSubmit = () => {
            // formRef 就是为了这一步  这样点击提交的时候  会触发表单验证 注:绑定formRef时不是{this.formRef}
            console.log(formRef.value)
            formRef.value
                .validate()
                .then(() => {
                    console.log('values', formState);
                    // 表单验证通过就会执行这里  你就可以操作了
                    formRef.value.resetFields(); // 重置表单到初始状态
                })
                .catch((error) => {
                    console.log('error', error);
                });
        }
    
        return {
            formRef,
            formState,
            rules,
            typeArr,
            onSubmit
        }
    },
    render() {
        return (
            <>
                <Form
                    ref="formRef"
                    model={this.formState} // 绑定表单的初始对象
                    rules={this.rules} // 表单的规则绑定 可以自定义规则
                    wrapperCol={{ span: 18 }} labelCol={{ span: 4 }} // 控制每一行 label 和 输入框 的占比 
                >
            {/* name 表单域 model 字段,在使用 validate(自定义规则)、resetFields(表单重置) 方法的情况下,该属性是必填的 */}
                    <Form.Item ref="name" label="账号" name="name">
                        <Input v-model={[this.formState.name, 'value']} />
                    </Form.Item>
                    <Form.Item ref="password" label="密码" name="password">
                        <Input v-model={[this.formState.password, 'value']} />
                    </Form.Item>
                    <Form.Item name="type" label="下拉选择">
                        <a-select ref="type" v-model={[this.formState.type, 'value']} placeholder="请选择类型" >
                            {this.typeArr.map((item) => {
                                return (
                                    <a-select-option v-model={[item.id, 'value']} key={item.id}>
                                        {item.type}
                                    </a-select-option>
                                );
                            })}
                        </a-select>
                    </Form.Item>
                    <Form.Item>
                        <Button onClick={this.onSubmit}>提交</Button>
                    </Form.Item>
                </Form>
            </>
        );
    }
})
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,312评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,578评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,337评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,134评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,161评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,303评论 1 280
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,761评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,421评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,609评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,450评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,504评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,194评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,760评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,836评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,066评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,612评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,178评论 2 341

推荐阅读更多精彩内容