什么是 JSX
- Facebook 起草的 JS 扩展语法,本质是一个 JS 对象,会被 babel 编译,最终会被转换为 React.createElement;
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/react/18.2.0/umd/react.production.min.js"></script> <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/react-dom/18.2.0/umd/react-dom.production.min.js"></script> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="root"></div> <div id="root2"></div> <script type="text/babel"> const h1 = ( <h1> Hello World <span>span元素</span> </h1> ); ReactDOM.render(h1, document.getElementById("root2")); // 两种方式等价 const _h1 = React.createElement( "h1", {}, "Hello World", React.createElement("span", {}, "span元素") ); ReactDOM.render(_h1, document.getElementById("root")); </script> </body> </html>
- 它用于描述 UI 界面,并且其完全可以和 JavaScript 融合在一起使用;
为什么 React 选择了 JSX
-
React 认为渲染逻辑本质上与其他 UI 逻辑存在内在耦合;
- 比如 UI 需要绑定事件 (button、a 原生等等);
- 比如 UI 中需要展示数据状态,在某些状态发生改变时,又需要改变 UI;
-
他们之间是密不可分,所以 React 没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件 (Component);
JSX 的书写规范
-
JSX 的顶层只能有一个根元素,所以很多时候会在外层包裹一个 div 原生,或者使用如下方式;
- 使用 <React.Fragment></React.Fragment> 后,并不会被渲染出来,类似 vue 中的 template 标签;
- 使用 <></> 后,并不会被渲染出来,类似 vue 中的 template 标签;
-
为了方便阅读,通常在 JSX 的外层包裹一个小括号 (),这样可以方便阅读,并且 jsx 可以进行换行书写;
-
JSX 中的标签可以是单标签,也可以是双标签 (如果是单标签,必须以 /> 结尾);
JSX 语法解析
在 JSX 中嵌入表达式
-
如果 jsx 中的内容是动态的,可以通过表达式来获取:
- 书写规则:{ 表达式 }
- 大括号内可以是 变量、字符串、数组、函数调用 等任意 js 表达式;
-
jsx 中的注释;
<div> {/* 我是一段注释 */} <h2>Hello World</h2> </div>
-
JSX 嵌入变量:
- 情况一:当变量是 Number、String、Array 类型时,可以直接显示;
- 情况二:当变量是 null、undefined、Boolean 类型时内容为空,如果希望可以显示,那么需要转成字符串;
- 情况三:对象类型不能作为子元素 (not valid as a React child);
class App extends React.Component { constructor(props) { super(props); this.state = { name: "why", age: 18, hobbies: ["篮球", "唱跳", "rap"], test1: null, test2: undefined, flag: false, friend: { name: "kobe", age: 40 } } } render() { return ( <div> {/* 我是一段注释 */} <h2>Hello World</h2> </div> <div> {/* 1.可以直接显示 */} <h2>{this.state.name}</h2> <h2>{this.state.age}</h2> <h2>{this.state.hobbies}</h2> {/* 2.不显示 */} <h2>{this.state.test1}</h2> <h2>{this.state.test1 + ""}</h2> <h2>{this.state.test2}</h2> <h2>{this.state.test2 + ""}</h2> <h2>{this.state.flag}</h2> <h2>{this.state.flag + ""}</h2> {/* 3.不显示,not valid as a React child 报错 */} <h2>123{this.state.friend}</h2> </div> ) } } ReactDOM.render(<App/>, document.getElementById("app"));
-
JSX 嵌入表达式
class App extends React.Component { constructor(props) { super(props); this.state = { firstName: "kobe", lastName: "bryant", age: 20 } } render() { return ( <div> {/* 运算表达式 */} <h2>{this.state.firstName + " " + this.state.lastName}</h2> {/* 三元运算符 */} <h2>{this.state.age >= 18 ? "成年人": "未成年人"}</h2> {/* 执行一个函数 */} <h2>{this.sayHello("kobe")}</h2> </div> ) } sayHello(name) { return "Hello " + name; } }
-
jsx 绑定属性:很多时候,描述的 HTML 原生会有一些属性,而我们希望这些属性也是动态的;
class App extends React.Component { constructor(props) { super(props); this.state = { title: "你好啊", imgUrl: "https://upload.jianshu.io/users/upload_avatars/1102036/c3628b478f06.jpeg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240", link: "https://www.baidu.com", active: false } } render() { return ( <div> <h2 title={this.state.title}>Hello World</h2> <img src={this.state.imgUrl} alt=""/> <a href={this.state.link} target="_blank">百度一下</a> <div className={"message " + (this.state.active ? "active": "")}>你好啊</div> <div className={["message", (this.state.active ? "active": "")].join(" ")}>你好啊</div> <div style={{fontSize: "30px", color: "red", backgroundColor: "blue"}}>我是文本</div> </div> ) } }
JSX 事件监听
-
事件绑定:
- React 事件的命名采用小驼峰式 (camelCase),而不是纯小写;
- 需要通过 {} 传入一个事件处理函数,这个函数会在事件发生时被执行;
class App extends React.Component { render() { return ( <div> <button onClick={this.btnClick}>点我一下(React)</button> </div> ); } btnClick() { console.log("React按钮点击了一下"); } }
-
this 绑定问题
- this 指向 丢失问题
- 原因是 事件函数 并不是我们主动调用的,而且 React 内部调用了 事件函数 函数;
- 而它内部调用时,并不知道要如何绑定正确的 this;
- 解决 this 指向 的问题
方案一
:bind 改变 this 指向;class App extends React.Component { constructor(props) { super(props); this.state = { message: "你好啊" } // 可以通过在构造方法中直接给 this.btnClick 绑定 this,避免在 jsx 中多次使用 bind this.btnClick = this.btnClick.bind(this); } render() { return ( <div> <button onClick={this.btnClick}>点我一下(React)</button> <button onClick={this.btnClick}>也点我一下(React)</button> </div> ) } btnClick() { console.log(this); console.log(this.state.message); } }
方案二
:使用 ES6 class fields 语法;class App extends React.Component { constructor(props) { super(props); this.state = { message: "你好啊" } } render() { return ( <div> <button onClick={this.btnClick}>点我一下(React)</button> <button onClick={this.btnClick}>也点我一下(React)</button> </div> ) } // ES6 中给类定义属性的方法,称之为 class fields 语法 btnClick = () => { console.log(this); console.log(this.state.message); } }
方案三
:事件监听时传入箭头函数(推荐);class App extends React.Component { constructor(props) { super(props); this.state = { message: "你好啊,李银河" } } render() { return ( <div> <button onClick={() => this.btnClick()}>点我一下(React)</button> <button onClick={() => this.btnClick()}>也点我一下(React)</button> </div> ) } btnClick() { console.log(this); console.log(this.state.message); } }
- this 指向 丢失问题
-
事件参数传递
- 情况一:获取 event 对象
class App extends React.Component { constructor(props) { render() { return ( <div> <a href="http://www.baidu.com" onClick={this.btnClick}>点我一下</a> </div> ) } btnClick(e) { e.preventDefault(); console.log(e); } }
- 获取更多参数:传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数
class App extends React.Component { constructor(props) { super(props); this.state = { names: ["衣服", "鞋子", "裤子"], }; } render() { return ( <div> <a href="http://www.baidu.com" onClick={this.aClick}> 点我一下 </a> {this.state.names.map((item, index) => { return ( <a href="#" onClick={(e) => this.aClick(e, item, index)}> {item} </a> ); })} </div> ); } aClick(e, item, index) { e.preventDefault(); console.log(item, index); console.log(e); } }
- 情况一:获取 event 对象
JSX 条件渲染
-
条件判断语句
class App extends React.Component { constructor(props) { super(props); this.state = { isLogin: true, }; } render() { let titleJsx = null; if (this.state.isLogin) { titleJsx = <h2>欢迎回来~</h2>; } else { titleJsx = <h2>请先登录~</h2>; } return <div>{titleJsx}</div>; } }
-
三元运算符
class App extends React.Component { constructor(props) { super(props); this.state = { isLogin: true, }; } render() { return ( <div> <h2>{this.state.isLogin ? "欢迎回来~" : "请先登录~"}</h2> <button onClick={(e) => this.loginBtnClick()}> {this.state.isLogin ? "退出" : "登录"} </button> </div> ); } loginBtnClick() { this.setState({ isLogin: !this.state.isLogin, }); } }
-
与运算符
{this.state.isLogin ? <h2>{this.state.username}</h2>: null} {this.state.isLogin && <h2>{this.state.username}</h2>}
-
v-show 效果 (自己手动实现)
render() { const { isLogin, username } = this.state; const nameDisplay = isLogin ? "block" : "none"; return ( <div> <h2 style={{ display: nameDisplay }}>{username}</h2> <button onClick={(e) => this.loginBtnClick()}> {isLogin ? "退出" : "登录"} </button> </div> ); }
JSX 列表渲染
-
列表渲染
class App extends React.Component { constructor(props) { super(props); this.state = { movies: [ "盗梦空间", "大话西游", "流浪地球", "少年派", "食神", "美人鱼", "海王" ], }; } render() { return ( <div> <h2>电影列表</h2> <ul> {this.state.movies.map((item) => { return <li>{item}</li>; })} </ul> </div> ); } } ReactDOM.render(<App />, document.getElementById("app"));
-
列表的 key,在列表展示的 jsx 中添加一个 key,用于虚拟 dom 更新;
案例:图片定时更换
import React from 'react';
import ReactDOM from 'react-dom';
import src1 from "./assets/1.jpg";
import src2 from "./assets/2.jpg";
import src3 from "./assets/3.jpg";
import "./index.css";
const container = document.getElementById('root');
const srcs = [src1, src2, src3]; // 保存图片路径的数组
let index = 0; // 显示的图片索引
let timer; // 计时器
function render() {
ReactDOM.render(<img src={srcs[index]} alt="" />, container);
}
/**
* 启动计时器,每隔一段时间,切换图片
*/
function start() {
stop();
timer = setInterval(() => {
index = (index + 1) % 3; //改变index
render();
}, 2000);
}
/**
* 停止计时器
*/
function stop() {
clearInterval(timer);
}
render();
start();
container.onmouseenter = function () {
stop();
}
container.onmouseleave = function () {
start();
}
React✍️ 入门
上一篇