17. class和createClass的比较
一个调用React.createClass并传入一个对象,另一个则是使用class继承React.Component
var InputControlES5 = React.createClass({ propTypes: { initialValue: React.PropTypes.string }, defaultProps: { initialValue: '' }, // Set up initial state getInitialState: function() { return { text: this.props.initialValue || 'placeholder' }; }, handleChange: function(event) { this.setState({ text: event.target.value }); }, render: function() { return ( <div> Type something: <input onChange={this.handleChange} value={this.state.text} /> </div> ); } }); Copy
18. 谈一下Vue 和 React区别?
- 改变数据方式不同,Vue 修改状态相比来说要简单许多,React 需要使用 setState 来改变状态,并且使用这个 API 也有一些坑点。
- Vue 的底层使用了依赖追踪,页面更新渲染已经是最优的了,但是 React 还是需要用户手动去优化这方面的问题。
- React 需要使用 JSX,Vue 使用了模板语法
19. 高阶组件 HOC (higher order component)
高阶组件是一个以组件为参数并返回一个新组件的函数。
- HOC 允许你重用代码、逻辑和引导抽象。
- 最常见的可能是 Redux 的 connect 函数。
- 除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。
- 如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。
function add(a, b) { return a + b } Copy
现在如果我想给这个 add 函数添加一个输出结果的功能,那么你可能会考虑我直接使用 console.log 不就实现了么。说的没错,但是如果我们想做的更加优雅并且容易复用和扩展,我们可以这样去做:
function withLog (fn) { function wrapper(a, b) { const result = fn(a, b) console.log(result) return result } return wrapper } const withLogAdd = withLog(add) withLogAdd(1, 2) Copy
这个做法在函数式编程里称之为高阶函数,大家都知道 React 的思想中是存在函数式编程的,高阶组件和高阶函数就是同一个东西。我们实现一个函数,传入一个组件,然后在函数内部再实现一个函数去扩展传入的组件,最后返回一个新的组件,这就是高阶组件的概念,作用就是为了更好的复用代码。
20. 谈一下React的事件机制
React 其实自己实现了一套事件机制,首先我们考虑一下以下代码
const Test = ({ list, handleClick }) => ({ list.map((item, index) => ( <span onClick={handleClick} key={index}>{index}</span> )) }) Copy
- 事实当然不是,JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了
document上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件。 - 另外冒泡到 document 上的事件也不是原生浏览器事件,而是React自己实现的合成事件(SyntheticEvent)。因此我们如果不想要事件冒泡的话,调用 event.stopPropagation 是无效的,而应该调用 event.preventDefault。
- 那么实现合成事件的目的好处有两点,分别是:
- 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力
- 对于原生浏览器事件来说,浏览器会给监听器创建一个事件对象。如果你有很多的事件监听,那么就需要分配很多的事件对象,造成高额的内存分配问题。但是对于合成事件来说,有一个事件池专门来管理它们的创建和销毁,当事件需要被使用时,就会从池子中复用对象,事件回调结束后,就会销毁事件对象上的属性,从而便于下次复用事件对象。
21. redux简介
redux 是一个应用数据流框架,主要是解决了组件间状态共享的问题,原理是集中式管理,主要有三个核心方法,action,store,reducer
工作流程是:
- view用actionCreator创建一个action,里面可能包含一些数据
- 使用store的dispatch方法将acion传入store
- store将action与旧的state转发给reducer
- reducer深拷贝state,并返回一个新的state给store
- store接收并更新state
- 使用store.subscribe订阅更新,重新render组件
21.1 reducer为什么是纯函数?
Note
从本质上讲,纯函数的定义如下:不修改函数的输入值,依赖于外部状态(比如数据库,DOM和全局变量),同时对于任何相同的输入有着相同的输出结果。
21.2 原理解析
- 阅读源码可以看到,Redux接收一个给定的state(对象),然后通过循环将state的每一部分传递给每个对应的reducer。如果有发生任何改变,reducer将返回一个新的对象。如果不发生任何变化,reducer将返回旧的state。
- Redux只通过比较新旧两个对象的存储位置来比较新旧两个对象是否相同(也就是Javascript对象浅比较)。如果你在reducer内部直接修改旧的state对象的属性值,那么新的state和旧的state将都指向同一个对象。因此Redux认为没有任何改变,返回的state将为旧的state。
- 深比较在真实的应用当中代价昂贵,因为通常js的对象都很大,同时需要比较的次数很多。
- 因此一个有效的解决方法是作出一个规定:无论何时发生变化时,开发者都要创建一个新的对象,然后将新对象传递出去。同时,当没有任何变化发生时,开发者发送回旧的对象。也就是说,新的对象代表新的state。 使用了新的策略之后,你能够比较两个对象通过使用!==比较两个对象的存储位置而不是比较两个对象的所有属性。
22. VDOM是什么?为什么会存在VDOM?
22.1 VDom的产生原因
- 非常耗费性能的。而且JS操作DOM是非常复杂,JS操作DOM越多,控制与页面的耦合度就越高,代码越难以维护。
- 虚拟DOM,即用JS对象来描述DOM树结构,Diff算法则是找旧VDOM与新的VDOM的最小差异,然后再把差异渲染出来
22.2 VDOM的组成
- tag 标签名
- attrs DOM属性键值对
- childen DOM字节点数组 或 文本内容
22.3 为什么DOM操作慢? 因为属性太多了
22.4 vdom如何应用,核心API是什么?
- 创建虚拟节点
- h('标签名', {...属性...}, [...子元素...])
- h('标签名', {...属性...}, '文本内容')
- 将VNode添加到一个DOM元素内
- patch(DOM_obj, vnode);
- 用一个新的vnode来和旧的vnode进行比较,得出新旧dom的差异
- patch(vnode, newVnode)
23. diff算法
23.1 对比Vdom树差异的算法
23.2 同层比对
新旧状态的比对时采用同层比对,当发现某节点不一致了直接替换该节点的子树。而不管它的子树是不是真的改动了。
23.3 key值的使用
- 在列表循环的时候React会要求每一个列表项有一个独一无二,稳定的key值,它的目的是为了当状态改变时新旧状态的每一个列表项能够对应起来,方便比对。
- Key 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
- Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系
23.4 合并操作
调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制
23.5 diff算法的流程
- tree diff 新旧两棵DOM树,逐层对比的过程,就是 Tree Diff; 当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到;
- component diff 在进行Tree Diff的时候,每一层中,组件级别的对比,叫做 Component Diff;
- 如果对比前后,组件的类型相同,则暂时认为此组件不需要被更新;
- 如果对比前后,组件类型不同,则需要移除旧组件,创建新组件,并追加到页面上;(重新创建并追加到页面上去)
- element diff 在进行组件对比的时候,如果两个组件类型相同,则需要进行 元素级别的对比,这叫做 Element Diff;
24. Vue和React中key的作用?(面试重点)
- 两个相同的组件产生类似的DOM结构,不同的组件产生不同的DOM结构。
- 同一层级的一组节点,他们可以通过唯一的id进行区分。
基于以上这两点假设,使得虚拟DOM的Diff算法的复杂度从O(n^3)降到了O(n)。
- 当页面的数据发生变化时,Diff算法只会比较同一层级的节点:
- 如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点了。
- 如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新。
- 当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程默认情况下也是遵循以上原则。
Note
所以一句话,key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。