官方文档 react.docschina.org/docs/gettin…
一、命令行 cli
- 安装 :
yarn global add create-react-app
- 创建文件 :
create-react-app 文件名
- 开始开发 :
yarn start
二、虚拟Dom
虚拟Dom:React 元素的返回值可以代表一个div,但是它并不是真正的dom 对象
所以一般把react返回的element称为 虚拟 Dom
函数组件:返回element的函数,可以代表一个div,函数可以多次执行,每次都会得到最新的虚拟div,
React会对比两个虚拟div,找出不同然后局部更新视图,这种方法依赖于 ****Dom Diff
****算法
执行顺序:App()
组件 -----> 执行 -----> 虚拟Dom -----> 更新Dom -----> 渲染页面 ----->useEffect
三、JSX
- 语法:可以直接在 js 中书写 HTML 语法,如果需要插入 js 语法 需要写在
{{}}
里
const App = () =>(<div
{{}}
<button onclick={()=>{n+1;render();}}>加1</button>
</div>);
ReactDom.render(<App/> , document.getElementById('root'));
- 注意 : 1. class 属性应该写成
className
2. 变量和对象要使用 { } 包起来 ,例: {变量}
{{对象}}
****3. return
后的内容要使用 () 包起来
4. 条件判断和循环控制,使用 原生js 语法实现,for循环需要有 key
四,react组件
- 书写:react元素首字母小写, 组件名 首字母大写
- 分类:react有两种组件,函数组件 ****和 类组件
- 元素:react元素 是构成应用的最小单位,不可改变
function Welcome(props){
const [n,setN]=useState(0) //设置 state 变量n 并且初始值为0。
return <h1>Hello,{props.name}</h1>
}
<Welcome name ="frank" / > //使用方法
class Welcome extends React.Component{
constructor(){
super()
this.state={
n:0
}
}
render(){
return <h1>Hello,{this.props.name}</h1>
}
}
<Welcome name ="frank" /> //使用方法
(1)可控组件与不可控组件
- 可控组件:表单数据由React组件管理,react中推荐使用可控组件
- 不可控组件:表单数据由Dom节点处理
五、state
- 类组件使用
this.state
来定义,使用setState
来更新值(异步更新) - 当组件渲染完毕后,修改state的值,不会触发重新渲染
- 函数组件使用Hooks,state在Hooks中介绍
class Welcome extends React.Component{
constructor(){
super()
this.state={n:0}
}
}
//this.state.n 就是n的值
this.setState({this.state.n+1})
六、props
- 定义:需要外部传递的数据,只能由父组件像子组件传递,子组件不能反向修改。
- 使用方法:函数组件通过参数传递
(props)=>{函数体}
,类组件直接调用this.props
function App(){
const a=10
return(
<div className="App">
这里是父组件
<Son messageForSon={a}> // 在父组件中向子组件传递一个变量 a
</div>
)
}
//子组件使用方式(类组件)
this.props.messageForSon
//子组件使用方式(函数组件)
props.messageForSon
七、Hooks
Hook就是JavaScript函数。但是它有两个额外的规则:
- 只能在函数最外层调用,不可以在循环,条件判断或者子函数中调用。
- 只能在React函数中调用Hook(或者自定义Hook中调用),不要在其他函数中调用
1. useState
- 采用析构赋值的方式,使得useState函数中包括一个
n
和一个setN()
,参数为n
的初始值表示数据当前的状态。setN()
是一个函数,封装对 n 的操作,但是不可以部分更新n中的数据
。 setN()
并不会直接修改n
的值,修改的是n的副本
,优先使用函数,引用类型采用浅比较(只比较地址),可以有多个state。对于数组对象等,修改时需要创建一个新的对象,setN
是 异步 的并不会马上得到修改后的值setN()
会把对于同一个数据多次修改合并为一次,只修改最后一次。对于不同的数据也会合并起来执行一次,页面渲染
function App(){
const [n,setN]=useState(0)
//初始化n的值为0 ,steN()用来操作n的值
}
2. useReducer
- 步骤:先声明一个初始值,再将所有操作都封装成一个函数,然后将它们传给 useReducer
- 优点:对操作进行封装,可以简化HTML代码,直接调用封装好的 Api 就可以
- 作用:
reducer
和Context
一起用可以用来代替Redux, 同样也是useState的升级版。
const initial={...} // 初始值
const reducer=(state,action)=>{
...
}// 对初始值的所有操作
//函数组件
function App(){
const[state,dispatch]=useReducer(roducer,initial)
//传给useReducer后,会获得一个读接口(state)和一个写接口(dispatch)
//useReducer(roducer,initial) 注意参数顺序,操作在前,值在后
}
使用useReducer代替redux
- 将初始数据集中在store对象中
- 将所有对数据的操作集中在reducer中
- 创建一个context,context的初始值
- 在组件内创建reducer的读写操作,并且将读写操作传递给context使其变为全局变量
- 组件内读写直接调用context传递的数据即可
3. useContext
- 使用
createContext()
创建一个Context
的上下文区域,并且使用.Provide
声明作用域的范围 - 将初始值传递给
value
属性,在作用域范围内的所有组件就都可以使用这个state
了 - 注意:useContext的值不是响应式的,一个模块中修改并不会影响另一个模块
- 子组件使用useContext中的 value,
const {n,setN}=useContext(c)
const C=createContext(initial) //这个c就是一个全局变量
function App(){
const [n,setN]=useState(0)
return(
<c.Provide value={{n,setN}}>
//声明作用域,该作用域内的所有组将都可以使用n和setN
</c.Provide>
)
}
//子组件的使用方法,表示子组件使用的n是来自于useContext
const {n,setN}=useContext(c)
4. useEffect
- 副作用,对环境的改变即为副作用,比如修改document.title,用途主要是在hooks中实现class组件的生命周期的功能
- useEffect可以大概理解为afterRender,默认为每次
render()
后都会运行,是一个异步的操作
- 语法:
useEffect(回调函数,array)
如果存在多个useEffect,会按出现顺序执行
//1.单纯的useEffect 会在组件渲染后执行,render()后只执行一次,第二参数为空数组
useEffect (()=>{function('组件内任意state变化就执行该函数'),[]})
//2.state任何值发生变化都执行,监听所有值,第二参数省略
useEffect (()=>{function('组件内任意state变化就执行该函数')})
//3.监控指定的值,第二参数数组为这个值
useEffect (()=>{function('组件内变量n变化就执行该函数'),[n]})
- 清除副作用: 组件卸载时,需要副作用,比如计时器等异步的操作,可能会造成内存泄露
使用Effect函数的return返回一个清除函数(会在组件被销毁时执行)
清理副作用是一个良好的习惯。
//4.return返回的函数会在组件销毁时执行
useEffect (()=>{return function('组件内任意state变化就执行该函数')})
5.useLayoutEffect
- useLayoutEffect和useEffect功能类似,只有执行时机不同
- useLayoutEffect会在页面更新之前执行,useEffect会在页面更新之后执行
- useEffect优先使用, 优先渲染页面,满足用户体验
6. useMemo
React.memo函数 :
函数组件的state默认是一个数组,每次函数调用state更新时,数组地址会发生变化,props同理。引用类型地址变化会使react重新渲染所有依赖此项的子组件,有些子组件会有多余的render()
,为了避免这种情况要使用memo重构子组件。使其只在props更新时执行代码,但是memo不支持监听具体的值,如果props是数组等引用类型,无法满足需求
const Child = React.memo((props)=>{}) //使用memo函数包裹的组件,仅在props值发生变化时重新渲染
useMemo主要用来做 性能优化,相比于memo函数,支持监听具体的依赖项。
useMemo()
支持监听值,第一个参数为()=>{value}
,第二个参数是依赖[值]
// 第一个参数为回调函数,第二个参数为监控的值,只有m更新时才执行这个回调函数
const onClickChild=useMemo(()=>{
return ()=>{fn回调函数}
},[m])
7. useCallback
useCallback
和useMemo
作用相同,useCallback是个语法糖
useCallback(x=>log(x),[m]) //可以直接写回调函数
useMemo(()=>x=>log(x),[m])
//这两种写法等价,只是省略了()=>
8. useRef
- useRef中声明的值发生变化,不会引发组件更新,
useRef()
数据更新是同步的 current
对象 ****每次获取的值都是相同的(地址相同的同一个对象),只要修改就会拿得到最新的值- useRef声明的值可以在其他函数内部使用,该值是稳定的可变的,不会随着组件更新而变化
- 用途:1、持久化缓存 2、获取真实DOM
const demo=useRef(0)//声明
demo.current //调用
- 获取真实Dom元素,(由于react 元素是虚拟Dom),可以通过ref属性将useRef对象绑定到Dom 上来获取
function App(){
const inputRef=useRef(null)
const onFocus=()=>{
inputRef.current.focus()
}
return(
<div>
<input type="text" ref={inputRef} /> /通过ref属性绑定
<button onClick={onFocus}>聚焦</button>
</div>
)
}
9. forwardRef
- 作用:函数组件只能接收props参数,无法接收ref,需要使用
forwardRef()
来包装一下
const Button=(
(props,ref)=>{...}
)
const Button2=forwardRef(Button)//有了这句话,Button就可以接收ref参数,不然就会报错
//简写
const Button=forwardRef(
(props,ref)=>{ ...}
)
10 .useRef 和 useState对比
useState
受组件更新影响,组件更新后不能第一时间获取新值(异步)useRef
相对独立不受组件更新影响,修改后可以第一时间获取新值,但是更新状态需要回调函数异步实现
八、自定义Hook
- 将数据以及操作都封装在一个函数中,可以使用其他现有Hook,然后这样具有自定义功能的Hook 就是自定义Hook,需要暴露一个读接口和一个写接口,负责一点还可以包括增,删,改,查
- 自定义Hook 加上Context,完全可以替代 Redux
- useState 不可以在if李使用,但是可以在函数中运行,只要这个函数在函数组件里运行就行
九、Hook想法
useState
基础内部数据,可以通过参数传递给其他组件使用(Props)。会随着组件更新变化useRef
基础数据,.current
可变,但是不会引起组件更新useEffect
生命周期useContext ``.Provide
作用域,优化Props
传值,createContent
创建对象useMemo
性能优化,防止不必要的state
更新useCallback
useMemo的语法糖,作用类似useReducer
数据管理,替代 redux