前端技术总结七:React
1、Hooks
什么是Hooks
Hooks是一个新的React特性提案,组件尽量写成纯函数,如果需要外部React特性(比如状态管理,生命周期),就用钩子把外部特性"钩"进来,通常函数名字都是以use开头。首次在v16.7.0-alpha版本中添加。
Hooks产生的背景
组件之间复用状态逻辑很难,在hooks之前,实现组件复用,一般采用高阶组件和 Render Props,它们本质是将复用逻辑提升到父组件中,很容易产生很多包装组件,带来嵌套地狱。
组件逻辑变得越来越复杂,尤其是生命周期函数中常常包含一些不相关的逻辑,完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。
复杂的class组件,使用class组件,需要理解 JavaScript 中 this 的工作方式,不能忘记绑定事件处理器等操作,代码复杂且冗余。除此之外,class组件也会让一些react优化措施失效。
针对上面提到的问题,react团队研发了hooks,它主要有两方面作用:
用于在函数组件中引入状态管理和生命周期方法
取代高阶组件和render props来实现抽象和可重用性
2、纯函数
纯函数的定义是
1)如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。
2)该函数不会产生任何可观察的副作用,例如网络请求,输入和输出设备或数据突变(mutation)。
使用纯函数的原因:
是方便测试以及重构。纯函数还使得维护和重构代码变得更加容易,不必担心其副作用。正确地使用纯函数可以产生更加高质量的代码。
3、副作用
一个可以被观察的副作用是在函数内部与其外部的任意交互。这可能是在函数内修改外部的变量,或者在函数里调用另外一个函数等。
注: 如果纯函数调用纯函数,则不产生副作用依旧是纯函数。
副作用来自,但不限于:
1)进行一个 HTTP 请求
2)Mutating data
3)输出数据到屏幕或者控制台
4)DOM 查询/操作
5)Math.random()
6)获取的当前时间
4、useCallback
函数式组件中,每一次更新状态,自定义的函数都要进行重新的声明和定义,如果函数作为props传递给子组件,会造成子组件不必要的重新渲染,有时候子组件并没有使用到父组件发生变化的状态,此时可以使用useCallback来进行性能优化,它会为函数返回一个记忆的值,如果依赖的状态没有发生变化,那么则不会重新创建该函数,也就不会造成子组件不必要的重新渲染。
import React, { useState, useCallback, memo } from 'react' const AddBtn = memo((props)=>{ // 使用函数表达式的方式定义了一个函数式组件 return<button onClick={props.increment}>+1</button> }) export default function CallBackPerformance(){ const [ count, setCount ] = useState(0) const [ show, setShow ] = useState(true) const increment1 = () => { console.log('increment1被调用了') setCount(count+1) } const increment2 = useCallback(()=>{ // 使用了useCallback来优化的函数 console.log('increment2被调用了') setCount(count+1) },[count]) return(<div> <div>当前计数:{count}</div> <AddBtn increment={increment1} name="1"/> <AddBtn increment={increment2} name="2"/> <button onClick={e => setShow(!show)}>切换show</button> </div>) } // 当show这个状态发生变化时,子组件increment1会重新渲染,increment2不会重新渲染
5、useMemo
useMemo也是返回一个记忆的值,如果依赖的内容没有发生改变的话,这个值也不会发生变化,useMemo与useCallback的不同点在于useMemo需要在传入的函数里需要return 一个值,这个值可以是对象、函数
6、高阶函数
接受一个或多个函数作为输入;
或者输出一个函数;
7、高阶组件
高阶组件是参数为组件,返回值为新组件的函数
;
高阶组件本身不是一个组件,而是一个函数;
参数是一个组件,返回值也是一个组件
优点:
1.实现了对原有组件的增强和优化,
2.可以对原有组件中的state, props和逻辑执行增删改操作, 一般用于代码重用和组件增强优化
应用场景:
1、需要代码重用时, react如果有多个组件都用到了同一段逻辑, 这时,就可以把共同的逻辑部分提取出来,利用高阶组件的形式将这段逻辑整合到每一个组件中, 从而减少代码的逻辑重复 2、需要组件增强优化时, 比如我们在项目中使用的组件是第三方的,组件可能比较复杂, 有时不能完全满足需求, 但第三方组件不易修改, 此时也可以用高阶组件,在不修改原始组件的前提下, 对组件添加满足实际开发需求的功能 3、针对优点的应用 4、也可以用来替换 mixins 混入
8、函数组件和类组件
函数式组件和类组件的区别
1. 语法上的区别:
函数式组件是⼀个纯函数,它是需要接受props参数并且返回⼀个React元素就可以了。类组件是需要继承React.Component的,⽽且class组件需要创建render并且返回React元素,语法上来讲更复杂。
2. 调⽤⽅式:
函数式组件可以直接调⽤,返回⼀个新的React元素;类组件在调⽤时是需要创建⼀个实例的,然后通过调⽤实例⾥的render⽅法来返回⼀个React元素。
3. 状态管理:
函数式组件没有状态管理,类组件有状态管理。
4. 使⽤场景:
类组件没有具体的要求。函数式组件⼀般是⽤在⼤型项⽬中来分割⼤组件(函数式组件不⽤创建实例,所有更⾼效),⼀般情况下能⽤函数式组件就不⽤类组件,提升效率。
9、为什么state值不能直接修改,需要通过setState
setState 是异步的。是否调⽤ render 进⾏再次渲染。 setState 本质是通过⼀个队列实现 state 更新的,执⾏ setState 时,会将要更新的 state 合并后放⼊状态队列, ⽽不会⽴即更新。如果没有通过 this.setState, ⽽是直接 this.state 修改,会导致这个修改没有放⼊队列中,下次执⾏ this.setState 合并队列时,就会忽略这次的修 改,从⽽导致数据没有更新。 简单点说,就是 setState 就是放⼊队列,⽽ this.state 会跳过队列,从⽽导致有可能这次的修改值会被忽略掉
10、react的钩⼦函数有哪些?请求放在那个函数中?为什么?渲染页⾯钩⼦函数的执⾏顺序什么?
钩⼦函数: componentWillMount 、 render 、 componentDidMount 、 shouldComponentUpdate 、 componentWillUpdate 、 componentDidUpdate
请求放在:componentDidMount ,因为这个在组件加载的时候只会执⾏⼀次
执⾏顺序:componentWillMount 、 render 、 componentDidMount 、 shouldComponentUpdate 、 componentWillUpdate 、 render 、 componentDidUpdate componentWillReceiveProps 是在 props 发⽣改变的时候执⾏
11、state与props有什么区别?
props 是外部组件传⼊的数据,⼀般是⽗组件传到⼦组件的数据。 props ⾥⾯的数据不能修改,只能通过绑定⽗组件的⽅法来修改 props ⾥⾯的值,然后再传到⼦组件。 ⽽ state 是组件的私有变量。主要⽤于组件的保存,控制以及修改⾃⼰的状态,不能通过外部的访问以及修改,只能通过内部的 this.setState ⽅法来修改 state ⾥⾯的内容。
12、react如何去操作dom的?
通过 refs 可以实现对 dom 的操作。通过给组件添加 ref=‘XXXXX’, 然后在⽅法⽴马通过调⽤ this.refs.XXXXX ,从⽽进⾏对 dom 的操作
13、react在setState之后执⾏了哪些操作?
setState 的基本过程是,在执⾏ setState 之后,会执⾏ shouldComponentUpdate 、 componentWillUpdate 、 render 、 componentDidUpdate 。在执⾏ render 的时候this.state 才会被更新。之前两个钩⼦函数都不会更新
14、react性能优化是哪个周期函数?
shouldComponentUpdate 这个函数⽤来判断是否需要调⽤ render ⽅法重新描绘 dom ,因为 dom 的描绘⾮常的消耗性能,如果可以在 shouldComponentUpdate 中写出更优化的 dom diff 算法可以极⼤的提⾼性能
15、Diff算法
由于在浏览器中操作DOM是很昂贵的,频繁的操作DOM,会产生一定的性能问题,这就是虚拟DOM的产生原因。
虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象状态变更时,记录新树与旧树的差异,最后把差异更新到真正的DOM中。
diff算法的作用:用来修改DOM的一小段,不会引起dom树的重绘
更高效的Diff算法:
React的开发者结合Web界面的特点做出了两个大胆的假设,使得Diff算法复杂度直接从O(n^3)降低到O(n),假设如下:
(1)两个相同组件产生类似的DOM结构,不同的组件产生不同的DOM结构; (2)对于同一层次的一组子节点,它们可以通过唯一的id进行区分。
新的Diff算法是逐层进行比较,只比较同一层次的节点,不会跨层次比较,大大降低了复杂度。
不同类型节点的比较:Diff算法会直接删除旧的节点及其子节点并插入新的节点
相同类型节点的比较:若是两个节点类型相同时,Diff算法会重新设置该节点的属性,从而实现节点的更新。
16、redux
redux的基本思想是整个应用的state保持在单一的store中。
三大原则:
1、单一数据源 2、State 是只读的 3、使用纯函数来执行修改
store:store就是一个javascript对象,保存整个应用的state。
1、允许通过getState()访问state 2、通过dispatch(action)改变state 3、通过subscribe(listener) 注册listener 4、通过subscribe(listener) 返回函数处理listener的注册
action:是一个javascript对象,必须有个type属性表明正在执行action的类型。
{ type: 'ADD_TODO', text: 'Go to swimming pool' }
reducer:是一个纯函数,已先前的state和action作为参数,并返回下一个state。
dispatch:store.dispatch() 将 action 传到 store。
import { createStore } from 'redux'; /** * 这是一个 reducer,形式为 (state, action) => state 的纯函数。 * 描述了 action 如何把 state 转变成下一个 state。 * * state 的形式取决于你,可以是基本类型、数组、对象、 * 甚至是 Immutable.js 生成的数据结构。惟一的要点是 * 当 state 变化时需要返回全新的对象,而不是修改传入的参数。 * * 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper) * 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。 */ function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } // 创建 Redux store 来存放应用的状态。 // API 是 { subscribe, dispatch, getState }。 let store = createStore(counter); // 可以手动订阅更新,也可以事件绑定到视图层。
const unsubscribe = store.subscribe(() =>
console.log(store.getState())
)
// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({ type: 'INCREMENT' }); // 1
store.dispatch({ type: 'INCREMENT' }); // 2
store.dispatch({ type: 'DECREMENT' }); // 1
// 停止监听 state 更新
unsubscribe();
17、vue和react的异同
共同点:
(1)都使用虚拟dom。
(2)提供了响应式和组件化的视图组件。
(3)关注核心代码,将其他功能如路由和全局状态管理交给相关的库。(vue-router、vuex、react-router、redux等等)
区别:
(1)核心思想不同:
Vue定位是降低前端开发的门槛,更快地上手开发。特点:灵活易用的渐进式框架,进行数据拦截/代理,它对侦测数据的变化更敏感、更精确。
React定位是提出 UI 开发的新思路。推崇函数式编程(纯组件),数据不可变以及单向数据流,需要双向的地方可借助onChange和setState来实现。
(2)组件写法差异:
React推荐的做法是JSX + inline style, 把 HTML 和 CSS 全都写进 JavaScript 中,即 all in js;
Vue 推荐的做法是 template 的单文件组件格式,即 html,css,JS 写在同一个文件(vue也支持JSX写法)
(3)响应式原理不同
Vue:递归监听data的所有属性,直接修改。当数据改变时,自动找到引用组件重新渲染。
React:基于状态机,手动优化,数据不可变,需要setState驱动新的state替换老的state。当数据改变时,以组件为根目录,默认全部重新渲染。
(4)应用场景
Vue
1、构建数据简单中小型应用时:vue提供简单明了的书写模板、大量api、指令等等,可快速上手、开发项目
2、应用尽可能的小和快:随着vue3.0的发布,vue的体积进一步缩小,远小于react的体积,也配合diff算法,采用proxy去实现双向绑定,渲染大幅度提升
React
1、构建一个大型应用项目时:React的渲染系统可配置性更强
2、适用于Web端和原生APP:React Native是一个使用Javascript构建移动端原生应用程序(iOS,Android)的库。
18、快速删除node_modules
解决办法:
方法一:.进入项目所在目录使用CMD命令进入DOCS控制台执行如下命令即可快速删除。
rd /s /q node_modules
方法二:使用PowerShell或git bash进入命令控制台执行如下命令即可快速删除(推荐,速度比较快)。
rm -rf ./node_modules
方法三:直接在项目中使用npm的rimraf工具实现秒删(需要安装库)
npm install rimraf -g
rimraf node_modules