-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React Render 相关 #45
Comments
Post.js 中的 React的底层 Virtual Dom 机制就是当组件状态的 深度剖析:如何实现一个 Virtual DOM 算法 shouldComponentUpdate() Will Short-Circuit An Entire Subtree Of Components In ReactJS 最后我的想法是, 你的文章写得超级棒, 给你八个大拇指。 👍👍👍👍👍👍👍👍 非常希望能向你请教问题。 |
love you @LonelyLiar |
其实写了那么多很多都是废话。 简单的总结一下就是。 因为react-redux会在state更新的时候触发mapState。 react-redux里面组件更新的依据是selector的props是否一致,不一致就将shouldComponentUpdate的返回值设为true,然后组件就更新了。 这样子做很合理思路也是对的。问题出在了我们自己的写法上面。因为我们写的mapState每次都会返回一个新的对象。所以会一直导致更新。 所以我们需要用到reselect。 那么reselect做了什么咧。reselect只是简单的把我们的mapState做了一遍缓存,保证同样的state的情况下每次返回一样的对象仅此而已。 这样一来就不会触发组件的更新了。 |
render函数是必须要有的。
当render函数被调用的时候。函数应该检查
this.props
和this.state
然后返回下列类型中的其中一种:React elements。典型的创建方式是通过JSX。一个element可以被代表称一个原生的组件(例如
String或者numbers。通常是在渲染text节点在DOM里面的时候。
Portals。通过ReactDOM.createPortal创建的元素。 PS: 这个还蛮有用的。看这个例子。
null。什么都不渲染的时候。
Booleans。什么都不渲染的时候。(这个情况大多会发生在return test && 这种写法时候。)
不管是null或则和false。
ReactDOM.findDOMNode(this)
函数都会返回null。render函数应该是
纯
(pure)的,也就是说我们不应该在render函数里面修改组建的state。render函数应该每次执行的时候都返回相同的结果。它不直接与浏览器交互。如果你想与浏览器交互你应该在componentDidMount
或者其他的生命周期里面进行。保持render函数的纯度可以让组件更容易理解。非常需要注意的一点是。当
shouldComponentUpdate
方法返回false的时候render函数不会被执行。Fragments
React支持片段。片段指的是没有被容器包裹的元素。通常直接写的话会报错。但是这样写就不会了
但是要注意需要添加
key
噢 不然会有警告。不过 React16.2.0以后可以不写key了。但是你需要改变一下写法.
参考资料
TL;DR
实际上我写这篇的目的是为了理解Redux为何会频繁触发component。Reselect是如何解决这个问题的。后面发现触发更新的其实是react-redux。但是很显然上面的官方文档中我没有找到特别有用能帮助到我的东西。为了让大家所谓的性能问题。我这里写个例子。
组件部分
App.js
demo/index.js
demo/Post.js
demo/Counter.js
Redux部分
demo/store.js
demo/reducers.js
这个demo是视频里面的小哥提供的。我把它重现了出来。复制粘贴一下就好了。因为我不想单独开个仓库存代码所以贴在上面。如果你喜欢视频教程你可以直接去看小哥哥的视频。里面讲的很清楚。
这个代码很简单就是每500毫秒在Counter组件里面调用一下increment。触发Redux的状态变化。让counter状态不停的加1。另外在Post的render函数里面写了个console.log来确认是Post函数被调用了。
然后运行的结果是。明明Post里面没有用到counter变量却
被更新
了。打开控制台可以看见。不停的在输出。换句话说不停的在render。这很显然如果比较复杂的组件会带来性能问题。运行结果
Post.js 内心OS: MMP 为什么我在疯狂更新,管我卵事。
那好啊。为什么会这样咧。就是我要探究的问题。到底为什么render函数触发更新了,又为什么用了Reselect修改后的版本就不会触发更新了。Reselect对Redux做了什么,期间有什么py交易。以及Render函数运行的条件是我接下来关心的重点。
Render 函数运行的条件
首先最重要的是这个。在官方文档中我们知道当shouldComponentUpdate方法返回false的时候render函数不会被执行。那么是不是因为这个呢。我粗略的扫了一下redux和reselect的源码,源码里面都没有用到
shouldComponentUpdate
来控制组件的更新。于是看了下react-redux。ok很快就找了shouldComponentUpdate相关的代码。很明显react-redux通过shouldComponentUpdate做了什么,那我们粗略的看一下源码吧。PS: 吃饭的时候想了一下傻逼了为什么去看redux的源码。redux本身和react无关。
首先看项目结构。我只看src目录这里用了学长写的工具(
可能他已经忘掉了这个工具了)。一开始用的是cmder自带的那个tree,可是我发现体验极差。投奔学长。东西很少。三个目录compoent,connect,utils。component下面放的是组件,connect是核心的逻辑,utils是会用到的工具类。index.js统一暴露所有主要的接口。
先看看index.js
Ok 那就先看Provider吧。平时我们都是用包裹组件的。来看看里面写了什么。
很快我们注意到了Provider里面用了context api。prop接收stroe和children两个参数。children就是根元素啦。就是平时用的
this.props.children
。有一个warnAboutReceivingStore。会在改变store的时候在非生产模式的时候提供警告。Children.only方法是验证是否只有一个child用的。如果不是会报错。如果是的话就会渲染。Provider是createProvider默认参数的实现。那createProvider也不用看了。是可以给用户自定义的Provider(并没有用过)。createProvider有两个参数storeKey和subKey。不过第二个参数文档里面没有讲怎么用 算了 当作不知道。
最主要的部分应该是这两个部分。
现在只知道Provider组件把props.store记录到了this[storeKey]里面。还有getChildContext里面访问[store]的话可以读取到props.store。另外一个subscriptionKey总是为空。
这些东西接下来应该会用到。所以看第三个connectAdvanced吧。connectAdvanced也是组件。更加的复杂。
其实是注释好多因为好长 直接戳进去看吧嘛 首先看下依赖。hoist-non-react-statics和invariant都是我不知道的东西。好呀既然不知道那就查查看。一下就找到了这个是hoist-non-react-statis。嗯他叫我这个傻逼去看官方的解释。好吧我没有好好看文档。那就看看。这个是做什么的。
简单的说就是有时候给组件加静态方法超爽der。但是如果你用了hoc的话静态方法会丢失。虽然你可以手动将hoc的静态方法指向原组件的静态方法。但是那样太麻烦了。所以你可以用hoist-non-react-statis解决这个问题。简单而又足够高效。
好hoist-non-react-statics知道了那就看看invariant。手动戳进invariant的源码。发现很短有一行这样的注释。
嗯大概是用来处理抛出异常时候的一个函数。用法看起来也比较像所以不关心这个东西。看到别人有个copy版本。额 他的描述更清晰了。
剩下的两个工具类其中PropTypes没什么好说的提取了可以复用的PropType。Subscription的话比较重要。用来订阅Redux的状态的。因为我觉得这个比较重要点开来看看好了。
Emmmm . 英语废表示完全不理解。那就直接看代码吧。嗯 就是普通的订阅者模式。
通过createListenerCollection创建订阅者。createListenerCollection提供了一个闭包函数。有notify,subscribe之类常用的功能。current和next都是数组。每次notify的时候会更新一下current并执行队列里面的回调。OK 继续回到connectAdvanced。
很好往下看发现根本无法理解嘛。那我们就看Connect吧。看看Connect是怎么调用connectAdvanced的。Connect暴露的connect也就是我们平时用的connect方法。
嗯一下子就找到了。connectAdvanced是作为一个Hoc来使用的。也就是说只要我们用了connect。react-redux都会将原组件封装成hoc再暴露出来。这样就使得组件有能力访问redux。
connect的文档可以读一下。虽然平时我们只要state和disptch。
抛开这些东西现在。我们知道reselect是把selector传给第二个参数的,第二个参数是mapDispatchToProps。那么一切都将围绕着它。我们只关心和这个参数挂钩的地方就好了。
和这个挂钩的只有initMapDispatchToProps。
match函数的作用是迭代工厂方法,最终返回结果。Emmmmm 目的不明。暂时不影响阅读。接下去看有一个
戳进去看就到了
connectAdvanced
。反正目的是建一个HOC组件Connect
。然后这个Connect
组件实现了shouldComponentUpdate
方法。shouldComponentUpdate方法围绕着selector依赖着selector的shouldComponentUpdate方法。selector的shouldComponentUpdate长这样子。
最关键部分在那个if判断里面
nextProps !== selector.props || selector.error
。如果上次传的prop不一致就会把shouldComponentUpdate设为true。如果一致的话就会是false。虽然没认真找但应该有这么一段。所以总结一下就是react-redux使用高阶组件Connect来链接Redux。而Connect组件实现了shouldComponentUpdate方法,目的应该是想做优化的。使用Connect链接的组件在state更新的时候都会触发mapState方法。但是因为mapState方法每次返回的对象不一致导致
props
不一致。而组件被动的更新了。使用reselect以后,reselect会在selector返回结果不一致的情况下调用tranfrom函数。但是如果一致的话会返回上次计算的结果。所以组件不会被更新。
The text was updated successfully, but these errors were encountered: