复习:
1.使用React的两种方式:
①脚本引入式:
<script src="./js/react.js"></script>
<script src="./js/react-dom.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
//1.创建虚拟DOM对象 React.createElement()
//2.把真实DOM容器转换为虚拟DOM容器 ReactDOM.createRoot()
//3.在虚拟DOM容器中渲染虚拟DOM元素 vparent.render(vchild)
</script>
②脚手架方式:
Node.js>=V14.0、 npx create-react-app 项目名、 cd 项目名 && npm start
public/index.html > div#root
src/index.js > ReactDOM.createRoot().render(<App/>)
src/App.js > rcc或rfc
2.JSX中的数据绑定
内容绑定:<div>{表达式}</div>
属性绑定:<img src={表达式}/>
样式绑定:<div className={表达式} style={样式对象}></div>
事件绑定:<button onClick={函数} onClick={()=>函数(参数)}></button>
双向数据绑定:
条件渲染:
<div style={{display:表达式?'block':'none'}}></div>
{ 判定表达式 && (JSX)}
{ 判定表达式 ? (JSX1): (JSX2) }
列表渲染:
5.创建自定义组件的两种语法:
①函数式组件
function Rating( props ){
return JSX
}
②类式组件
class Rating extends React.Component{
//挂载阶段:constructor()、render()、componentDidMount()
//更新阶段:shouldComponentUpdate()、render()、componentDidUpdate()
//卸载阶段:componentWillUnmount()
state = { num: 18, age: 20 }
add = ()=>{ this.setState({num: 19},()=>{}) }
render(){ return JSX }
}
6.React Hooks
React V16.8官方为函数式组件新增的特性:钩子
①本质就是函数,以use开头 ②只能在函数式组件中调用 ③只能在最顶层调用
官方目前提供了15个钩子函数,可以为函数式组件扩展15种功能
常用钩子之一: let [num, setNum] = useState(18) setNum(19) 异步但无第二个参数
常用钩子之二: useEffect( 生命周期方法 )
一、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={函数}>
双向数据绑定:React中没有双向数据绑定的概念,但可以使用两个方向上的绑定模拟实现双向数据绑定
方向1:Model=>View
<input value={uname}/>
方向2:View=>Model
<input onChange={e=>setUname(e.target.value)}/>
条件渲染:
仿v-show:<any style={{display: this.state.show ? "block" : "none"}}/>
仿v-if:{ 判定表达式 && (JSX) } { 判定表达式 ? (JSX1) : (JSX2) }
列表渲染:
let empList = ['亮亮', '小明', '小鑫']
<ul>{ empList.map( (e, i)=>JSX ) }</ul>
面试题:React中获取表单元素当前的输入内容,有两种方式?
方式1:受控组件 —— 监控用户的每一次输入是否合规
React要求每个表单元素都只能有“单一数据源”,要么是“用户输入”,要么是“模型变量”,不能二者同时生效:
<input /> —— 此时数据源就是“用户输入”
<input value="模型变量" onChange={e=>setXxx(e.target.value)}/> —— 此时数据源就是“模型变量”—— 此时输入框就不能再接收用户输入了,这种状态称为“输入框被模型变量控制住了”,称为“受控组件”
方式2:非受控组件 —— 不必监视用户的每次输入,最后提交时能够读取到值即可
非受控组件不能指定value属性,只能在最后提及时通过ref找到该元素,获取其中最新输入
let resumeRef = useRef()
<input ref={resumeRef} defaultValue="初始值"/>
resumeRef.current.value 就是当前最新的输入的内容
七、class组件的生命周期方法
第一阶段:挂载阶段
constructor():创建组件对象
render():渲染组件的内容
componentDidMount():组件完成挂载
第二阶段:更新阶段(state/props发生改变)
shouldComponentUpdate():此次数据改变应该重新渲染组件吗?
render():重新渲染组件内容
componentDidUpdate():组件完成更新
第三阶段:卸载阶段
componentWillUnmount():组件即将卸载
八、重点/难点/面试题: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 —— 用于监视整个组件数据的改变(从无到有,从1到2...)
useEffect( ( )=>{
//生命周期方法1 = 组件挂载 + 任意数据更新
} )
//生命周期方法2 —— 用于监视组件特定数据的改变(从无到有,从1到2...)
useEffect( ( )=>{
//生命周期方法2 = 组件挂载 + 指定数据更新
} , [count, age, ...] ) //依赖列表有内容
//生命周期方法3 —— 用于监视组件挂载
useEffect( ( )=>{
//生命周期方法3 = 组件挂载
} , [ ] ) //依赖列表为空
//生命周期方法4 —— 用于监视组件数据的更新(获取旧值)和销毁——可用于执行旧数据的清理操作
useEffect( ( )=>{
return ( )=>{
//生命周期方法4 = 任意数据更新(旧值) + 组件卸载
}
} )
//生命周期方法5 —— 用于监视特定数据的更新(获取旧值)和销毁——可用于执行旧数据的清理操作
useEffect( ( )=>{
return ( )=>{
//生命周期方法5 = 指定数据更新(旧值) + 组件卸载
}
} , [count, age, ...]) //依赖列表不为空
//生命周期方法6 —— 用于监视组件的销毁——可用于执行组件的清理操作
useEffect( ( )=>{
return ( )=>{
//生命周期方法6 = 组件卸载
}
} , [ ]) //依赖列表为空
框架 组件库
Vue.js PC端:ElementUI
移动端:MintUI、VantUI
uni-app 偏移动端:uni-ui
React PC端:Ant Design
移动端:VantUI、ReactNative
九、React Native组件库
React是一个MVVM框架;
ReactNative是一个基于React框架的移动端UI组件库——是Facebook官方提供的;
官网:https://reactnative.dev/
中文网:https://reactnative.cn/
Vue.js+Vant项目运行原理:
.vue => Webpack编译打包 => .html/.css/.js => 运行于客户端浏览器
React+RN项目运行原理:
.js/.jsx => Webpack编译 => .java或.oc => JAVA或OC编译 => .apk或.ipa安装包 => 安装到手机中运行为原生App(完全脱离浏览器)
(可选操作)在自己电脑上搭建RN编译环境,创建出一个空白的RN项目——3GB+
①安装基础软件
②安装AndroidStudio
③运行AndroidStudio,使用它下载AndroidSDK
C:\Users\tedu\AppData\Local\Android\Sdk
④修改Windows系统环境变量,指定AndroidSDK安装路径
⑤创建RN空白项目,下载必需的第三方模块
⑥编译RN空白项目,得到JAVA代码再打包得到.apk安装包
(必选操作)运行空白RN项目,在自己的电脑和手机中运行该项目——250MB+
①PC端运行RN项目自带的开发服务器
cd MyRnApp && npm start
提示:开发服务器只会占用8081端口
②启动安卓模拟器,安装RN项目编译得到的App
APK路径:MyRnApp\android\app\build\outputs\apk\debug
③启动手机中的App,在App设置中配置开发服务器的IP和端口号
查看Windows服务器当前的真正IP地址: ipconfig
例如,我当前电脑的IP地址是 192.168.0.105
点击手机“菜单键”>Settings>DebugServerHost&PortForDevice>输入:
192.168.0.105:8081
④重启手机中的App,会自动连接开发服务器,获取最新的内容
如果提示:App已停止、App无法启动、App启动后白屏.....请重启Windows再重试上述几步