1 React生命周期钩子函数
组件生命周期,指的是组件从实例化到挂载到最终卸载的过程.
1.挂载阶段 constructor() 数据的初始化
接收props和context,当想在函数内使用这两个参数需要在super传入参数,当使用constructor时必须使用super,否则可能会有this的指向问题,如果不初始化state或者不进行方法绑定,则可以不为组件实现构造函数;
2.getDerivedStateFromProps() 从props获取state。
在初始挂载和后续更新时都会被调用,返回一个对象更新state,如果返回null就不更新;如果props传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾。
3.render() class组件中唯一必须实现的方法。
render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。
4.componentDidMount() 在组件挂在后(插入到dom树中)后立即调用
可以在这里调用Ajax请求,返回的数据可以通过setState使组件重新渲染,或者添加订阅,但是要在conponentWillUnmount中取消订阅
更新阶段
1.getDerivedStateFromProps()
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容
2.shouldComponentUpdate() 当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法
3.render() 渲染页面
4.getSnapshotBeforeUpdate() 该函数在组件更新之前执行
5.componentDidUpdate() 该函数在组件更新之后执行,它是组件更新的最后一个环节。
卸载阶段
1.componentWillUnmount() 在组件卸载和销毁之前调用
在这执行必要的清理操作,例如,清除timer(setTimeout,setInterval),取消网络请求,或者取消在componentDidMount的订阅,移除所有监听
借助shouldComponentUpdate 和PureComponent实现组件性能优化
shouldComponentUpdate通过新旧props和state对比 return true或false控制是否更新,
PureComponent是react在声明PureComponent时,在原型上添加了isPureReactComponent = true的属性判断是PureComponent组件,才走浅比较逻辑。
Render在组件中挂载,每次更新都会执行。不论页面是否真的有变化。
2 React组件传值
父组件向子组件传值: 父组件在子组件身上通过自定义属性传值, 子组件内部通过this.props接收值
子组件向父组件传值: 父组件在子组件身上通过自定义属性传递一个函数, 子组件内部通过this.props接收这个函数并回调该函数.
兄弟组件间通信: 通信双方都需要借助一个公共的通信对象, 其中一个组件提前通过on监听事件, 另一个组件通过emit触发事件并传值.
跨组件传值: 通信双方都需要借助一个功能的通信对象, 外层组件通过通信对象的Provider组件的value属性传值,内层组件通过和通信对象建立连接 再通过this.context获取值.
import { createContext } from 'react'实例化一个通信对象 export default createContext() <*context.Provider* *value*={999999}> <*ChildC* /> provider组件传值给c </*context.Provider*> ChildD.contextType = context;让当前组件和context通信对象建立连接
受控组件
表单元素值不受所在组件状态控制叫做非受控组件 受控组件通过default Value(默认值)属性,onChange事件配合将非受控组件变为受控组件
高阶函数
high-order-function(高阶函数)接受函数作为参数的函数,返回值是另一个函数.高阶函数的应用有很多,函数防抖,函数节流,bind函数,函数柯里化, map,Promise的then函数等。 防抖==短时间内多次触发同一事件,Ⅰ只执行最后一次,或者只执行最开始的一次,中间的不执行.| 节流==连续触发事件但是在n秒中只执行一次函数。即2n秒内执行2次.….。节流如字面意思,会稀释函数的执行频率。
Redux三大原则
单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。就是所有的state都被保存在一个对象里面。尽管页面中的可能有成千上百个state,但redux都将它们统统保存在一个objcet里面(这个object可以通过store.getState()这个方法得到)。redux并不禁止创建多个store,但这样做没必要。从单一的数据源中获取state,明显会为开发带来很多便利。
State 是只读的
我们通过store自带的dispatch方法发出了一个action。 你只能读取存储在redux中的state而不能直接修改(即用=号直接对它赋值),唯一改变 state 的方法就是触发 action。什么是action? action本身不具有任何业务相关的逻辑,你可以理解为action就是让我们告诉redux我们准备做什么事。 当redux检测到一个action被dispatch时,这些数据就会被传入reducer里面。
使用纯函数来执行修改
纯函数的返回值只允许通过对参数进行计算得来。修改参数,操作/获取外部的变量,打印log,发送http请求等操作都是不允许的。 纯函数的使用是非常安全的,一个纯函数在相同的输入下总会有相同的输出。我们不必担心它做出一些出乎我们意料之外的操作。
reducer 只做一件事情,通过之前的 state 和当前的 action 计算得出新的 state
正因为直接修改reducer的参数是不被允许的(因为在函数执行的时候,参数实际是外部传进来的对象),所以我们最后通过return的方式直接替换整个state,而不是直接修改旧的state。
类组件和函数组件
class组件, 组件内有this(表示当前组件实例), 组件内有状态(state), 组件内有生命周期函数
函数组件, 组件内没有this, 不需要实例化, 不需要占用多余的内存
函数组件, 组件内没有状态
函数组件, 组件内没有生命周期函数
作用: 函数式组件适合用在纯粹的组件展示的场景.
hooks在函数组件顶层调用
useState
返回的两个参数: 参数一是保存状态值的变量,参数二是更新状态值的函数,同时会触发组件更新
var [count,setCount] = useState(0)
var [loading,setLoading] = useState(false)
var [arr,setArr] = useState([])
可以更新变量的值但不能获取变量最新的值只能去useEffect监听回调中才能获取最新值 count = 0; 通过赋值号修改状态变量的值 , 无法触发组件更新
setCount(count+1); set开头的函数可以更新状态变量的值, 同时会触发组件更新( 如果setCount的参数值和原有值相比没有变化 则当前组件不会更新 )组件初次渲染时执行一次, 后续组件更新时都会执行,
useEffect
用来给当前组件引入生命周期
写法零 忽略第二个参数 只有第一个参数回调函数 会在组件初次渲染和后期每一次组件更新都会执行 `useEffect(()=>{
console.log('111111111111');
})`
写法一 带一个空数组 类似于componentDidMount方法 初次渲染时更新一次,可以发请求,监听 不能多次执行 要自动执行
`useEffect(()=>{
console.log('111111111111');
},[])`
两个参数【监听数组】 初次渲染执行一次,后续更新时,监听的变量值发生变化才执行 可以获取最新值 类似于vue的watch监听
` useEffect(()=>{
console.log('222222222222');
console.log(count);
},[loading,count])`
写法三,【空数组】 且加上return。 return 后面的这个函数会在组件卸载时 自动执行. 类似componentWillUnmount方法 总体来说也类似与挂载期的效果 只是通过return添加了卸载期的效果
useEffect(()=>{ console.log('333333333333'); 可以在这里发请求 return ()=>{ console.log('44444444444444'); } },[])
useRef()
用来定义ref引用. ref引用绑定在普通标签身上, 获取的是dom节点 ref引用无法用在函数组件身上, 函数组件不会实例化 在函数组件中使用ref要定义一个ref引用,并绑定在要获取的标签身上 用ref1.current来获取对应的值 子组件身上无法使用ref绑定
var ref1= useRef类型(null)
var updateCount = ()=>{
setCount(count+1);
console.log( ref1.current );
}
``<divref={ref1}>count的值: { count }
<button onClick={updateCount}>修改count</button>
</div>`
useContext
实例化一个通信对象 export default createContext(0)
通过通信对象.provider 包裹 通过vlue传值
`<ctx.Provider value={99999}>
<ChildA />
</ctx.Provider>`
通过useContext获取通信对象中传来的值
var value = useContext(ctx)
useMemo
用于性能优化 每次组件更新都会执行但第一个参数取决于依赖的数据是否改变,如果监听的依赖数组的值没发生变化 第一个参数不会执行
发生改变才会执行第一个参数这个回调函数会产生新的计算结果并缓存该结果
使用上一次依赖缓存的结果。
第一个参数是回调函数,第二个是监听数组
` var calc = useMemo(()=>{
console.log('calc');
var sum = 0;
for(var i = 0;i <= count;i++){
sum += i;
}
return sum;
},[count])`
useCallback()
会监听[]中数据的变化.
当前组件更新时, 执行useCallback(), 如果数据发生改变则重新创建一个新的回调函数并返回该函数,同时会缓存该函数.
当前组件更新时, 执行useCallback(), 如果数据没有发生改变则直接返回上一次缓存的函数. useCallback() 是通过缓存一个函数实现组件更新时的性能优化
`var calc = useCallback(()=>{
console.log('calc');
var sum = 0;
for(var i = 0;i <= count;i++){
sum += i;
}
return sum;
},[count])`
路由传参
动态路由传参
this.props.history.push("/路径/"+666)
获取参数log(this.props.match.params)
#固定路由传参(query参数和state参数)
this.props.history.push({pathname:"/路径",query:{id:666}})
获取参数log(this.props.location.query);
this.props.history.push({pathname:'/路径',state:{id:999}})
获取参数log(this.props.location.state);