React
JSX的书写规范
JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div元素(或者Fragment);
为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
JSX中的标签可以是单标签,也可以是双标签;
注意:如果是单标签,必须以/>结尾;
JSX的使用
JSX嵌入变量作为子元素
情况一:当变量是Number、String、Array类型时,可以直接显示
情况二:当变量是null、undefined、Boolean类型时,内容为空;
如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
情况三:Object对象类型不能作为子元素(not valid as a React child)
JSX嵌入表达式
运算表达式
三元运算符
执行一个函数
<body>
<div id="root"></div>
<script src="../lib/react.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1.定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
counter: 100,
message: "Hello World",
names: ["abc", "cba", "nba"],
aaa: undefined,
bbb: null,
ccc: true,
friend: { name: "kobe" },
firstName: "kobe",
lastName: "bryant",
age: 20,
movies: ["流浪地球", "星际穿越", "独行月球"]
}
}
render() {
// 1.插入标识符
const { message, names, counter } = this.state
const { aaa, bbb, ccc } = this.state
const { friend } = this.state
// 2.对内容进行运算后显示(插入表示)
const { firstName, lastName } = this.state
const fullName = firstName + " " + lastName
const { age } = this.state
const ageText = age >= 18 ? "成年人": "未成年人"
const liEls = this.state.movies.map(movie => <li>{movie}</li>)
// 3.返回jsx的内容
return (
<div>
{/* 1.Number/String/Array直接显示出来 */}
<h2>{counter}</h2>
<h2>{message}</h2>
<h2>{names}</h2>
{/* 2.undefined/null/Boolean */}
<h2>{String(aaa)}</h2>
<h2>{bbb + ""}</h2>
<h2>{ccc.toString()}</h2>
{/* 3.Object类型不能作为子元素进行显示*/}
<h2>{friend.name}</h2>
<h2>{Object.keys(friend)[0]}</h2>
{/* 4.可以插入对应的表达式*/}
<h2>{10 + 20}</h2>
<h2>{firstName + " " + lastName}</h2>
<h2>{fullName}</h2>
{/* 5.可以插入三元运算符*/}
<h2>{ageText}</h2>
<h2>{age >= 18 ? "成年人": "未成年人"}</h2>
{/* 6.可以调用方法获取结果*/}
<ul>{liEls}</ul>
<ul>{this.state.movies.map(movie => <li>{movie}</li>)}</ul>
<ul>{this.getMovieEls()}</ul>
</div>
)
}
getMovieEls() {
const liEls = this.state.movies.map(movie => <li>{movie}</li>)
return liEls
}
}
// 2.创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
</script>
</body>
jsx绑定属性
比如元素都会有title属性
比如img元素会有src属性
比如a元素会有href属性
比如元素可能需要绑定class
比如原生使用内联样式style
<body>
<div id="root"></div>
<script src="../lib/react.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1.定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
title: "哈哈哈",
imgURL: "https://ts1.cn.mm.bing.net/th/id/R-C.95bc299c3f1f0e69b9eb1d0772b14a98?rik=W5QLhXiERW4nLQ&riu=http%3a%2f%2f20178405.s21i.faiusr.com%2f2%2fABUIABACGAAgoeLO-wUo4I3o2gEw8Qs4uAg.jpg&ehk=N7Bxe9nqM08w4evC2kK6yyC%2bxIWTjdd6HgXsQYPbMj0%3d&risl=&pid=ImgRaw&r=0",
href: "https://www.baidu.com",
isActive: true,
objStyle: {color: "red", fontSize: "30px"}
}
}
render() {
const { title, imgURL, href, isActive, objStyle } = this.state
// 需求: isActive: true -> active
// 1.class绑定的写法一: 字符串的拼接
const className = `abc cba ${isActive ? 'active': ''}`
// 2.class绑定的写法二: 将所有的class放到数组中
const classList = ["abc", "cba"]
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>
)
}
}
// 2.创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
</script>
</body>
React事件绑定
如果原生DOM原生有一个监听事件,我们可以如何操作呢?
方式一:获取DOM原生,添加监听事件;
方式二:在HTML原生中,直接绑定onclick;
在React中是如何操作呢?我们来实现一下React中的事件监听,这里主要有两点不同
React 事件的命名采用小驼峰式(camelCase),而不是纯小写;
我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行;
this的绑定问题
在事件执行后,我们可能需要获取当前类的对象中相关的属性,这个时候需要用到this
如果我们这里直接打印this,也会发现它是一个undefined
为什么是undefined呢?
原因是btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;
而它内部调用时,并不知道要如何绑定正确的this;
如何解决this的问题呢?
方案一:bind给btnClick显示绑定this
方案二:使用 ES6 class fields 语法
方案三:事件监听时传入箭头函数(个人推荐)
<body>
<div id="root"></div>
<script src="../lib/react.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
/*
this的四种绑定规则:
1.默认绑定 独立执行 foo()
2.隐式绑定 被一个对象执行 obj.foo() -> obj
3.显式绑定: call/apply/bind foo.call("aaa") -> String("aaa")
4.new绑定: new Foo() -> 创建一个新对象, 并且赋值给this
*/
// 1.定义App根组件
class App extends React.Component {
// class fields
name = "App"
constructor() {
super()
this.state = {
message: "Hello World",
counter: 100
}
this.btn1Click = this.btn1Click.bind(this)
}
btn1Click() {
console.log("btn1Click", this);
this.setState({ counter: this.state.counter + 1 })
}
btn2Click = () => {
console.log("btn2Click", this)
this.setState({ counter: 1000 })
}
btn3Click() {
console.log("btn3Click", this);
this.setState({ counter: 9999 })
}
render() {
const { message } = this.state
return (
<div>
{/* 1.this绑定方式一: bind绑定 */}
<button onClick={this.btn1Click}>按钮1</button>
{/* 2.this绑定方式二: ES6 class fields */}
<button onClick={this.btn2Click}>按钮2</button>
{/* 3.this绑定方式三: 直接传入一个箭头函数(重要) */}
<button onClick={() => console.log("btn3Click")}>按钮3</button>
<button onClick={() => this.btn3Click()}>按钮3</button>
<h2>当前计数: {this.state.counter}</h2>
</div>
)
}
}
// 2.创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
</script>
</body>
事件参数传递
在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数
情况一:获取event对象
很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)
那么默认情况下,event对象有被直接传入,函数就可以获取到event对象;
情况二:获取更多参数
有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数
<body>
<div id="root"></div>
<script src="../lib/react.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1.定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World"
}
}
btnClick(event, name, age) {
console.log("btnClick:", event, this)
console.log("name, age:", name, age)
}
render() {
const { message } = this.state
return (
<div>
{/* 1.event参数的传递 */}
<button onClick={this.btnClick.bind(this)}>按钮1</button>
<button onClick={(event) => this.btnClick(event)}>按钮2</button>
{/* 2.额外的参数传递 */}
<button onClick={this.btnClick.bind(this, "kobe", 30)}>按钮3(不推荐)</button>
<button onClick={(event) => this.btnClick(event, "james", 18)}>按钮4</button>
</div>
)
}
}
// 2.创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
</script>
</body>
React条件渲染
某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容:
在vue中,我们会通过指令来控制:比如v-if、v-show;
在React中,所有的条件判断都和普通的JavaScript代码一致;
常见的条件渲染的方式有哪些呢?
方式一:条件判断语句
适合逻辑较多的情况
方式二:三元运算符
适合逻辑比较简单
方式三:与运算符&&
适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染;
v-show的效果
主要是控制display属性是否为none
<body>
<div id="root"></div>
<script src="../lib/react.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1.定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World",
isReady: false,
friend: undefined
}
}
render() {
const { isReady, friend } = this.state
// 1.条件判断方式一: 使用if进行条件判断
let showElement = null
if (isReady) {
showElement = <h2>准备开始比赛吧</h2>
} else {
showElement = <h1>请提前做好准备!</h1>
}
return (
<div>
{/* 1.方式一: 根据条件给变量赋值不同的内容 */}
<div>{showElement}</div>
{/* 2.方式二: 三元运算符 */}
<div>{ isReady ? <button>开始战斗!</button>: <h3>赶紧准备</h3> }</div>
{/* 3.方式三: &&逻辑与运算 */}
{/* 场景: 当某一个值, 有可能为undefined时, 使用&&进行条件判断 */}
<div>{ friend && <div>{friend.name + " " + friend.desc}</div> }</div>
</div>
)
}
}
// 2.创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
</script>
</body>
React列表渲染
真实开发中我们会从服务器请求到大量的数据,数据会以列表的形式存储:
比如歌曲、歌手、排行榜列表的数据;
比如商品、购物车、评论列表的数据;
比如好友消息、动态、联系人列表的数据;
在React中并没有像Vue模块语法中的v-for指令,而且需要我们通过JavaScript代码的方式组织数据,转成JSX:
很多从Vue转型到React的同学非常不习惯,认为Vue的方式更加的简洁明了;
但是React中的JSX正是因为和JavaScript无缝的衔接,让它可以更加的灵活;
另外我经常会提到React是真正可以提高我们编写代码能力的一种方式;
如何展示列表呢?
在React中,展示列表最多的方式就是使用数组的map高阶函数;
很多时候我们在展示一个数组中的数据之前,需要先对它进行一些处理:
比如过滤掉一些内容:filter函数
比如截取数组中的一部分内容:slice函数
<body>
<div id="root"></div>
<script src="../lib/react.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1.定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
students: [
{ id: 111, name: "why", score: 199 },
{ id: 112, name: "kobe", score: 98 },
{ id: 113, name: "james", score: 199 },
{ id: 114, name: "curry", score: 188 },
]
}
}
render() {
const { students } = this.state
// 分数大于100的学生进行展示
const filterStudents = students.filter(item => {
return item.score > 100
})
// 分数大于100, 只展示两个人的信息
// slice(start, end): [start, end)
const sliceStudents = filterStudents.slice(0, 2)
return (
<div>
<h2>学生列表数据</h2>
<div className="list">
{
students.filter(item => item.score > 100).slice(0, 2).map(item => {
return (
<div className="item" key={item.id}>
<h2>学号: {item.id}</h2>
<h3>姓名: {item.name}</h3>
<h1>分数: {item.score}</h1>
</div>
)
})
}
</div>
</div>
)
}
}
// 2.创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
</script>
</body>
列表中的key
我们会发现在前面的代码中只要展示列表都会报一个警告:
这个警告是告诉我们需要在列表展示的jsx中添加一个key。
key主要的作用是为了提高diff算法时的效率;
JSX的本质
实际上,jsx 仅仅只是 React.createElement(component, props, …children) 函数的语法糖。
所有的jsx最终都会被转换成React.createElement的函数调用。
createElement需要传递三个参数:
参数一:type
当前ReactElement的类型;
如果是标签元素,那么就使用字符串表示 “div”;
如果是组件元素,那么就直接使用组件的名称;
参数二:config
所有jsx中的属性都在config中以对象的属性和值的形式存储;
比如传入className作为元素的class;
参数三:children
存放在标签中的内容,以children数组的方式进行存储;
当然,如果是多个元素呢?React内部有对它们进行处理,处理的源码在下方