react redux学习之路

chapter one React新的前端思维方式

React的首要思想是通过组件(Component)来开发应用。所谓组件,简单说,指的是能够完成某个特定功能的独立的、可重用的代码。

基于组件的应用开发是广泛使用的软件开发模式,用分而治之的方法,把一个大的应用分解成若干的小组件,每个组件只关注某个小范围的特定功能,但是把组件组合起来,就能构成一个功能庞大的应用。如果分解功能的过程足够巧妙,那么每个组件可以在不同场景下重用,那样不光可以构建庞大的应用,还可以构建出灵活的应用。打个比方,每个组件是一块砖,而一个应用是一座楼,想要一次锻造就构建一座楼是不现实的。实际上,总是先锻造很多砖,通过排列组合这些砖,才能构建伟大的建筑。

React非常适合构建用户交互组件。

jsx 是进步还是退步?

多年来,业界一直流行三次分离,即html(结构)、css(表现)、js(逻辑)分离,一般会写在不同的文件之中。

而jsx把类似HTML的标记语言和javascript混在一起了,但是,随着时间的推移,业界分离的认识有了改变,将html,css,js这三种语言分在三种不同的文件里面,实际上是把不同技术分开管理了,而不是逻辑上的“分而治之”。

html之中的onclick与jsx之中的onClick的区别:

  • onclick添加的时间处理函数是在全局环境下执行的,这污染了全局环境,很容易产生意料不到的后果;
  • 给很多DOM袁术添加onclick时间,可能会影响网页的性能,毕竟,网页需要的时间处理函数越多,性能就会越低;
  • 对于使用onclick的DOM元素,如果要动态地从DOM树中删除的话,需要把对应的时间处理器注销,加入忘了注销,就可能造成内存泄露,这样的bug,很难被发现。

在jsx之中,onClick挂载的每个函数,都可以控制在组件范围内,不会污染全局空间。

我们在jsx中看到了一个组件使用了onClick,但并没有产生直接使用onclick的html,而是使用了事件委托(event delegation)的方式处理点击事件,无论有多少个onClick出现,其实最后都只在DOM树上添加了一个事件处理函数,挂载在最顶层的DOM节点上。所有的点击事件都被这个事件处理函数捕获,然后根据具体组件分配给特定函数,使用事件委托的性能当然比每个onClick都挂载一个事件处理函数要高。

因为React控制了组件的生命周期,我们还可以在React组件中定义样式。

分解Reac应用

React的理念,归结为一个公式,就像下面这样:

UI = render(data)

用户看到的界面(UI),应该是一个函数render执行结果,只接受数据(data)作为参数。这个函数是一个纯函数,所谓纯函数,指的的是没有任何副作用,输出完全依赖于输入的函数,两次函数调用如果输入相同,得到的结果也绝对相同。如此一来,最终的用户界面,在render函数确定的情况下完全取决于输入数据。

对于开发者来说,重要的是区分开那些是属于data,哪些属于render,想要更新用户界面,要做的就是更新data,用户界面自然会做出相应,所以react实际的也是“响应式编程”(reactive programming)的思想,这也就是react为什么叫做react的原因。

Virtual Dom

既然react应用就是通过重复渲染来实现用户交互,你可能会有一个疑惑:这样的重复渲染会不会效率太低了呢?在jQuery的实现方式中,我们可以清楚地看到每次只有需要变化的哪一个DOM元素被修改了;可是,在react的实现方式中,看起来每次render函数被调用,都需要把整个组件重新绘制一次,这样看起来有点浪费。

事实并不是这样,react利用virtual dom,让每次渲染都只重新渲染最少的DOM元素。要了解virtual dom,就要先了解dom,dom是结构话文本的抽象表达式,特定于web环境中,这个结构化文本就是html文本,html中的每个元素都对应dom中某个节点,这样,因为html元素的逐级包含关系,dom节点自然就构成了一个树形结构,成为DOM树。

浏览器为了渲染html格式的网页,会先将html文本解析以构建dom树,然后根据dom树渲染出用户看到的界面,当要改变界面内容的时候,就去改变dom树上的节点。

web前端开发关于性能优化有一个原则:尽量减少DOM操作。虽然dom操作也只式一些简单的javascript语句,但是dom操作会引起浏览器对网页进行重新布局,重新绘制,这就是一个比javascript语句执行慢很多的过程。

如果使用mustache或者hogan这样的模板工具,那就是生成html字符串赛到网页中,浏览器又要做一次解析生产新的DOM节点,然后替换DOM树上对应的子树部分,这个过程的肯定效率不高。虽然jsx看起来很像是一个模板,但是最终会被babel解析为一条条创建react组件或者html元素的语句,神奇之处在于,react并不是通过这些语句直接构建DOM树,而是首先构建virtual dom。

既然dom树是对html的抽象,那virtual dom就是对dom树的抽象。virtual dom不会触及浏览器部分,只是存在于javascript空间的树形结构,每次自上而下渲染react组件时,会对比这一次产生的virtual dom和上一次渲染的virtual dom,对比就会发现差别,然后修改真正的dom树时就只需要初级差别中的部分就行。

jquery的方式直观易懂,对于初学者十分适用,但是当项目逐渐变得庞大时,用jquery写出的代码往往互相纠缠,不同的事件直接修改dom元素。

使用react的方式,就可以避免构建这样复杂的程序结构,无论何种事件,引发的都是react组件的重新渲染,至于如何只修改必要的dom部分,则完全交给react去操作,开发者并不需要关系。react利用函数式编程的思维来解决用户界面渲染的问题,最大的优势式开发者的效率会大大提高,开发出来的代码可维护性和可阅读性也大大增强。

chapter two 设计高质量的react组件

作为一个合格的开发者,不要只满足于编写出了可以运行的代码,而要了解代码背后的工作原理;不要只满足于自己编写的程序能够运行,还要让自己的代码可读而且易于维护。这样才能开发出高质量的软件。

作为软件设计的通则,组件的划分要满足高内聚(high cohesion)低耦合(low coupling)的原则。

高内聚指的式把逻辑紧密的相关内容放在一个组件中。用户界面无外乎内容、交互行为和样式。传统上,内容由html表示,交互行为放在javascript代码文件中,样式放css文件中定义。这虽然满足一个功能模块的需要,却要放在三个不同的文件中,这其实不满足高内聚的原则。react却不是这样,展示内容的jsx、定义行为的javascript代码,甚至定义样式的css,都可以放在一个javascript文件中,因为他们本来就是为了实现一个具体的目的而存在的,所以说react天生具有高内聚的特点。

低耦合指的式不同组件之间的依赖关系要尽量弱化,也就是每个组件要尽量独立。保持整个系统的低耦合度,需要对系统中的功能有充分的认识,然后根据功能点划分模块,让不同的组件去实现不同的功能,这个功夫还在开发者身上,不过,react组件的对外接口非常规范,方便开发者设计低耦合的系统。

react组件的数据

"差劲的程序要操心代码,优秀的程序要操心数据结构和他们之间的关系。"

react组件的数据分为两种,prop和state,无论prop或者state的改变,都可能引发组件的重新渲染,那么,设计一个组件的时候吗,什么时候选择prop什么时候选择用state呢?原则很简单,prop是组件对外的接口,state是组件内部的状态,对外用prop,内部用state。

每个react组件都是独立存在的模块,组件之外的一切都是外部世界,外部世界就是通过prop来和组件对话的。

当prop的类型不是字符串类型时,在jsx中必须使用花括号{}把prop值包住,所以style的值有两层花括号,外层花括号代表是jsx的语法,内层的花括号代表这是一个对象常量。

react中的prop

prop 传值分析:

class Counter extends Component {
    constructor(props){
        super(props);

        this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
        this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

        this.state = {
            count:props.initValue || 0;
        }
    }       
}

如果一个组件需要定义自己的构造函数,一定要记得在构造函数的第一行通过super调用父类也就是React.Component的构造函数。如果在构造函数中没有调用super(props),那么组件实例被构造之后,类实例的所有成员函数就无法通过this.props访问到父组件传递过来的props值。很明显,给this.props赋值是React.Component构造函数的工作之一。

在Counter的构造函数中还给哦i昂贵成员绑定了当前this的执行环境,因为es6方法构造的react组件类并不自动给我们绑定this到当前实例对象。在构造函数的最后,我们可以看到读取传入prop的方法,在构造函数中可以通过参数props获得传入prop值,在其他函数中则可以通过this,props访问传入的prop的值,比如在counter组件的render函数中,我们就是通过this.props获得传入的caption ,render函数代码如下:

render(){
    const { caption } = this.props;
    return <div>
                <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
                <button style={buttonStyle} onClick={this.onClickDecremnetButton}>-</button>
                <span>{caption} count:{ this.state.count } </span>
           </div> 
}

react的state

驱动组件渲染过程的除了prop,还有state,state代表组件的内部状态。由于react组件不能修改传入的prop,所以需要i记录自身数据变化,就要使用state。

通常在组件类的构造函数结尾出初始化state,在counter构造函数中,通过this.state的赋值完成了对组件state的初始化。代码如下:

constructor(props){
    // ...
    this.state = {
        count:props.initValue || 0;
    }
}

于2017.10.2

differences between react element and react component

react elements are plain objects.They describe what you want to see on the screen.React elements are inmutable .Once you create an element,you can't change its children or attributes.An element is like a single frame in a movie :it represents the UI at a certain point in time.

react components are like Javacript functions.they accept arbitrary inputs (called "props") and return react elements describing what should appear on the screen.

functional and class components

React componets must act like pure functions with repect to their props.

Locak state is exactly that:a feature available only to a Class.

什么是组件

能够完成某个特定功能的独立可重用的代码。基于组件是广泛使用的软件开发模式,是分而治之的方法。把大的应用分解成为若干个小的组件,每个组件专注某个特定的功能。把组件合起来就能够成为一个功能庞大的应用。如果组件功能分解足够巧妙,每个组件可以在不同的场景下重用,可以构建出庞大灵活的应用。

prop

当外部世界传递一些数据给React组件,一个最直接的方式就是通过prop;同样React组件要反馈数据给外部数据,也可以用prop,因为Prop的类型限于纯数据,也可以是函数,函数类型的prop等于让父组件交给子组件一个毁掉函数,子组件在恰当的实际调用函数的类型prop,可以带上必要的参数,这样就可以反过来把信息传递给外部的世界。

哈哈:看到这里,不禁想到了一个例子,一个也是在别处调用的情况,就是常见的跨域方式,如jsonp,script标签请求的数据就返回就是回掉一个之前已经定义好的函数。对于后端的数据处理来说,是相当于一个黑匣子。对于父子组件而言,也相当一个黑匣子里面调用数据。两个例子都是一个回掉函数的作怪。常言到,函数是代码的分割,此话可当真