1.认识JSX语法★★★
<!-- <script>
// js语法中这种书写方式会直接报错
let element = <div>林夕</div>
console.log(element)
</script> -->
<script type="text/babel">
// 虽然这种写法没有报错是因为babel将JSX语法进行了转化
// 1.定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World"
}
}
render() {
// 这种结构代码即包含js代码也包含js的扩展代码即成为JSX
// 这种代码就是JSX代码
const { message } = this.state
const element = <h2>{message}</h2>//<h2>{message}</h2>代码本质是React.createElemnet("h2",null,"Hello World"),因为我们使用了bable
return element
}
}
// 2.创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
// 开发react项目都要用bable进行转化
</script>
这段element变量的声明右侧赋值的标签语法是什么呢?
- 它不是一段字符串(因为没有使用引号包裹);
- 它看起来是一段HTML元素,但是我们能在js中直接给一个变量赋值html吗?
- 其实是不可以的,如果我们将 type="text/babel" 去除掉,那么就会出现语法错误;
- 其实它是一段
jsx的语法;
JSX是什么?
- JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;
- 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
- 它不同于Vue中的模块语法,不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
还有const element = <h2>{message}</h2>又被称之为html in js;后续还有css in js;js in js;all in js
注意:没有这种写法const element ="<h2>{message}</h2>"
为什么React选择了JSX
- React认为
渲染逻辑本质上与其他UI逻辑存在内在耦合- 比如
UI需要绑定事件(button、a原生等等); - 比如
UI中需要展示数据状态 - 比如
在某些状态发生改变时,又需要改变UI;
- 比如
- 他们之间是
密不可分,所以React没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件; - JSX其实是嵌入到JavaScript中的一种结构语法;
- JSX的书写规范:
- JSX的顶层
只能有一个根元素,所以我们很多时候会在外层包裹一个div元素; - 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
- JSX中的标签可以是单标签,也可以是双标签;
注意:如果是单标签,必须以/>结尾;
- JSX的顶层
2.JSX的基本使用★★★
- jsx中的注释
render(){ return( <div> {/* 我是注释 */} <h1>{this.state.message}</h1> <button onClick={this.btnClick}>点击测试按钮</button> </div> ) } - JSX嵌入变量作为子元素
- 情况一:当变量是
Number、String、Array类型时,可以直接显示 - 情况二:当变量是
null、undefined、Boolean类型时,内容为空;- 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
- 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
- 情况三:
Object对象类型不能作为子元素(not valid as a React child)
- 情况一:当变量是
- JSX嵌入表达式
运算表达式三元运算符执行一个函数
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
message: "我是测试类组件",
arr: ["数据1", "数据2", "数据3"],
count: 100,
type1: null,
type2: undefined,
type3: true,
obj: {
name: "测试"
}
}
}
render() {
const { message, arr, count } = this.state;
const { type1, type2, type3 } = this.state;
const { obj } = this.state;
const allText = message + "" + count
return (
<div>
{/* 1.Number/String/Array直接显示出来 */}
<h1>{message}</h1>
<h1>{arr}</h1>
<h1>{count}</h1>
{/* 2.null、undefined、Boolean类型显示为空*/}
<h2>{type1}</h2>
<h2>{type2}</h2>
<h2>{type3.toString()}</h2>
{/* 3.Object对象类型不能作为子元素*/}
<h2>{obj.name}</h2>
<h2>{Object.keys(obj)[0]}</h2>
{/* 4.可以插入对应的表达式*/}
<h3>{message + arr}</h3>
<h3>{allText}</h3>
<h3>{count>100?"大于":"不大于"}</h3>
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
- jsx绑定属性
// 1.定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
title: "哈哈哈",
imgURL: "",
href: "https://www.baidu.com",
isActive: true,
objStyle: {color: "blue", fontSize: "24px"}
}
}
render() {
const { title, imgURL, href, isActive, objStyle } = this.state
// 1.class绑定的写法一: 字符串的拼接
const className = `ceshi ceshi1 ${isActive ? 'active': ''}`
// 2.class绑定的写法二: 将所有的class放到数组中
const classList = ["ceshi", "ceshi1"]
if (isActive) classList.push("active")
// 3.class绑定的写法三: 第三方库classnames -> npm install classnames
return (
<div>
{ /* 1.基本属性绑定 */ }
<h2 title={title}>h2元素</h2>
{/*<img src={imgURL} alt=""/>*/}
<a href={href}>百度</a>
{ /* 2.绑定class属性: 使用className */ }
<h2 className={className}>哈哈哈哈</h2>
<h2 className={classList.join(" ")}>测试</h2>
{ /* 3.绑定style属性: 绑定对象类型 */ }
<h2 style={{color: "red", fontSize: "30px"}}>测试</h2>
<h2 style={objStyle}>测试</h2>
</div>
)
}
}
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
3.JSX的事件绑定★★★
我们都知道在原生的Dom中进行事件绑定有两种方式:
- 获取DOM原生,添加监听事件
- 直接绑定onClick
- React 事件的命名采用
小驼峰式(camelCase),而不是纯小写。 - 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。通过{}
this绑定问题
你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
这并不是 React 特有的行为;这其实与 JavaScript 函数工作原理有关。通常情况下,如果你没有在方法后面添加 (),例如 onClick={this.handleClick},你应该为这个方法绑定 this。
- 原因是btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;
- 内部调用时,并不知道要如何绑定正确的this;
如果觉得使用 bind 很麻烦,这里有两种方式可以解决。你可以使用 public class fields 语法 正确地绑定回调函数:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
handleClick = () => {
console.log('this is:', this);
};
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
Create React App 默认启用此语法。
如果你没有使用 class fields 语法,你可以在回调中使用箭头函数:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
由此可知:
- bind给handleClick显示绑定this,上一节提到
- 使用 ES6 class fields 语法
- 事件监听时传入箭头函数
参数传递
class App extends React.Component {
handleClick1(event, name, age) {
console.log(event);
};
handleClick2(event, name, age) {
// 注意测试3和测试4
// 测试3按钮需要绑定this,后面参数追加对照,age就变成了我们的event参数
// 测试4是我们期望的接参数期望结果
console.log(event);
console.log(name, age);
};
render() {
return (
<div>
{ /* 1. event参数传递,event可以阻止默认行为 */ }
<button onClick={this.handleClick1.bind(this)}>测试1</button>
<button onClick={(event)=>{this.handleClick2(event)}}>测试2</button>
{ /* 2. 额外参数传参数 */ }
<button onClick={this.handleClick2.bind(this,"林夕",18)}>测试3</button>
<button onClick={(event)=>{this.handleClick2(event,"林夕",18)}}>测试4</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
4.JSX的条件渲染★★★
通常你的组件会需要根据不同的情况显示不同的内容。在 React 中,你可以通过使用 JavaScript 的 if 语句、&& 和 ? : 运算符来选择性地渲染 JSX。
class App extends React.Component {
constructor() {
super();
this.state = {
ready: false,
}
}
render() {
const { ready } = this.state
let element = null
if (ready) {
element = <h2>我是方式一true</h2>
} else {
element = <h2>我是方式一false</h2>
}
return (
<div>
{/*方式一: 条件判断语句 */}
{element}
{/*方式二: 三元运算符 */}
{ready ? <h2>方式二</h2> : <h2>方式二否则</h2>}
{/*方式三: 与运算符&& */}
{ready===true && <h2>方式三</h2> }
{ready===false && <h2>方式三false</h2> }
</div>
)
}
}
注意:切勿将数字放在 && 左侧:
JavaScript 会自动将左侧的值转换成布尔类型以判断条件成立与否。然而,如果左侧是 0,整个表达式将变成左侧的值(0),React 此时则会渲染 0 而不是不进行渲染。
5.JSX的列表渲染★★★
class App extends React.Component {
constructor() {
super();
this.state = {
arr: [12, 23, 4, 5],
active: 0
}
}
changeClick(active) {
this.setState({
active
})
}
render() {
const { arr, active } = this.state
return (
<div>
{
arr.map((item, index) => {
return <li className={active == index ? 'active' : ''} key={item} onClick={() => this.changeClick(index)}>{item}</li>
})
}
</div>
)
}
}
通过案例观察以及官网介绍我们可以知道
使用 filter() 筛选需要渲染的组件和使用 map() 把数组转换成组件数组。
- 在React中,展示列表最多的方式就是使用数组的map高阶函数;
在使用列表渲染时候会出现报错因此我们需要加上key来区分
- key主要的作用是为了提高diff算法时的效率;
- key 值在兄弟节点之间必须是唯一的。 不过不要求全局唯一,在不同的数组中可以使用相同的 key。
- key 值不能改变,否则就失去了使用 key 的意义!所以千万不要在渲染时动态地生成 key。
6.JSX的原理和本质★★★
实际上,JSX 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖。
- createElement需要传递三个参数:
- 参数一:type 当前ReactElement的类型;
- 如果是标签元素,那么就使用字符串表示 “div”;
- 如果是组件元素,那么就直接使用组件的名称;
- 参数二:config
- 所有jsx中的属性都在config中以对象的属性和值的形式存储;
- 比如传入className作为元素的class;
- 参数三:children
- 存放在标签中的内容,以children数组的方式进行存储;
- 当然,如果是多个元素呢?React内部有对它们进行处理 如下 JSX 代码:
- 参数一:type 当前ReactElement的类型;
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>
会编译为:
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
如果没有子节点,你还可以使用自闭合的标签形式,如:
<div className="sidebar" />
会编译为:
React.createElement(
'div',
{className: 'sidebar'}
)
如果你想测试一些特定的 JSX 会转换成什么样的 JavaScript,你可以尝试使用 在线的 Babel 编译器。 如果我们使用babel编译器转换之后我们复制代码就可以进行删除babel.js使用
render() {
/*#__PURE__*/
const element = React.createElement("div", {
children: /*#__PURE__*/React.createElement("h2", {
children: "\u4F60\u597D\u6D4B\u8BD5"
})
});
return element
}
jsx – ReactElement对象 – 真实DOM