最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 面试官叫我手写 Redux - 2

    正文概述 掘金(方应杭)   2021-03-23   498

    前篇 juejin.cn/post/694197…

    方:目前,我们需要整理一下我们的代码,把 appContext.js、connect.js 和 createNewState.js 的代码全整合到 redux.js 里来:

    面试官叫我手写 Redux - 2

    如此一来,App.js 就只用到了两个 API 了:

    import { appContext, connect } from "./redux";
    

    学生:接下来做什么呢?

    方:优化一下 appContext,来看看它是被如何使用的:

    面试官叫我手写 Redux - 2

    可以看到,appContext 主要是用来初始化 appState 和 setAppState 的。目前我们使用 App 的 state 当做全局 state,其实是有很大的性能问题的

    学生:什么问题?

    方:我给你演示一下这个 bug,首先,我在三个儿子组件的第一行都添加一个 log

    面试官叫我手写 Redux - 2

    然后,你注意看控制台里的 log:

    面试官叫我手写 Redux - 2

    发现什么问题没有?

    学生:有什么问题吗?

    方:你没发现有多余的 render 吗?我在「二儿子」里的 UserModifier 调用 dispatch,理论上应该只需要执行「大儿子」函数和「二儿子」函数即可(因为这两个组件的子组件都读取了 user),为什么要执行「幺儿子」函数呢?这属于多余的 render 对吧?

    学生:哦,对哦……

    方:你知道这意味着什么吗?这意味着我们每次对 state 的微小改动,都会渲染这个 App

    学生:因为你调用了 App 的 setAppState 函数对吗?

    方:是的,只要调用这个函数(并传给它了一个新的 appState),就必定会触发 App 重新执行,也就必定会导致 App 的所有子组件重新执行,除非给子组件加缓存:

    面试官叫我手写 Redux - 2

    显然,大部分人不会给每个后代组件都加缓存。

    学生:那怎么办?

    方:问题出在我们「使用 App 的 state 当做全局 state」,可以考虑将 appState 独立出来,于是我在 redux.js 里声明了一个 store,并将其导出:

    面试官叫我手写 Redux - 2

    然后将 store 传给 context 的 value:

    面试官叫我手写 Redux - 2

    如此一来,就消除了对 App 的 state 的依赖了。

    学生:那么所有用到 appState 的地方,就都要改成 store.state 了

    方:没错,由于我们上节课让 User 和 UserModifier 组件都从 connect 那里获取 state 和 dispatch,所以我们只需要修改 connect 就行了:

    面试官叫我手写 Redux - 2

    学生:这样就可以了吗?

    方:完全不行,运行页面你会发现「无法修改 user.name」。因为 store.state 的变化并不能触发任何一个组件更新,React 里想要触发组件更新一般需要调用 setState。

    学生:这不有回到原点了吗?我们一开始就是用的 setState

    方:我们一开始用的是 App 的 setState,这次我们可以使用 Wrapper 的 setState 呀

    学生:可是 Wrapper 怎么知道 store.state 变化了呢?

    方:Wrapper 可以订阅 store.state 的变化,我们只需要给 store 添加一个 subscribe 接口即可:

    面试官叫我手写 Redux - 2

    然后,在 setState 运行完毕后,遍历调用所有 fn 即可:

    面试官叫我手写 Redux - 2

    学生:你就用这么几行代码就实现了一个发布订阅?

    方:没错,只不过实现得比较简陋而已。接下来我们需要在 Wrapper 里面监听 store.state 的变化:

    面试官叫我手写 Redux - 2

    学生:state 变化了就重新渲染 Wrapper?

    方:对,但问题是如何重新渲染 Wrapper?函数组件并没有提供 forceUpdate 接口哦。这里我们要使用一个小技巧:

    面试官叫我手写 Redux - 2

    26 行的 update({}) 就相当于 forceUpdate()

    学生:你利用了 {} !== {} 这一特性!

    方:聪明,用 Symbol() 也能达到相同的效果。接下来看看我们的优化成果:

    面试官叫我手写 Redux - 2

    看到没,修改 user.name 时,三个儿子组件全都没有重新执行,只有 User 和 UserModifier 重新执行了。

    学生:那也就是说,每次修改 state,只有被 connect 过的组件会重新渲染?

    方:对。

    学生:诶?那我又有一个问题了,如果一个组件被 connect 过,但是它没有使用 user.name 也会在 user.name 被更新时重新渲染吗?

    方:目前是

    学生:能优化吗?

    方:这还不简单吗?每个 Wrapper 对比一下自己依赖的值是否改变了(用 ref 来存储上一次的值),没变就不要 update({}) 呗……

    学生:那每个 Wrapper 怎么知道自己依赖的值是 state.user 还是 state.group 呢?

    方:让 connect 接受一个 selector 即可,如果依赖 user,就给 connect 传一个 state => ({user: state.user});如果依赖 group,就传 state => ({group: state.group})

    学生:原来 redux 的 connect 的第一个参数 mapStateToProps是干这个的啊

    方:嗯,目前的代码先给你看看 damp-wave-1ufwd - CodeSandbox ,具体怎么实现 selector 下节课再讲

    学生:好的!

    待续……


    起源地下载网 » 面试官叫我手写 Redux - 2

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元