JSX簡介以及在Vue3中使用JSX+antd開發(fā)組件

一、什么是JSX?

就像這樣:

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

從表面上來看這和html沒啥區(qū)別,但是請看左邊;我們把一段html代碼直接賦值給了一個變量。
JSX=javascript xml就是Javascript和XML結(jié)合的一種格式。是 JavaScript 的語法擴展,只要你把HTML代碼寫在JS里,那就是JSX

二、書寫規(guī)范

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

1、jsx頂部只能有一個根元素,通常我們用<div></div>包起來(在Vue3中也可以使用和React一樣不占據(jù)Dom結(jié)構(gòu)的Fragment<></>空標(biāo)簽)。
2、為了方便閱讀,jsx外面需要包上一層小括號()
3、標(biāo)簽要閉合(單邊標(biāo)簽得有斜杠)

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.函數(shù)調(diào)用

const func1 = ()=>{ return (<div>func1</div>) }
let jsx = (
    <div>
        <h1>
        {
            //如果在render外定義的函數(shù),請注意加this:this.func1()
            func1()
        }
        </h1>
    </div>
)
JSX 綁定屬性

1.綁定普通屬性

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

2.綁定style

在jsx中,windows風(fēng)格的命名方式(屬性、style、方法、event)都需要轉(zhuǎn)換成駝峰式的寫法,比如,正常寫一個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模塊化的方式進行樣式導(dǎo)入后綁定。即在vue.config.js文件中css配置項中開啟css模塊化(requireModuleExtension: true),然后把css文件命名設(shè)置成***.module.less

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

JSX中綁定事件類似在HTML原生中綁定事件,只不過React中事件命名采用小駝峰(camelCase),而不是純小寫;(Vue3中經(jīng)過測試也通用)

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

在jsx中,不允許使用if、if-else,請使用三元運算符或者邏輯與&&
同樣,也允許使用for循環(huán),請使用JS中的高階函數(shù)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的優(yōu)勢和各種指令去使用JSX

當(dāng)出現(xiàn)以下場景,雖然下列寫法也能實現(xiàn)想要的效果,但是他不僅冗長,而且我們?yōu)槊總€級別標(biāo)題重復(fù)書寫了 。當(dāng)我們添加錨元素時,我們必須在每個 v-if/v-else-if 分支中再次重復(fù)它

<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
    }
  }
})

這里用模板并不是最好的選擇:不但代碼冗長,而且在每一個級別的標(biāo)題中重復(fù)書寫了 <slot></slot>,在要插入錨點元素時還要再次重復(fù)。
雖然模板在大多數(shù)組件中都非常好用,但是顯然在這里它就不合適了。那么,我們來嘗試使用 render 函數(shù)重寫上面的例子:

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // 標(biāo)簽名稱
      this.$slots.default // 子節(jié)點數(shù)組
    )
  },
  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中導(dǎo)入defineComponent

如何導(dǎo)入JSX導(dǎo)入的組件以及向子組件傳值
<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();// 綁定表單,對提交時觸發(fā)表單驗證規(guī)則
        let formState = reactive({
            name: '',
            password: '',
            type: null // 下拉下選擇默認值 設(shè)置為null  默認展示placeholder的值 設(shè)置為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',
                    // 觸發(fā)方式   ['change', 'blur']  可以這樣多種寫法
                    // type:'string|array|number' //這是規(guī)定了類型  字符串  數(shù)組   數(shù)字 
                },
                {
                    validator: validatePassword,//自定義規(guī)則
                    trigger: 'blur'
                }
            ],
            type: [
                {
                    required: true,
                    message: '請選擇類型',
                    trigger: 'blur'
                }
            ]
        };

        const onSubmit = () => {
            // formRef 就是為了這一步  這樣點擊提交的時候  會觸發(fā)表單驗證 注:綁定formRef時不是{this.formRef}
            console.log(formRef.value)
            formRef.value
                .validate()
                .then(() => {
                    console.log('values', formState);
                    // 表單驗證通過就會執(zhí)行這里  你就可以操作了
                    formRef.value.resetFields(); // 重置表單到初始狀態(tài)
                })
                .catch((error) => {
                    console.log('error', error);
                });
        }
    
        return {
            formRef,
            formState,
            rules,
            typeArr,
            onSubmit
        }
    },
    render() {
        return (
            <>
                <Form
                    ref="formRef"
                    model={this.formState} // 綁定表單的初始對象
                    rules={this.rules} // 表單的規(guī)則綁定 可以自定義規(guī)則
                    wrapperCol={{ span: 18 }} labelCol={{ span: 4 }} // 控制每一行 label 和 輸入框 的占比 
                >
            {/* name 表單域 model 字段,在使用 validate(自定義規(guī)則)、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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容