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