网站集约化建设 技术,瀑布流网站后台,it软件外包公司,唐山广告设计制作公司了解redux吗#xff1f; redux 是一个应用数据流框架#xff0c;主要解决了组件之间状态共享问题#xff0c;原理是集中式管理#xff0c;主要有三个核心方法#xff1a;action store reduce 工作流程 view 调用store的dispatch 接受action传入的store#xff0c;reduce…了解redux吗 redux 是一个应用数据流框架主要解决了组件之间状态共享问题原理是集中式管理主要有三个核心方法action store reduce 工作流程 view 调用store的dispatch 接受action传入的storereduce进行state操作 view通过store提供的getState获取最新的数据 redux的优点 新增的state 对状态的管理更加明确 流程更加规范减少手动编写代码提高编码效率 redux的缺点 当数据更新是有时候组件不需要也要重新绘制影响效率
diff算法如何比较?
只对同级比较跨层级的dom不会进行复用不同类型节点生成的dom树不同此时会直接销毁老节点及子孙节点并新建节点可以通过key来对元素diff的过程提供复用的线索单节点diff单点diff有如下几种情况key和type相同表示可以复用节点key不同直接标记删除节点然后新建节点key相同type不同标记删除该节点和兄弟节点然后新创建节点
React 设计思路它的理念是什么
1编写简单直观的代码
React最大的价值不是高性能的虚拟DOM、封装的事件机制、服务器端渲染而是声明式的直观的编码方式。react文档第一条就是声明式React 使创建交互式 UI 变得轻而易举。为应用的每一个状态设计简洁的视图当数据改变时 React 能有效地更新并正确地渲染组件。 以声明式编写 UI可以让代码更加可靠且方便调试。
2简化可复用的组件
React框架里面使用了简化的组件模型但更彻底地使用了组件化的概念。React将整个UI上的每一个功能模块定义成组件然后将小的组件通过组合或者嵌套的方式构成更大的组件。React的组件具有如下的特性∶
可组合简单组件可以组合为复杂的组件可重用每个组件都是独立的可以被多个组件使用可维护和组件相关的逻辑和UI都封装在了组件的内部方便维护可测试因为组件的独立性测试组件就变得方便很多。
3) Virtual DOM
真实页面对应一个 DOM 树。在传统页面的开发模式中每次需要更新页面时都要手动操作 DOM 来进行更新。 DOM 操作非常昂贵。在前端开发中性能消耗最大的就是 DOM 操作而且这部分代码会让整体项目的代码变得难 以维护。React 把真实 DOM 树转换成 JavaScript 对象树也就是 Virtual DOM每次数据更新后重新计算 Virtual DOM并和上一次生成的 Virtual DOM 做对比对发生变化的部分做批量更新。React 也提供了直观的 shouldComponentUpdate 生命周期回调来减少数据变化后不必要的 Virtual DOM 对比过程以保证性能。
4函数式编程
React 把过去不断重复构建 UI 的过程抽象成了组件且在给定参数的情况下约定渲染对应的 UI 界面。React 能充分利用很多函数式方法去减少冗余代码。此外由于它本身就是简单函数所以易于测试。
5一次学习随处编写
无论现在正在使用什么技术栈都可以随时引入 React来开发新特性而不需要重写现有代码。
React 还可以使用 Node 进行服务器渲染或使用 React Native 开发原生移动应用。因为 React 组件可以映射为对应的原生控件。在输出的时候是输出 Web DOM还是 Android 控件还是 iOS 控件就由平台本身决定了。所以react很方便和其他平台集成
何为高阶组件(higher order component)
高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合HOC最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时就应该考虑将代码重构为可重用的 HOC。
练习 写一个反转其输入的 HOC写一个从 API 提供数据给传入的组件的 HOC写一个实现 shouldComponentUpdate 来避免 reconciliation 的 HOC写一个通过 React.Children.toArray 对传入组件的子组件进行排序的 HOC
**
React 与 Vue 的 diff 算法有何不同
diff 算法是指生成更新补丁的方式主要应用于虚拟 DOM 树变化后更新真实 DOM。所以 diff 算法一定存在这样一个过程触发更新 → 生成补丁 → 应用补丁。
React 的 diff 算法触发更新的时机主要在 state 变化与 hooks 调用之后。此时触发虚拟 DOM 树变更遍历采用了深度优先遍历算法。但传统的遍历方式效率较低。为了优化效率使用了分治的方式。将单一节点比对转化为了 3 种类型节点的比对分别是树、组件及元素以此提升效率。
树比对由于网页视图中较少有跨层级节点移动两株虚拟 DOM 树只对同一层次的节点进行比较。组件比对如果组件是同一类型则进行树比对如果不是则直接放入到补丁中。元素比对主要发生在同层级中通过标记节点操作生成补丁节点操作对应真实的 DOM 剪裁操作。
以上是经典的 React diff 算法内容。自 React 16 起引入了 Fiber 架构。为了使整个更新过程可随时暂停恢复节点与树分别采用了 FiberNode 与 FiberTree 进行重构。fiberNode 使用了双链表的结构可以直接找到兄弟节点与子节点。整个更新过程由 current 与 workInProgress 两株树双缓冲完成。workInProgress 更新完成后再通过修改 current 相关指针指向新节点。
Vue 的整体 diff 策略与 React 对齐虽然缺乏时间切片能力但这并不意味着 Vue 的性能更差因为在 Vue 3 初期引入过后期因为收益不高移除掉了。除了高帧率动画在 Vue 中其他的场景几乎都可以使用防抖和节流去提高响应性能。
何为受控组件(controlled component)
在 HTML 中类似 input, textarea 和 select 这样的表单元素会维护自身的状态并基于用户的输入来更新。当用户提交表单时前面提到的元素的值将随表单一起被发送。但在 React 中会有些不同包含表单元素的组件将会在 state 中追踪输入的值并且每次调用回调函数时如 onChange 会更新 state重新渲染组件。一个输入表单元素它的值通过 React 的这种方式来控制这样的元素就被称为受控元素。
参考 前端进阶面试题详细解答
react16的错误边界Error Boundaries是什么
部分 UI 中的 JavaScript 错误不应该破坏整个应用程序。 为了解决 React 用户的这个问题React 16引入了一个 “错误边界(Error Boundaries)” 的新概念。
import React from react;
import ReactDOM from react-dom;
class ErrorBoundary extends React.Component{constructor(props) {super(props);this.state{hasError:false};}componentDidCatch(err,info) {this.setState({hasError: true});}render() {if (this.state.hasError) {return h1Something Went Wrong/h1}return this.props.children;}
}class Page extends React.Component{render() {return (ErrorBoundaryClock//ErrorBoundary)}
}
class Clock extends React.Component{render() {return (divhello{null.toString()}/div)}
}ReactDOM.render(Page/,document.querySelector(#root));React setState 调用的原理
具体的执行过程如下源码级解析
首先调用了setState 入口函数入口函数在这里就是充当一个分发器的角色根据入参的不同将其分发到不同的功能函数中去
ReactComponent.prototype.setState function (partialState, callback) {this.updater.enqueueSetState(this, partialState);if (callback) {this.updater.enqueueCallback(this, callback, setState);}
};
enqueueSetState 方法将新的 state 放进组件的状态队列里并调用 enqueueUpdate 来处理将要更新的实例对象
enqueueSetState: function (publicInstance, partialState) {// 根据 this 拿到对应的组件实例var internalInstance getInternalInstanceReadyForUpdate(publicInstance, setState);// 这个 queue 对应的就是一个组件实例的 state 数组var queue internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue []);queue.push(partialState);// enqueueUpdate 用来处理当前的组件实例enqueueUpdate(internalInstance);
}
在 enqueueUpdate 方法中引出了一个关键的对象——batchingStrategy该对象所具备的isBatchingUpdates 属性直接决定了当下是要走更新流程还是应该排队等待如果轮到执行就调用 batchedUpdates 方法来直接发起更新流程。由此可以推测batchingStrategy 或许正是 React 内部专门用于管控批量更新的对象。
function enqueueUpdate(component) {ensureInjected();// 注意这一句是问题的关键isBatchingUpdates标识着当前是否处于批量创建/更新组件的阶段if (!batchingStrategy.isBatchingUpdates) {// 若当前没有处于批量创建/更新组件的阶段则立即更新组件batchingStrategy.batchedUpdates(enqueueUpdate, component);return;}// 否则先把组件塞入 dirtyComponents 队列里让它“再等等”dirtyComponents.push(component);if (component._updateBatchNumber null) {component._updateBatchNumber updateBatchNumber 1;}
}
注意batchingStrategy 对象可以理解为“锁管理器”。这里的“锁”是指 React 全局唯一的 isBatchingUpdates 变量isBatchingUpdates 的初始值是 false意味着“当前并未进行任何批量更新操作”。每当 React 调用 batchedUpdate 去执行更新动作时会先把这个锁给“锁上”置为 true表明“现在正处于批量更新过程中”。当锁被“锁上”的时候任何需要更新的组件都只能暂时进入 dirtyComponents 里排队等候下一次的批量更新而不能随意“插队”。此处体现的“任务锁”的思想是 React 面对大量状态仍然能够实现有序分批处理的基石。
除了在构造函数中绑定 this还有其它方式吗
你可以使用属性初始值设定项(property initializers)来正确绑定回调create-react-app 也是默认支持的。在回调中你可以使用箭头函数但问题是每次组件渲染时都会创建一个新的回调。
React-Router的路由有几种模式
React-Router 支持使用 hash对应 HashRouter和 browser对应 BrowserRouter 两种路由规则 react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现应用的 UI 和 URL 同步
BrowserRouter 创建的 URL 格式xxx.com/pathHashRouter 创建的 URL 格式xxx.com/#/path
1BrowserRouter
它使用 HTML5 提供的 history APIpushState、replaceState 和 popstate 事件来保持 UI 和 URL 的同步。由此可以看出BrowserRouter 是使用 HTML 5 的 history API 来控制路由跳转的
BrowserRouterbasename{string}forceRefresh{bool}getUserConfirmation{func}keyLength{number}
/
其中的属性如下
basename 所有路由的基准 URL。basename 的正确格式是前面有一个前导斜杠但不能有尾部斜杠
BrowserRouter basename/calendarLink to/today /
/BrowserRouter
等同于
a href/calendar/today /
forceRefresh 如果为 true在导航的过程中整个页面将会刷新。一般情况下只有在不支持 HTML5 history API 的浏览器中使用此功能getUserConfirmation 用于确认导航的函数默认使用 window.confirm。例如当从 /a 导航至 /b 时会使用默认的 confirm 函数弹出一个提示用户点击确定后才进行导航否则不做任何处理
// 这是默认的确认函数
const getConfirmation (message, callback) {const allowTransition window.confirm(message);callback(allowTransition);
}
BrowserRouter getUserConfirmation{getConfirmation} / 需要配合Prompt 一起使用。 KeyLength 用来设置 Location.Key 的长度。
2HashRouter
使用 URL 的 hash 部分即 window.location.hash来保持 UI 和 URL 的同步。由此可以看出HashRouter 是通过 URL 的 hash 属性来控制路由跳转的
HashRouterbasename{string}getUserConfirmation{func}hashType{string}
/
其参数如下
basename, getUserConfirmation 和 BrowserRouter 功能一样hashType window.location.hash 使用的 hash 类型有如下几种 slash - 后面跟一个斜杠例如 #/ 和 #/sunshine/lollipopsnoslash - 后面没有斜杠例如 # 和 #sunshine/lollipopshashbang - Google 风格的 ajax crawlable例如 #!/ 和 #!/sunshine/lollipops。
React 性能优化在哪个生命周期它优化的原理是什么
react的父级组件的render函数重新渲染会引起子组件的render方法的重新渲染。但是有的时候子组件的接受父组件的数据没有变动。子组件render的执行会影响性能这时就可以使用shouldComponentUpdate来解决这个问题。
使用方法如下
shouldComponentUpdate(nexrProps) {if (this.props.num nexrProps.num) {return false}return true;
}
shouldComponentUpdate提供了两个参数nextProps和nextState表示下一次props和一次state的值当函数返回false时候render()方法不执行组件也就不会渲染返回true时组件照常重渲染。此方法就是拿当前props中值和下一次props中的值进行对比数据相等时返回false反之返回true。
需要注意在进行新旧对比的时候是**浅对比**也就是说如果比较的数据时引用数据类型只要数据的引用的地址没变即使内容变了也会被判定为true。
面对这个问题可以使用如下方法进行解决 1使用setState改变数据之前先采用ES6中assgin进行拷贝但是assgin只深拷贝的数据的第一层所以说不是最完美的解决办法
const o2 Object.assign({},this.state.obj)o2.student.count 00000;this.setState({obj: o2,})
2使用JSON.parse(JSON.stringfy())进行深拷贝但是遇到数据为undefined和函数时就会错。
const o2 JSON.parse(JSON.stringify(this.state.obj))o2.student.count 00000;this.setState({obj: o2,})
React 事件机制
div onClick{this.handleClick.bind(this)}点我/div
React并不是将click事件绑定到了div的真实DOM上而是在document处监听了所有的事件当事件发生并且冒泡到document处的时候React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗还能在组件挂在销毁时统一订阅和移除事件。
除此之外冒泡到document上的事件也不是原生的浏览器事件而是由react自己实现的合成事件SyntheticEvent。因此如果不想要是事件冒泡的话应该调用event.preventDefault()方法而不是调用event.stopProppagation()方法。 JSX 上写的事件并没有绑定在对应的真实 DOM 上而是通过事件代理的方式将所有的事件都统一绑定在了 document 上。这样的方式不仅减少了内存消耗还能在组件挂载销毁时统一订阅和移除事件。
另外冒泡到 document 上的事件也不是原生浏览器事件而是 React 自己实现的合成事件SyntheticEvent。因此我们如果不想要事件冒泡的话调用 event.stopPropagation 是无效的而应该调用 event.preventDefault。
实现合成事件的目的如下
合成事件首先抹平了浏览器之间的兼容问题另外这是一个跨浏览器原生事件包装器赋予了跨浏览器开发的能力对于原生浏览器事件来说浏览器会给监听器创建一个事件对象。如果你有很多的事件监听那么就需要分配很多的事件对象造成高额的内存分配问题。但是对于合成事件来说有一个事件池专门来管理它们的创建和销毁当事件需要被使用时就会从池子中复用对象事件回调结束后就会销毁事件对象上的属性从而便于下次复用事件对象。
(组件的)状态(state)和属性(props)之间有何不同
State 是一种数据结构用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变但多数时候是作为用户事件行为的结果。
Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件并且就子组件而言props 是不可变的(immutable)。组件不能改变自身的 props但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据–回调函数也可以通过 props 传递。
为什么调用 setState 而不是直接改变 state
解答
如果您尝试直接改变组件的状态React 将无法得知它需要重新渲染组件。通过使用setState()方法React 可以更新组件的UI。
另外您还可以谈谈如何不保证状态更新是同步的。如果需要基于另一个状态或属性更新组件的状态请向setState()传递一个函数该函数将 state 和 props 作为其两个参数
this.setState((state, props) ({counter: state.counter props.increment
}));
在React中组件的this.state和setState有什么区别
this.state通常是用来初始化state的this.setState是用来修改state值的。如果初始化了state之后再使用this.state之前的state会被覆盖掉如果使用this.setState只会替换掉相应的state值。所以如果想要修改state的值就需要使用setState而不能直接修改state直接修改state之后页面是不会更新的。
Redux Thunk 的作用是什么
Redux thunk 是一个允许你编写返回一个函数而不是一个 action 的 actions creators 的中间件。如果满足某个条件thunk 则可以用来延迟 action 的派发(dispatch)这可以处理异步 action 的派发(dispatch)。
React 16中新生命周期有哪些
关于 React16 开始应用的新生命周期 可以看出React16 自上而下地对生命周期做了另一种维度的解读
Render 阶段用于计算一些必要的状态信息。这个阶段可能会被 React 暂停这一点和 React16 引入的 Fiber 架构我们后面会重点讲解是有关的Pre-commit阶段所谓“commit”这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit就是说我在这个阶段其实还并没有去更新真实的 DOM不过 DOM 信息已经是可以读取的了Commit 阶段在这一步React 会完成真实 DOM 的更新工作。Commit 阶段我们可以拿到真实 DOM包括 refs。
与此同时新的生命周期在流程方面仍然遵循“挂载”、“更新”、“卸载”这三个广义的划分方式。它们分别对应到
挂载过程 constructorgetDerivedStateFromPropsrendercomponentDidMount 更新过程 getDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdate 卸载过程 componentWillUnmount
React的生命周期有哪些
React 通常将组件生命周期分为三个阶段
装载阶段Mount组件第一次在DOM树中被渲染的过程更新过程Update组件状态发生变化重新更新渲染的过程卸载过程Unmount组件从DOM树中被移除的过程
1组件挂载阶段
挂载阶段组件被创建然后组件实例插入到 DOM 中完成组件的第一次渲染该过程只会发生一次在此阶段会依次调用以下这些方法
constructorgetDerivedStateFromPropsrendercomponentDidMount
1constructor
组件的构造函数第一个被执行若没有显式定义它会有一个默认的构造函数但是若显式定义了构造函数我们必须在构造函数中执行 super(props)否则无法在构造函数中拿到this。
如果不初始化 state 或不进行方法绑定则不需要为 React 组件实现构造函数Constructor。
constructor中通常只做两件事
初始化组件的 state给事件处理方法绑定 this
constructor(props) {super(props);// 不要在构造函数中调用 setState可以直接给 state 设置初始值this.state { counter: 0 }this.handleClick this.handleClick.bind(this)
}
2getDerivedStateFromProps
static getDerivedStateFromProps(props, state)
这是个静态方法所以不能在这个函数里使用 this有两个参数 props 和 state分别指接收到的新参数和当前组件的 state 对象这个函数会返回一个对象用来更新当前的 state 对象如果不需要更新可以返回 null。
该函数会在装载时接收到新的 props 或者调用了 setState 和 forceUpdate 时被调用。如当接收到新的属性想修改 state 就可以使用。
// 当 props.counter 变化时赋值给 state
class App extends React.Component {constructor(props) {super(props)this.state {counter: 0}}static getDerivedStateFromProps(props, state) {if (props.counter ! state.counter) {return {counter: props.counter}}return null}handleClick () {this.setState({counter: this.state.counter 1})}render() {return (divh1 onClick{this.handleClick}Hello, world!{this.state.counter}/h1/div)}
}
现在可以显式传入 counter 但是这里有个问题如果想要通过点击实现 state.counter 的增加但这时会发现值不会发生任何变化一直保持 props 传进来的值。这是由于在 React 16.4^ 的版本中 setState 和 forceUpdate 也会触发这个生命周期所以当组件内部 state 变化后就会重新走这个方法同时会把 state 值赋值为 props 的值。因此需要多加一个字段来记录之前的 props 值这样就会解决上述问题。具体如下
// 这里只列出需要变化的地方
class App extends React.Component {constructor(props) {super(props)this.state {// 增加一个 preCounter 来记录之前的 props 传来的值preCounter: 0,counter: 0}}static getDerivedStateFromProps(props, state) {// 跟 state.preCounter 进行比较if (props.counter ! state.preCounter) {return {counter: props.counter,preCounter: props.counter}}return null}handleClick () {this.setState({counter: this.state.counter 1})}render() {return (divh1 onClick{this.handleClick}Hello, world!{this.state.counter}/h1/div)}
}
3render
render是React 中最核心的方法一个组件中必须要有这个方法它会根据状态 state 和属性 props 渲染组件。这个函数只做一件事就是返回需要渲染的内容所以不要在这个函数内做其他业务逻辑通常调用该方法会返回以下类型中一个
React 元素这里包括原生的 DOM 以及 React 组件数组和 Fragment片段可以返回多个元素Portals插槽可以将子元素渲染到不同的 DOM 子树种字符串和数字被渲染成 DOM 中的 text 节点布尔值或 null不渲染任何内容。
4componentDidMount()
componentDidMount()会在组件挂载后插入 DOM 树中立即调。该阶段通常进行以下操作
执行依赖于DOM的操作发送网络请求官方建议添加订阅消息会在componentWillUnmount取消订阅
如果在 componentDidMount 中调用 setState 就会触发一次额外的渲染多调用了一次 render 函数由于它是在浏览器刷新屏幕前执行的所以用户对此是没有感知的但是我应当避免这样使用这样会带来一定的性能问题尽量是在 constructor 中初始化 state 对象。
在组件装载之后将计数数字变为1
class App extends React.Component {constructor(props) {super(props)this.state {counter: 0}}componentDidMount () {this.setState({counter: 1})}render () {return (div classNamecountercounter值: { this.state.counter } /div)}
}
2组件更新阶段
当组件的 props 改变了或组件内部调用了 setState/forceUpdate会触发更新重新渲染这个过程可能会发生多次。这个阶段会依次调用下面这些方法
getDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdate
1shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState)
在说这个生命周期函数之前来看两个问题
setState 函数在任何情况下都会导致组件重新渲染吗例如下面这种情况
this.setState({number: this.state.number})
如果没有调用 setStateprops 值也没有变化是不是组件就不会重新渲染
第一个问题答案是 会 第二个问题如果是父组件重新渲染时不管传入的 props 有没有变化都会引起子组件的重新渲染。
那么有没有什么方法解决在这两个场景下不让组件重新渲染进而提升性能呢这个时候 shouldComponentUpdate 登场了这个生命周期函数是用来提升速度的它是在重新渲染组件开始前触发的默认返回 true可以比较 this.props 和 nextProps this.state 和 nextState 值是否变化来确认返回 true 或者 false。当返回 false 时组件的更新过程停止后续的 render、componentDidUpdate 也不会被调用。
注意 添加 shouldComponentUpdate 方法时不建议使用深度相等检查如使用 JSON.stringify()因为深比较效率很低可能会比重新渲染组件效率还低。而且该方法维护比较困难建议使用该方法会产生明显的性能提升时使用。
2getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(prevProps, prevState)
这个方法在 render 之后componentDidUpdate 之前调用有两个参数 prevProps 和 prevState表示更新之前的 props 和 state这个函数必须要和 componentDidUpdate 一起使用并且要有一个返回值默认是 null这个返回值作为第三个参数传给 componentDidUpdate。
3componentDidUpdate
componentDidUpdate() 会在更新后会被立即调用首次渲染不会执行此方法。 该阶段通常进行以下操作
当组件更新后对 DOM 进行操作如果你对更新前后的 props 进行了比较也可以选择在此处进行网络请求例如当 props 未发生变化时则不会执行网络请求。
componentDidUpdate(prevProps, prevState, snapshot){}
该方法有三个参数
prevProps: 更新前的propsprevState: 更新前的statesnapshot: getSnapshotBeforeUpdate()生命周期的返回值
3组件卸载阶段
卸载阶段只有一个生命周期函数componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作
清除 timer取消网络请求或清除取消在 componentDidMount() 中创建的订阅等
这个生命周期在一个组件被卸载和销毁之前被调用因此你不应该再这个方法中使用 setState因为组件一旦被卸载就不会再装载也就不会重新渲染。
4错误处理阶段
componentDidCatch(error, info)此生命周期在后代组件抛出错误后被调用。 它接收两个参数∶
error抛出的错误。info带有 componentStack key 的对象其中包含有关组件引发错误的栈信息
React常见的生命周期如下 React常见生命周期的过程大致如下
挂载阶段首先执行constructor构造方法来创建组件创建完成之后就会执行render方法该方法会返回需要渲染的内容随后React会将需要渲染的内容挂载到DOM树上挂载完成之后就会执行componentDidMount生命周期函数如果我们给组件创建一个props用于组件通信、调用setState更改state中的数据、调用forceUpdate强制更新组件时都会重新调用render函数render函数重新执行之后就会重新进行DOM树的挂载挂载完成之后就会执行componentDidUpdate生命周期函数当移除组件时就会执行componentWillUnmount生命周期函数
React主要生命周期总结
getDefaultProps这个函数会在组件创建之前被调用一次有且仅有一次它被用来初始化组件的 PropsgetInitialState用于初始化组件的 state 值componentWillMount在组件创建后、render 之前会走到 componentWillMount 阶段。这个阶段我个人一直没用过、非常鸡肋。后来React 官方已经不推荐大家在 componentWillMount 里做任何事情、到现在 React16 直接废弃了这个生命周期足见其鸡肋程度了render这是所有生命周期中唯一一个你必须要实现的方法。一般来说需要返回一个 jsx 元素这时 React 会根据 props 和 state 来把组件渲染到界面上不过有时你可能不想渲染任何东西这种情况下让它返回 null 或者 false 即可componentDidMount会在组件挂载后插入 DOM 树中后立即调用标志着组件挂载完成。一些操作如果依赖获取到 DOM 节点信息我们就会放在这个阶段来做。此外这还是 React 官方推荐的发起 ajax 请求的时机。该方法和 componentWillMount 一样有且仅有一次调用。
Redux中的connect有什么作用
connect负责连接React和Redux
1获取state
connect 通过 context获取 Provider 中的 store通过 store.getState() 获取整个store tree 上所有state
2包装原组件
将state和action通过props的方式传入到原组件内部 wrapWithConnect 返回—个 ReactComponent 对 象 ConnectConnect 重 新 render 外部传入的原组件 WrappedComponent 并把 connect 中传入的 mapStateToPropsmapDispatchToProps与组件上原有的 props合并后通过属性的方式传给WrappedComponent
3监听store tree变化
connect缓存了store tree中state的状态通过当前state状态 和变更前 state 状态进行比较从而确定是否调用 this.setState()方法触发Connect及其子组件的重新渲染
React Hook 的使用限制有哪些
React Hooks 的限制主要有两条
不要在循环、条件或嵌套函数中调用 Hook在 React 的函数组件中调用 Hook。
那为什么会有这样的限制呢Hooks 的设计初衷是为了改进 React 组件的开发模式。在旧有的开发模式下遇到了三个问题。
组件之间难以复用状态逻辑。过去常见的解决方案是高阶组件、render props 及状态管理框架。复杂的组件变得难以理解。生命周期函数与业务逻辑耦合太深导致关联部分难以拆分。人和机器都很容易混淆类。常见的有 this 的问题但在 React 团队中还有类难以优化的问题希望在编译优化层面做出一些改进。
这三个问题在一定程度上阻碍了 React 的后续发展所以为了解决这三个问题Hooks 基于函数组件开始设计。然而第三个问题决定了 Hooks 只支持函数组件。
那为什么不要在循环、条件或嵌套函数中调用 Hook 呢因为 Hooks 的设计是基于数组实现。在调用时按顺序加入数组中如果使用循环、条件或嵌套函数很有可能导致数组取值错位执行错误的 Hook。当然实质上 React 的源码里不是数组是链表。
这些限制会在编码上造成一定程度的心智负担新手可能会写错为了避免这样的情况可以引入 ESLint 的 Hooks 检查插件进行预防。