一莫其、什么是JSX?
就像這樣:
let jsx = <h1>hello world</h1>;
從表面上來(lái)看這和html沒(méi)啥區(qū)別耸三,但是請(qǐng)看左邊乱陡;我們把一段html代碼直接賦值給了一個(gè)變量。
JSX=javascript xml就是Javascript和XML結(jié)合的一種格式仪壮。是 JavaScript 的語(yǔ)法擴(kuò)展憨颠,只要你把HTML代碼寫(xiě)在JS里,那就是JSX积锅。
二爽彤、書(shū)寫(xiě)規(guī)范
JSX支持換行
let jsx = (
<div>
<h1>hello world</h1>
<button/>
</div>
)
1、jsx頂部只能有一個(gè)根元素缚陷,通常我們用<div></div>包起來(lái)(在Vue3中也可以使用和React一樣不占據(jù)Dom結(jié)構(gòu)的Fragment<></>空標(biāo)簽)适篙。
2、為了方便閱讀蹬跃,jsx外面需要包上一層小括號(hào)()
3匙瘪、標(biāo)簽要閉合(單邊標(biāo)簽得有斜杠)
jsx的注釋都需要用花括號(hào)包起來(lái)
{
//我是單行注釋
}
{/*我是一段注釋*/}
jsx插入變量
const _c = 'hello world';
let jsx = (
<div>
<h1>{ _c }</h1>
</div>
)
jsx表達(dá)式嵌入
1.運(yùn)算表達(dá)式
const a = 1;
const b = 1;
let jsx = (
<div>{ a + b }</div>
)
2.三元表達(dá)式
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ù)蝶缀,請(qǐng)注意加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)換成駝峰式的寫(xiě)法,比如柄慰,正常寫(xiě)一個(gè)style指定左邊的外邊距:margin-left:‘10px’,需要換成:marginLeft: '10px'鳍悠;
let jsx = (
<>
<h1 style={{ marginLeft:'10px',width:'100%' }}>hello world</h1>
</>
)
3.綁定class
在jsx中,class屬性需要指定為className坐搔,因?yàn)閏lass在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模塊化的方式進(jìn)行樣式導(dǎo)入后綁定。即在vue.config.js文件中css配置項(xiàng)中開(kāi)啟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原生中綁定事件蠢挡,只不過(guò)React中事件命名采用小駝峰(camelCase),而不是純小寫(xiě);(Vue3中經(jīng)過(guò)測(cè)試也通用)
function fnc(){}
let jsx = (
<>
<button onClick={this.fnc}/>
</>
)
JSX 條件渲染
在jsx中业踏,不允許使用if禽炬、if-else,請(qǐng)使用三元運(yùn)算符或者邏輯與&&
同樣勤家,也允許使用for循環(huán)腹尖,請(qǐng)使用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)勢(shì)和各種指令去使用JSX
當(dāng)出現(xiàn)以下場(chǎng)景,雖然下列寫(xiě)法也能實(shí)現(xiàn)想要的效果晓殊,但是他不僅冗長(zhǎng)断凶,而且我們?yōu)槊總€(gè)級(jí)別標(biāo)題重復(fù)書(shū)寫(xiě)了 。當(dāng)我們添加錨元素時(shí)巫俺,我們必須在每個(gè) 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
}
}
})
這里用模板并不是最好的選擇:不但代碼冗長(zhǎng)认烁,而且在每一個(gè)級(jí)別的標(biāo)題中重復(fù)書(shū)寫(xiě)了 <slot></slot>,在要插入錨點(diǎn)元素時(shí)還要再次重復(fù)介汹。
雖然模板在大多數(shù)組件中都非常好用却嗡,但是顯然在這里它就不合適了。那么嘹承,我們來(lái)嘗試使用 render 函數(shù)重寫(xiě)上面的例子:
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // 標(biāo)簽名稱
this.$slots.default // 子節(jié)點(diǎn)數(shù)組
)
},
props: {
level: {
type: Number,
required: true
}
}
})
三窗价、如何在vue3中使用JSX
vue3中jsx的兩種寫(xiě)法
1.在vue3中,可以直接使用render選項(xiàng)編寫(xiě)叹卷。
import { defineComponent } from "vue";
export default defineComponent({
name: "Jsx",
render() {
return <div>我是一個(gè)div</div>;
},
});
2.也可以在setup中返回
import { defineComponent } from "vue";
export default defineComponent({
name: "Jsx",
setup() {
return () => <div>我是div</div>;
},
});
在項(xiàng)目目錄中新建***.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>
子組件接收并使用父組件傳過(guò)來(lái)的值
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 (
<>
請(qǐng)無(wú)視我
</>
);
}
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();// 綁定表單,對(duì)提交時(shí)觸發(fā)表單驗(yàn)證規(guī)則
let formState = reactive({
name: '',
password: '',
type: null // 下拉下選擇默認(rèn)值 設(shè)置為null 默認(rèn)展示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: '請(qǐng)輸入賬號(hào)',
trigger: 'blur'
},
],
password: [
{
required: true,
message: '請(qǐng)輸入密碼',
trigger: 'change',
// 觸發(fā)方式 ['change', 'blur'] 可以這樣多種寫(xiě)法
// type:'string|array|number' //這是規(guī)定了類型 字符串 數(shù)組 數(shù)字
},
{
validator: validatePassword,//自定義規(guī)則
trigger: 'blur'
}
],
type: [
{
required: true,
message: '請(qǐng)選擇類型',
trigger: 'blur'
}
]
};
const onSubmit = () => {
// formRef 就是為了這一步 這樣點(diǎn)擊提交的時(shí)候 會(huì)觸發(fā)表單驗(yàn)證 注:綁定formRef時(shí)不是{this.formRef}
console.log(formRef.value)
formRef.value
.validate()
.then(() => {
console.log('values', formState);
// 表單驗(yàn)證通過(guò)就會(huì)執(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} // 綁定表單的初始對(duì)象
rules={this.rules} // 表單的規(guī)則綁定 可以自定義規(guī)則
wrapperCol={{ span: 18 }} labelCol={{ span: 4 }} // 控制每一行 label 和 輸入框 的占比
>
{/* name 表單域 model 字段骤竹,在使用 validate(自定義規(guī)則)帝牡、resetFields(表單重置) 方法的情況下,該屬性是必填的 */}
<Form.Item ref="name" label="賬號(hào)" 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="請(qǐng)選擇類型" >
{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>
</>
);
}
})