本篇文章主要帮助大家了解下
技术栈相关的概念,以及为什么我们需要引入这些,他们能解决什么问题。
- react
没有加入虚拟
- vue1
,做服务端渲染很难,所以
- DOM
引入了虚拟
- vue2
的机制,而且由于
- DOM
的响应式原理,所以天然的就比
- vue2
的性能好,
- react
的更新是通过顶层组件的
- react
变化触发整个组件的重新渲染,而
- state
由于其是通过
- vue2
来进行数据管理,所以可以准确的定位到需要重新渲染的节点,避免了无效的
- getter/setter
- re-render
端支持不好,
- native
很厉害但是目前只有阿里用于生产环境,而
- weex
有着大量的成熟案例,如手机 QQ
- react native
小,用在移动端很合适
小的缺点很多,但是比较好克服,最大的缺点还是在于
端,如果想用
- native
实现
- riot
端的话,需要造轮子,写就是自己写一套
- native
,来进行
- Native Bridge
与
- js
通信,难度太大,需要引入
- Objective C
等高大上的东西(
- js引擎
基本上可以理解为一个浏览器内核,而且肯定是
- Native Bridge
写)。
- C++
一个线程为
引擎,执行打包好的
- js
,主线程负责
- js
绘制,
- UI
需要绘制
- js
时会向主线程发出一个命令,主线程接收到命令后执行相应的绘制逻辑,
- UI
执行的结果会经过层层回调通过
- Objective C
引擎传回给
- js
。
- js
利用
做数据管理的话,
- redux
的
- redux
会被放置到最顶层组件的
- store
中,也就是
- state
为我们提供的
- react-redux
组件。这样就是意味着每次
- Provider
发生变化就会重新渲染整个应用,也就是触发所有组件的
- store
方法,每次都会触发
- render
,但是这种大多是无意义的,这也是产生性能瓶颈的地方:
- diff
如下面代码:
- render() {
- const { child1Data, child2Data } = this.props.store;
- return (
- <div>
- <Child1 data="child1Data" />
- <Child2 data="child2Data" />
- </div>
- );
- }
假如只有
发生变化,而
- child1Data
并没有发生变化,理论上来说我们只想触发
- child2Data
的
- Child1
,但事实上我们同时会触发
- render
的
- Child2
,这次显然是无意义的,所以需要来解决这个问题。
- render
的生命周期函数中有一个
- react
,根据其返回的值来决定是否需要来触发组件的
- shouldComponentUpdate
,
- render
默认返回的是
- shouldComponentUpdate
,也就是无论什么情况都会触发
- true
,
- render
改善的就是这个生命周期,根据传入的
- purerender
和
- state
来进行简单的判断,从而决定是否需要进行
- props
,为什么说只是进行了简单的判断,来看其判断部分代码: (注:利用 connect 将组件与
- render
关联起来的容器不需要加
- redux
,因为这个工作
- purerender
已经替我们做好了)
- react-redux
- // is可以理解为Object.is()
- function shallowEqual(objA, objB) {
- if (is(objA, objB)) {
- return true;
- }
- // 非引用类型,且不相等直接返回
- if (typeof objA !== 'object' || objA === null ||
- typeof objB !== 'object' || objB === null) {
- return false;
- }
- const keysA = Object.keys(objA),
- keysB = Object.keys(objB);
- if (keysA.length !== keysB.length) {
- return false;
- }
- // 问题所在,仅仅是比较了第一层,假设引用没变,不会触发更新
- for (let i = 0; i < keysA.length; i++) {
- if (
- !hasOwnProperty.call(objB, keysA[i]) ||
- !is(objA[keysA[i]], objB[keysB][i])
- ) {
- return false;
- }
- }
- return true;
- }
注意:使用
时一定注意不要在
- react
函数中进行函数的
- render
,因为这样每次
- bind
中会有属性的引用改变,一定会触发更新
- props
对于一般的情况来说
已经足够,可以减少一些
- purerender
,但是不是很彻底,比如:
- re-render
- // 之前的数据:
- let person = {
- name: 'zp1996',
- age: 21
- };
- // 改变引用
- person = {
- name: 'zp1996',
- age: 21
- };
(注:上面场景我做了下简化,真实中可能是从服务器端获得的数据,比如帖子列表这种)
明显的引用发生了改变,所以会触发
,但是明显的是我的数据完全没有变化,根本不用进行
- re-render
。 - 情况二:
- diff
- const data = {
- person: {
- students: [{
- name: 'zp1996',
- age: 21
- }]
- }
- };
- // 加入一个新的学生,数据结构会变成这样
- {
- person: {
- students: [{
- name: 'zp1996',
- age: 20
- },
- {
- name: 'zpy',
- age: 21
- }]
- }
- }
假如是这种情况,引用根本没有发生变化,所以就不会触发
,每次改变一个小的地方,就需要将整个的数据重新生成一个,这样造成了内存的不必要的浪费。
- re-render
很容易想到的是在
中进行深度比较,用递归的方式来进行比较,这样的代价同样很大,并不是一个有效的解决方案。为了解决这个问题,需要引入另一个库——
- shouldComponentUpdate
,其思想是强调不可变数据,一个
- immutable
的创建就是一个不可变的,需要变化时不是利用深拷贝,而是仅仅改变这个变化的节点和其父节点,其余节点仍是共享内存。同样的这样的一个强大的框架也是非常大,压缩过后仍然有
- Immutable Data
。
- 50k
我们希望的是
中保持简单,从服务端请求回来的数据直接存在
- reducer
中,看个例子:
- store
- // 从服务端拉回来的数据
- {
- students: {
- 'id_111': {
- name: 'zp1996',
- age: 21
- }
- }
- }
- // 最终组件希望我们传入这样的数据
- {
- students: [{
- name: 'zp1996',
- age: 21,
- id: 'id_111'
- }]
- }
- // 一般会在connect中对数据进行整理
- const mapStateToProp = state => {
- const { students } = state,
- res = [];
- for (let key in students) {
- if (students.hasOwnProperty(key)) {
- let obj = students[key];
- obj[id] = key;
- res.push(obj);
- }
- }
- return res;
- };
- @connect(
- mapStateToProp, // 被叫做selector
- );
每次
变化后,也就是执行一次
- store
之后都会执行利用
- dispatch
方法注册的回调(注册的回调就是
- subscribe
的第一个参数,也就是
- connect
),这样就意味着,尽管
- selector
并没有发生变化还是会触发一次数据结构的重整,这种显然是一种浪费,所以这个过程也需要优化:
- students
强调的是函数式编程,对于函数式编程来说有一个很明显的特点就是易于缓存,对于一个函数而言,给定相同的输入肯定会得到相同的输出,而
- redux
也全部为纯函数,同时
- selector
的
- connect
参数也支持返回一个函数。
- mapStateToProp
库就是这个思想,先来看看基本用法:
- reselect
- // createSelector的最后一个参数作为计算函数
- const state = { num: 10 },
- selector = state => state.num,
- reSelector = createSelector(
- selector,
- a => {
- console.log('被调用了')
- return a * a;
- }
- );
- console.log(reSelector(state)); // 第一次计算
- console.log(reSelector(state)); // 拿缓存
- console.log(reSelector(state)); // 拿缓存
- state.num = 100;
- console.log(reSelector(state)); // 值发生改变,计算
- console.log(reSelector(state)); // 拿缓存
- console.log(reSelector(state)); // 拿缓存
实现也是非常简单,就是对传入的参数进行判断,如果与之前一样则直接返回结果
- function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
- let lastArgs = null,
- lastResult = null;
- const isEqualToLastArg = (value, index) => equalityCheck(value, lastArgs[index]);
- return (...args) => {
- // 检测输入是否相等,不等或者第一次执行的话执行函数,反之拿之间的结果
- if (
- lastArgs === null ||
- lastArgs.length !== args.length ||
- !args.every(isEqualToLastArg)
- ) {
- lastResult = func(...args);
- }
- lastArgs = args;
- return lastResult;
- };
- }
会引入很多的概念,同时代码量也会很多,而
- redux
要更为简单。
- mobx
给我的感觉就像是把
- mobx
的响应式数据那一套给拿了出来,给了我们极大的自由度,可以利用
- vue
那一套来建立模型,而用
- OOP
必须利用
- redux
的那些套路写代码;同时在性能上也会有一些提升。但是同时也会带来很大的问题:
- redux
满天飞是避免不了的,但是提供了一个
- state
模式,要求数据必须利用
- strict
来进行更改,但是我用了之后发现并没有什么作用,而且组件外是可以随意更改数据的。 还有一个小问题就是热更新,根本没有找到热更新的解决方案,每次还得手动刷新页面。
- action
还有一块很重要的部分,那就是异步处理,目前本人只用过
- redux
,所以关于这个方面没(水)有(平)讲(不)到(够),同时哪里有错误也请大家指出。
- redux-thunk
来源: http://www.bubuko.com/infodetail-1963957.html