复习:
1.React是一个用于构建用户界面的JS库;不涉及HTML/CSS;Facebook于2013开源,最新版本V18.2
2.使用React的两种方式:
①脚本引入式: react.js / react-dom.js / babel.js
②脚手架方式: npx create-react-app 项目名
3.JSX
JavaScript XML,是使用类似XML的语法创建虚拟DOM对象,本身是TypeScript语法一部分。
所有的JSX都会被Babel编译为: “use strict”; React.createElement(‘button/BuyCount’,…)
4.JSX中的数据绑定
内容绑定:<div>{表达式}</div>
属性绑定:<img src={表达式}/>
样式绑定:<div className={表达式} style={表达式}>
事件绑定:<button onClick={函数} onmouseenter={函数}>
双向数据绑定:
条件渲染:
列表渲染:
5.创建自定义组件的两种语法:
①函数式组件 —— 面向过程的编程思想©
function BuyCount( props ){ return (JSX) }
let vchild = <BuyCount c={5}/>
②类式组件 —— 面向对象的编程思想(Java)
class BuyCount extends React.Component{
render(){ log(this.props) return (JSX) }
}
let vchild = <BuyCount c={5}/>
6.React中事件处理函数中this指向丢失问题
//解决方案1:使用箭头函数
class BuyCount extends React.Component{
f1 = ( )=>{ log(this) }
render(){ return <button onClick={this.f1}></button>》 }
}
//解决方案2:使用箭头函数(可以传参)
class BuyCount extends React.Component{
f1( 形参 ){ log(this) }
render(){ return <button onClick={ ()=>this.f1(实参) }></button>》 }
}
//解决方案3:使用bind固定this的指向
class BuyCount extends React.Component{
f1 = (){ log(this) }
render(){ return <button onClick={ this.f1.bind( this ) }></button>》 }
}
//解决方案4:使用bind固定this的指向
class BuyCount extends React.Component{
constructor(){
super()
this.f1 = this.f1.bind( this )
}
f1(){ log(this) }
render(){ return <button onClick={ this.f1 }></button> }
}
一、React概述
英文官网:https://reactjs.org/
中文网站:https://zh-hans.reactjs.org/
用于构建用户界面的 JavaScript 库 —— 其核心功能中不涉及HTML和CSS
其特点有三个:
①声明式 / 数据响应式
②组件化编程
③一次学习,到处编写 —— React可以创建网站、H5、WebApp、原生App
项目中使用React的两种方式:
方式1:脚本引入式
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="babel.js"></script>
方式2:脚手架方式 在服务器端编译/渲染
使用方法:
①确保Node.js版本号达标
node -v 必须确保版本号>=14.0.0
②下载全局脚手架工具 —— create-react-app
npm i -g create-react-app
③运行脚手架工具,创建空白项目
create-react-app 项目名 //注意:React项目名不能包含大写字母
提示:上述②③两步可以使用一行命令代替: npx create-react-app 项目名
④进入空白项目
cd 项目名
⑤启动其中的开发服务器,浏览器会自动打开,显示默认首页
npm start
二、JSX
JSX:JavaScript XML,使用XML语法创建JS对象的技术。这种语法本身是TS的一部分,与React没有必然的联系(不过JSX确实是因为React而流行)。
使用JSX需要注意:
1.语法类似于XML(严格来说不是HTML)
2.有且只能有一个根元素,根元素下面可以有多个子/孙元素;如果根元素有多个,那就在外面再套一个父元素:<></>、<React.Fragment></React.Fragment>
3.标签/属性名严格区分大小写
4.标签必须闭合
5.因为浏览器不支持TS语法,也就不支持JSX语法;——必须使用一款TS编译器,把JSX语法转换为标准JS语句
6.注意:JSX的本质是运行于严格模式下的React.createElement( ),是JS不是HTML!!!React项目可以不用JSX,但是使用了JSX,创建虚拟DOM元素语法会变得简单
7.JSX语法中可以任意添加换行
8.JSX中的注释: {/* 注释内容 */}
9.JSX中元素可以使用的属性与HTML标签的属性大体类似,但是有些是不同的,例如:
HTML: class JSX:className
HTML: onclick / onkeyup JSX:onClick / onKeyUp
10.JSX中可以执行数据绑定,不论是哪种绑定(内容绑定/属性绑定/样式绑定/事件绑定…)语法都是一样的:{ 表达式 }
<div title={表达式} style={表达式} onClick={表达式}>{表达式}</div>
注意:React中“内容绑定”禁止绑定Object!!!“样式绑定”必须绑定一个样式Object!!!
三、React中自定义组件
React中提供了两种创建自定义组件的语法:
语法1: 函数式组件(Function Component)
function ZhRating( props ){
return <div>⭐⭐⭐⭐⭐</div>
}
let vchild = <ZhRating color="#f00" count={5}/>
注意:React要求,HTML元素对应的JSX标签必须用纯小写;自定义组件名必须采用大驼峰命名法(每个单词首字母必须大写)
语法2: 类式组件(Class Component)
class ZhRating extends React.Component {
render( ){
//使用this.props读取组件属性
return <div>⭐⭐⭐⭐⭐</div>
}
}
let vchild = <ZhRating color="#f00" count={5}/>
四、重点&难点&面试题:React中有时this指向会丢失/this会指向undefined,为什么?如何解决?
一般情况下,class中的方法访问当前类中的属性或其它方法,都要使用this——它指向当前类的当前实例;
有一种特例:JSX事件处理函数中的this,却指向undefined!!!
可能性1:this指向事件源对象? 否——虚拟DOM元素不能做事件源
可能性2:this指向class或实例? 否——类的实例方法赋值给另一个对象了
可能性3:this指向window? 否——Babel编译后的结果运行在严格模式
可能性4:this指向undefined? 是
解决方案:
方案1:使用箭头函数(不能传参)
f1 = ()=>{ log(this) }
<button onClick={ this.f1 }>
方案2:使用箭头函数(可以传参)
f1 = function( ){ log(this) }
<button onClick={ ( )=>this.f1( ) }>
方案3:使用bind固定this指向(多次bind会生成多个副本)
f1 = function( ){ log(this) }
<button onClick={ this.f1.bind( this ) }>
方案4:使用bind固定this指向(生成且仅生成一次副本)
constructor(){
super()
this.f1 = this.f1.bind( this )
}
f1 = function( ){ log(this) }
<button onClick={ this.f1 }>
五、组件的状态数据(State)
面试题:Vue.js和React的区别?尤雨溪答:
Vue.js中的数据响应式采用“Push-Based”机制——数据的改变会自动推送出去,告诉渲染系统重新渲染;
data: { return { count: 1 } }
this.count = 2 //此处的修改会执行两项任务:①修改模型变量 ②通知渲染系统重新渲染整个视图
React中的数据响应式采用“Pull-Based”机制,即组件的模型数据改变必须手工通知渲染系统。
state = { count: 1 }
this.state.count = 2 //此处的修改仅仅修改了变量的值,不会通知渲染系统
this.setState({ count: 2 }) //setState()会修改状态变量,并通知渲染系统
面试题:关于setState()方法
① setState()方法只会覆盖state中的同名属性,而不丢弃其它属性
② setState()方法是异步执行的,启动渲染系统很耗时,不会立即进行
③ setState()因为是异步的,所以它后面的语句会先执行,然后才去修改状态变量;如果想查看修改后的状态数据,必须放在第二个参数(状态修改并重新渲染后的回调) setState( state, callback )
六、JSX中的数据绑定 —— React中没有指令的概念
注意:JS中变量、常量、运算式、调用方法都是“表达式”;但是if、for、function不是“表达式”!!!{ }中只能放置表达式!!!
内容绑定:<div>{表达式}</div>
属性绑定:<img src={表达式}/>
样式绑定:<div className={表达式} style={表达式}>
事件绑定:<button onClick={函数} onmouseenter={函数}>
双向数据绑定:
条件渲染:
仿v-show:<any style={{display: this.state.show ? "block" : "none"}}/>
仿v-if:{ 判定表达式 && (JSX) } { 判定表达式 ? (JSX1) : (JSX2) }
列表渲染:
七、class组件的生命周期方法
第一阶段:挂载阶段
constructor():创建组件对象
render():渲染组件的内容
componentDidMount():组件完成挂载
第二阶段:更新阶段(state/props发生改变)
shouldComponentUpdate():此次数据改变应该重新渲染组件吗?
render():重新渲染组件内容
componentDidUpdate():组件完成更新
第三阶段:卸载阶段
componentWillUnmount():组件即将卸载
function Child( props ){
}
八、重点/难点/面试题:Hook(钩子)
自从React诞生,“函数式组件”功能一直有缺失:没有状态、没有生命周期方法 —— 没有父类,导致函数式组件只能编写一些简单的固定内容的组件。
V16.8开始,官方为“函数式组件”弥补了不足——增加了“Hook”的概念,同时由于自身的优势(天然没有this),导致越来越多的程序员开始偏向使用“函数式组件”。
当前的项目中,可以同时使用“类式组件”和“函数式组件”。
Hook的定义:
①钩子,用于勾住/增加一些扩展的功能
②每个Hook都是一个函数,都必须以 use 开头,例如:useState()、useRef()
③Hook函数只能在“函数式组件”中使用,不能用于“类式组件”
④Hook函数只能在“函数式组件”的最顶层调用,不能在内层调用
function Child(){
useState() //合法!最顶层
f1=function(){ useState() } //错误!
if(){ useState() } //错误!
for(){ useState() } //错误!
useState() //合法!最顶层
}
React官方提供的常用钩子之一 —— useState( ):为组件添加状态变量
import { useState } from 'react'
let [ 状态变量名, 修改状态变量的方法名 ] = useState( 状态变量初始值 )
React官方提供的常用钩子之二 —— useEffect( ):为组件添加生命周期方法
//生命周期方法1
useEffect( ()=>{
//生命周期方法1 = 组件挂载 + 任意数据更新
} )
//生命周期方法2
useEffect( ()=>{
//生命周期方法2 = 组件挂载 + 指定数据更新
} , [count, age, ...] ) //依赖列表有内容
//生命周期方法3
useEffect( ()=>{
//生命周期方法3 = 组件挂载
} , [ ] ) //依赖列表为空