一、什么是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>
</>
);
}
})
