- 合成事件的绑定方式
`<button onClick={this.handleClick}>Test</button>`
- 合成事件的实现机制:事件委派和自动绑定。
- React合成事件系统的委托机制,在合成事件内部仅仅是对最外层的容器进行了绑定,而且依赖事件的冒泡机制完成了委派。
- React受控组件更新state的流程:
- 能够经过在初始state中设置表单的默认值。
- 每当表单的值发生变化时,调用onChange事件处理器。
- 事件处理器经过合成事件对象e拿到改变后的状态,并更新应用的state。
- setState触发视图的从新渲染,完成表单组件值的更新。
- 受控组件和非受控组件的最大区别是:非受控组件的状态并不会受应用状态的控制,应用中也多了局部组件状态,而受控组件的值来自于组件的state。
- CSS模块化遇到了哪些问题?全局污染,命名混乱,依赖管理不完全,没法共享变量,代码压缩不完全。
- CSS Modules模块化方案:启用CSS Modules,样式默认局部,使用composes来组合样式。
- 子组件向父组件通讯
- 利用回调函数
- 利用自定义事件机制
- 当须要让子组件跨级访问信息时,咱们还可使用context来实现跨级父子组件间的通讯。
- 没有嵌套关系的组件通讯:咱们在处理事件的过程当中须要注意,在componentDidMount事件中,若是组件挂载完成,再订阅事件;当组件卸载的时候,在componentWillUnmount事件中取消事件的订阅。
- mixin 的目的,就是为了创造一种相似多重继承的效果,或者说,组合。实际上,包括C++等一些年龄较大的OOP语言,都有一个强大可是危险的多重继承特性。现代语言权衡利弊,大都舍弃了它,只采用单继承。可是单继承在实现抽象的时候有不少不便,为了弥补缺失,Java引入接口(interface),其余一些语言则引入了mixin的技巧。
封装mixin方法
方法:cssconst mixin = function(obj, mixins) { const newObj = obj; newObj.prototype = Object.create(obj.prototype); for (let prop in mixins) { if (mixins.hasOwnProperty(prop)) { newObj.prototype[prop] = mixins[prop]; } } return newObj; }应用:react
const BigMixin = { fly: () => { console.log('I can fly'); } }; const Big = function() { console.log('new big'); }; const FlyBig = mixin(Big, BigMixin); const flyBig = new FlyBig(); // => 'new big' flyBig.fly(); // => 'I can fly'上面这段代码实现对象混入的方法是:用赋值的方式将mixin对象里的方法都挂载到原对象上。数组
- 在React中使用mixin
React在使用createClass构建组件时提供了mixin属性,好比官方封装的:PureRenderMixin。浏览器
import React from 'react'; import PureRenderMixin from 'react-addons-pure-render-mixin'; React.createClass({ mixins: [PureRenderMixin], render() { return <div>foo</div>; } });在createClass对象参数中传入数组mixins,里面封装了咱们须要的模块。mixins数组也能够添加多个mixin。同时,在React中不容许出现重名普通方法的mixin。而若是是生命周期方法,则React将会将各个模块的生命周期方法叠加在一块儿而后顺序执行。
使用createClass实现的mixin为组件作了两件事:安全
- 工具方法:这是mixin的基本功能,若是但愿共享一些工具类的方法,就能够直接定义它们而后在组件中使用。
- 生命周期继承,props和state合并。mixin可以合并生命周期方法。若是有不少mixin来定义componentDidMount这个周期,那么React会很机智的将它们都合并起来执行。一样,mixin也能够做state和props的合并。
- ES6 Classes和decorator
然而,当咱们使用ES6 classes的形式构建组件的时候,却并不支持mixin。为了使用这个强大的功能,咱们还须要采起其余方法,来达到模块重用的目的。可使用ES7的语法糖decorator来实现class上的mixin。core-decorators库为开发者提供了一些实用的decorator, 其中也正好实现了咱们想要的@mixin。性能优化
import React, { Component } from 'React'; import { mixin } from 'core-decorators'; const PureRender = { shouldComponentUpdate() {} }; const Theme = { setTheme() {} }; @mixin(PureRender, Theme) class MyComponent extends Component { render() {} }mixin的问题数据结构
- 破坏了原有组件的封装:mixin会混入方法,给原有的组件带来新特性。但同时它也可能带来新的state和props,这意味着组件有一些“不可见”的状态须要咱们去维护。另外,mixin也有可能去依赖其余的mixin,这样会创建一个mixin的依赖链,当咱们改动一个mixin的状态,颇有可能也会影响其余的mixin。
- 命名冲突
- 增长复杂性
针对这些困扰,React提出的新的方式来取代mixin,那就是高阶组件。app
- 高阶组件
若是已经理解高阶函数,那么理解高阶组件也很容易的。高阶函数:就是一种这样的函数,它接受函数做为参数输入,或者将一个函数做为返回值。例如咱们常见的方法map, reduce, sort等都是高阶函数。高阶组件和和高阶函数很相似,高阶组件就是接受一个React组件做为参数输入,输出一个新的React组件。高阶组件让咱们的代码更具备复用性、逻辑性与抽象性,它能够对render方法做劫持,也能够控制props和state。
实现高阶组件的方法有以下两种:框架
- 属性代理:高阶组件经过被包裹的React组件来操做props。
- 反向继承:高阶组件继承于被包裹的React组件。
属性代理
示例代码:lessimport React, { Component } from 'React'; const MyContainer = (WrappedComponent) => class extends Component { render() { return <WrappedComponent {...this.props} />; } }在代码中咱们能够看到,render方法返回了传入的WrappedComponent组件。这样,咱们就能够经过高阶组件来传递props。这种方式就是属性代理。
如何使用上面这个高阶组件:import React, { Component } from 'React'; class MyComponent extends Component { // ... } export default MyContainer(MyComponent);这样组件就能够一层层的做为参数被调用,原始组件久具有了高阶组件对它的修饰。这样,保持单个组件封装的同时也保留了易用行。
从功能上, 高阶组件同样能够作到像mixin对组件的控制:
- 控制props
咱们能够读取、增长、编辑或是移除从WrappedComponent传进来的props。
例如:新增propsimport React, { Component } from 'React'; const MyContainer = (WrappedComponent) => class extends Component { render() { const newProps = { text: newText, }; return <WrappedComponent {...this.props} {...newProps} />; } }注意:
<WrappedComponent {...this.props}/> // is equivalent to React.createElement(WrappedComponent, this.props, null)这样,当调用高阶组件的时候,就可使用text这个新的props了。
- 经过refs使用引用
- 抽象state
高阶组件能够讲原组件抽象为展现型组件,分离内部状态。
const MyContainer = (WrappedComponent) => class extends Component { constructor(props) { super(props); this.state = { name: '', 4 }; this.onNameChange = this.onNameChange.bind(this); } onNameChange(event) { this.setState({ name: event.target.value, }) } render() { const newProps = { name: { value: this.state.name, onChange: this.onNameChange, }, } return <WrappedComponent {...this.props} {...newProps} />; } }在这个例子中,咱们把组件中对name prop 的onChange方法提取到高阶组件中,这样就有效的抽象了一样的state操做。
使用方式@MyContainer class MyComponent extends Component { render() { return <input name="name" {...this.props.name} />; } }反向继承
const MyContainer = (WrappedComponent) => class extends WrappedComponent { render() { return super.render(); } }
性能优化的思路
影响网页性能最大的因素是浏览器的重排(repaint)和重绘(reflow)。React的Virtual DOM就是尽量地减小浏览器的重排和重绘。从React渲染过程来看,如何防止没必要要的渲染是解决问题的关键。
性能优化的具体办法
- 尽可能多使用无状态函数构建组件
无状态组件只有props和context两个参数。它不存在state,没有生命周期方法,组件自己即有状态组件构建方法中的render方法。在合适的状况下,都应该必须使用无状态组件。无状态组件不会像React.createClass和ES6 class会在调用时建立新实例,它建立时始终保持了一个实例,避免了没必要要的检查和内存分配,作到了内部优化。
- 拆分组件为子组件,对组件作更细粒度的控制
相关重要概念:纯函数
纯函数的三大构成原则:
- 给定相同的输入,它老是返回相同的输出: 好比反例有 Math.random(), New Date();
- 过程没有反作用:即不能改变外部状态;
- 没有额外的状态依赖:即方法内部的状态都只能在方法的生命周期内存活,这意味着不能在方法内使用共享的变量。
纯函数很是方便进行方法级别的测试及重构,它可让程序具备良好的扩展性及适应性。纯函数是函数式变成的基础。React组件自己就是纯函数,即传入指定props获得必定的Virtual DOM,整个过程都是可预测的。
具体办法
拆分组件为子组件,对组件作更细粒度的控制。保持纯净状态,可让方法或组件更加专一(focus),体积更小(small),更独立(independent),更具备复用性(reusability)和可测试性(testability)。
- 运用PureRender,对变动作出最少的渲染
相关重要概念: PureRender
PureRender的Pure便是指知足纯函数的条件,即组件被相同的props和state渲染会获得相同的结果。在React中实现PureRender须要从新实现shouldComponentUpdate生命周期方法。shouldComponentUpdate是一个特别的方法,它接收须要更新的props和state,其本质是用来进行正确的组件渲染。当其返回false的时候,再也不向下执行生命周期方法;当其返回true时,继续向下执行。组件在初始化过程当中会渲染一个树状结构,当父节点props改变的时候,在理想状况下只需渲染一条链路上有关props改变的节点便可;可是,在默认状况下shouldComponentUpdate方法返回true,React会从新渲染全部的节点。
有一些官方插件实现了对shouldComponentUpdate的重写,而后本身也能够作一些代码的优化来运用PureRender。具体办法
- 运用PureRender
使用官方插件react-addons-pure-render-mixin实现对shouldComponentUpdate的重写
import React from 'react'; import PureRenderMixin from 'react-addons-pure-render-mixin'; class App extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); } render() { return <div className={this.props.className}>foo</div> } }它的原理是对object(包括props和state)作浅比较,即引用比较,非值比较。好比只用关注props中每个是否全等(若是是prop是一个对象那就是只比较了地址,地址同样就算是同样了),而不用深刻比较。
- 优化PureRender
避免不管如何都会触发shouldComponentUpdate返回true的代码写法。避免直接为prop设置字面量的数组和对象,就算每次传入的数组或对象的值没有变,但它们的地址也发生了变化。
如如下写法每次渲染时style都是新对象都会触发shouldComponentUpdate为true:<Account style={{color: 'black'}} />改进办法:将字面量设置为一个引用:
const defaultStyle = {}; <Account style={this.props.style || defaultStyle} />避免每次都绑定事件,若是这样绑定事件的话每次都要生成一个新的onChange属性的值:
render() { return <input onChange={this.handleChange.bind(this)} /> }该尽可能在构造函数内进行绑定,若是绑定须要传参那么应该考虑抽象子组件或改变现有数据结构:
constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange() { ... } render() { return <input onChange={this.handleChange} /> }在设置子组件的时候要在父组件级别重写shouldComponentUpdate。
- 运用immutable
JavaScript中对象通常是可变的,由于使用引用赋值,新的对象的改变将影响原始对象。为了解决这个问题是使用深拷贝或者浅拷贝,但这样作又形成了CPU和内存的浪费。Immutable data很好地解决了这个问题。Immutable data就是一旦建立,就不能再更改的数据。对Immutable对象进行修改、添加或删除操做,都会返回一个新的Immutable对象。Immutable实现的原理是持久化的数据结构。即便用旧数据建立新数据时,保证新旧数据同时可用且不变。同时为了不深拷贝带来的性能损耗,Immutable使用告终构共享(structural sharing),即若是对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其余节点则进行共享。
jest 是 facebook 开源的,用来进行单元测试的框架,功能比较全面,测试、断言、覆盖率它均可以,另外还提供了快照功能。 对测试群众来讲,从质量保证的角度出发,单元测试覆盖率100%是否就足够了呢?确定不够啊! 结合实际的项目经验来看,jest的测试还能够根据产品的实际需求,作一些诸如: 点击某个页面元素后,须要在页面上显示新的区块,而且要加载指定的的css的测试; 点击某个link,须要跳转到指定的网站的测试; 等等 这些测试本来在UI自动化功能测试中也比较常见,这里咱们均可以把它们挪到低层中去。因此具体的测试用例,在单元测试覆盖率超级高的前提下,咱们测试的群众还能够跟研发结对完成。或者指导研发完成,要不干脆本身加上去算了。另外,产品的功能性测试完成的状况下,咱们还须要考虑下非功能性的问题,例如兼容性、性能、安全性等。再加上测试金字塔的顶端之上,其实还有探索性测试的位置。产品的基本功能由单元测试保障了,剩下的时间,咱们能够作更多的探索性测试了不是吗?总之,干掉UI自动化功能测试只是一个加速测试反馈周期、减小投入成本的尝试。软件的质量不只仅是测试攻城狮的事情,而是整个团队的责任。坚持一些重要的编码实践,好比state less的组件、build security in等,也是提升质量的重要手段。