一、React概述
英文官网: https://reactjs.org/
用于构建用户界面的 JavaScript 库 —— React只涉及到JS,不涉及HTML/CSS;
由Facebook在2013年5月开源,目前最新稳定版是v18.2。
特点: ①数据响应式/声明式 ②组件化开发 ③既可以开发Web应用,又可以开发原生App
项目中使用React的两种方式:
方式1:脚本引入式, <script src="react.js"></script>
方式2:脚手架方式, create-react-app
二、JSX
JavaScript XML:是使用XML的语法创建DOM元素的语法 —— 来自于TypeScript,后来被React框架所采用。但是,浏览器不能直接执行TS/JSX代码,必须使用Babel编译器才可以。即:如果React项目没有使用JSX语法,就可以不引入babel.js;反之就必须引入。
Babel.js:是一个第三方提供的JS编译器,可以把浏览器不认识的 JS 语法转换为浏览器可以认识的JS语法,例如: ES6=>ES5、 TypeScript=>JS
JSX语法要求:
①标签名称任意,大小写任意,但是必须闭合 —— 但是React要求自定义组件名必须使用“大驼峰命名法”;而HTML标签名必须纯小写;
②标签可以声明属性,值必须用引号括起来;
③一段JSX有且只有一个根元素;
④“JSX本质不是HTML而是JS”——JSX会被Babel编译器转化为纯JS:React.createElement(…);
⑤JSX编译出来的代码都运行在“严格模式下”——“use strict”;
⑥一段JSX严重推荐使用一个()括起来;
总结:JSX在React项目中用于“在严格模式下,创建虚拟DOM对象”
三、React中自定义组件
React中提供了两种创建自定义组件的语法:
语法1:function式组件 —— 体现的是“面向过程”的编程思想 (C、C++、PHP、JS)
function 组件名( ){
return (JSX)
}
let vchild = <组件名 />
语法2:class式组件 —— 体现的是“面向对象”的编程思想 (Java、C++、PHP、JS)
class 组件名 extends React.Component { //类 = 属性 + 方法
render( ){ //重写继承自父类的方法
return (JSX)
}
}
let vchild = <组件名 />
四、使用React的第二种方式:使用React命令行脚手架
创建一个Vue.js项目,可以使用脚手架工具:Vue-CLI
类似的,创建一个React项目,可以使用脚手架工具:Create-React-App
使用步骤:
1)必须安装Node.js,且版本号必须 >= V14.0.0
node -v
该版本的Node.js提供了三个工具: node.exe、npm.cmd、npx.exe
2)下载并执行React脚手架工具
npx create-react-app 项目名
3)进入项目目录,启动开发服务器
npm start
Node.js 安装包提供的工具:npx —— Node Package Executor,Node包执行器
功能: 如果当前计算机上有后面的命令,则直接执行;否则就立即下载该命令,再执行
使用方法: npx 包名 使用参数
NPM工具的下载仓库:
查看NPM当前默认的下载仓库:npm config get registry
修改NPM默认的下载仓库:npm config set registry 新的仓库地址
推荐的仓库地址,例如: https://registry.npmmirror.com/
JS对象字面量语法 | class中声明成员语法 |
---|---|
创建自定义组件的两种语法
面向过程 —— function式组件 —— rfc
function MfFooter(){
return (JSX)
}
let vnode = <MfFooter />
面向对象 —— class式组件 —— rcc
class MfFooter extends React.Component{
f1=function(){ clg(this) }
render(){
return (<button onClick={this.f1}></button>)
}
}
let vnode = <MfFooter/>
五、重点面试题:React中的事件处理
为什么React 事件处理函数 中的this指向undefined???而普通函数调用中没有此现象。
①JS中this指向调用当前方法的对象
②React中“事件源对象”不存在,JSX构建的虚拟DOM
③JSX编译得到的代码是一段运行于“严格模式”的JS,其中的函数里this指向undefined
如果函数发生了调用,That指向调用者
解决方案:
- 把事件处理函数声明为箭头函数
f1 = ()=>{ clg(this) }
- 在事件处理函数赋值时使用箭头函数
onClick={ ()=>{this.f1(实参)} }
可以给事件处理函数传参 - 在指定事件处理函数时使用“固定了this指向的f1副本”—— 多次渲染后会生成多个副本
onClick={ this.f1.bind(this) }
- 在指定事件处理函数时使用“固定了this指向的f1副本”—— 此bind操作只执行一次
见下方代码
constructor(){
super( )
this.f1 = this.f1.bind( this )
//构造方法中的语句,自动执行且仅执行一次
}
onClick={ this.f1 }
(复习JS知识点:this的指向问题)
(复习:JS高级中的bind函数 —— 返回当前函数的一个副本,
唯一的区别是其中的this固定指向参数对象)
let f1 = function(){
console.log('------------------')
console.log( this )
console.log('------------------')
}
f1( )
let emp = {ename:'dangdang', age: 20}
let car = {brand:'奔驰', speed:200}
//希望调用f1时,输出的this指向emp
let f2 = f1.bind( emp )
f2( )
//希望调用f1时,输出的this指向car
let f3 = f1.bind( car )
f3( )
(复习JS知识点:class中执行且仅执行一次的方法 —— 构造方法)
class Parent{
constructor(){ ... }
}
class Emp extends Parent{
constructor( ){
super( ) //子类体内调用父类构造方法,在子类对象体内创建一个父类对象的实例——继承的本质
console.log('一个Emp实例被创建出来了')
}
}
let e1 = new Emp( ) //调用构造方法,创建一个对象的实例
let e2 = new Emp( ) //调用构造方法,创建一个对象的实例
六、重要面试题:Vue.js和React两个框架的区别?
尤雨溪:Vue.js中的数据响应式是“Push Based(基于推送的)”;而React中的是“Pull Based(基于拉取的)”。
Vue.js —— Push Based
data(){
return { age: 10 }
}
f1(){
this.age = 11
//①修改模型 ②通知渲染系统
}
小程序 —— Pull Based
data: {
age : 10
}
f1(){
this.age = 11 //无效语句
this.setData({age:11}) //正确
}
React —— Pull Based
state = { //就相当于data
age: 10
}
f1(){
this.state.age = 11 //无效语句
this.setState({age: 11}) //正确
}
提示:React中的setState()方法是异步的,想查看修改后的值,必须使用其第二个参数:回调函数;且此方法会把修改后的状态变量和没修改的合并起来,而不是覆盖掉。
七、React中的数据绑定
注意:React中没有“指令”的概念,即无v-on、v-if、v-for、v-model…
- 内容绑定:
<any>{表达式}</any>
- 属性绑定:
<any 属性名={表达式}/>
- 样式绑定:
<any style={ {color:'red'} }/>
- 事件绑定:
<any onClick={表达式}/>
- 双向数据绑定:
<input value={表达式} onChange={e=>setXxx(e.target.value)}/>
(重要面试题:React中如何获取输入框中的内容) - 条件渲染:运用短路逻辑
<any>{ 判定表达式 && (JSX) }</any>
- 列表渲染:
<any>{ arr.map( (e,i)=>(JSX) ) }</any>
重要面试题:React中如何获取输入框中的内容?
方案1:使用“受控组件”—— 监控用户的每一次输入
Model:let [kw, setKw] = useState("戴尔")
View: <input value={kw} onChange={ e=>setKw(e.target.value) }/>
//方向1的绑定:Model=>View
//方向2的绑定:View=>Model
方案2:使用“非受控组件”—— 无需监控用户的每次输入动作,只需要在最后提交时读取其中的输入
<input ref={ }/>
<button onClick={通过ref查找输入值}>提交</button>
八、class组件的生命周期方法
React中class是生命周期方法分为三个阶段:(比vue少一个创建阶段)
挂载阶段:
constructor( ):构造方法
render( ):渲染内容
componentDidMount( ):组件完成挂载,等价于之前的mounted、onLoad
更新阶段:
shouldComponentUpdate( ):此次修改需要让组件更新吗?需要返回true或false 提供两个参数监听数据变化
render( ):渲染内容
componentDidUpdate( ):组件完成更新
卸载阶段:
componentWillUnmount( ):组件即将卸载,等价于之前的destroyed、onUnload
九、重要知识点:React Hooks
早期的React提供了function组件和class组件两种方式;但是function组件功能太弱了:
- 没有state和setState() —— 不继承React.Component类
- 没有任何生命周期方法 —— 不继承React.Component类
React V16.8,官方为function式组件提供了新功能:Hooks,弥补了之前的不足,再加上本身天然优势(天然就没有this),导致function式组件“异常强大”。官方提供了15个钩子函数
Hook:钩子,本身就是一个普通的函数,用于为function式组件“钩住”更多的功能——例如:状态、生命周期方法…所有的钩子函数有如下特性:
- Hook函数只能在function组件中使用,不能用于class组件;
- 所有的Hook函数都必须以use-开头,例如:useState( )、useEffect( )、…;
- Hook函数只能在组件的最外层使用,不能在内层
function XzFooter(){
useState() 合法
if(){ useState() 非法 }
for(*){ useState() 非法 }
function(){ useState() 非法}
}
十、React官方提供的常用Hook
- useState( )状态钩子 —— 为函数式组件添加state和setState( )方法
import {useState} from 'react'
let [ 变量名, 方法名 ] = useState( 初始值 ) //[ 初始值, func ]
- useEffect( )副作用钩子—— 为函数式组件添加“副作用(即生命周期方法)”
import {useEffect} from 'react'
function式组件生命周期方法1:
useEffect( ( )=>{
//生命周期方法1 = 组件完成挂载 + 任意状态数据发生改变
} )
function式组件生命周期方法2:
useEffect( ( )=>{
//生命周期方法2 = 组件完成挂载 + 指定的状态变量发生改变
}, [变量1, 变量2, ...] ) //有依赖列表
function式组件生命周期方法3:
useEffect( ( )=>{
//生命周期方法3 = 组件完成挂载 //等价于之前的mounted、onLoad
}, [ ] ) //空依赖
function式组件生命周期方法4:
useEffect( ( )=>{
return ( )=>{
//生命周期方法4 = 任意的状态数据发生改变 + 组件卸载
}
} )
function式组件生命周期方法5:
useEffect( ( )=>{
return ( )=>{
//生命周期方法5 = 依赖的状态数据发生改变 + 组件即将卸载
}
}, [变量1, 变量2,....] ) //有依赖列表
function式组件生命周期方法6:
useEffect( ()=>{
return ()=>{
//生命周期方法6 = 组件即将卸载
}
}, [ ] ) //空依赖
- useRef( )元素引用钩子