七周七种前端框架二: React 之概览

React的思想

React 的三个特点:

  • 单向数据流(而不是双向数据绑定)
  • 只关心MVC 中的View,所以组织大规模应用还需要 flux 之类的框架
  • Virtual DOM,对DOM的一层抽象

React 的特点就是简单,Simple。所以它只关心View,而整个MVC的架构需要借助其他框架实现。

因为没有双向数据绑定,所以React的组件状态非常容易保持,只要确定了 state 和 props ,同一个类创建出来的实例应该都是一样的(虽然这不是强制的)。

不过同时也导致使用 React 不像 Angular 那么方便,需要自己去监听各种事件来更新自己的State。

如果说 Angular 开创了一个双向数据绑定的时代,解决了数据和视图同步的问题,那么 React 可以说是开创了一个 Virtual DOM的时代,解决了跨平台UI的问题。

Virtual DOM 可以说完全是为了跨平台考虑,可以把它看做一个UI 接口,虽然是一层简单的DOM抽象,但是确实做到了从直接操作DOM 到 操作抽象的Virtual DOM。从操作实现类到操作接口,是质变。Virtual DOM 既可以编译成HTML,也完全可以编译成 Java 和 OC,并且写法没有太多区别。

之前写过一个React系列博客,感兴趣的可以翻翻我之前的博客。 今天再看的时候发现 React 已经更新到了 0.14 ,并且把 ReactDOM 独立出来了。

JSX 和 Virtual DOM

JSX 虽然在 React 体系中并不重要(官方列出的三大特点根本没他什么事,而且React 完全可以不用JSX来编写),但是确实让写模板方便了很多,至少可以不用再拼接字符串了。

JSX 就是一个简单的编译器,可以把 JSX 语法的模板编译成对应的 React 代码。而JSX语法就是直接在JS中写HTML。

比如:

var app = <Nav color="blue"><Profile>click</Profile></Nav>;

实际上被编译成了:

var app = React.createElement(
  Nav,
  {color:"blue"},
  React.createElement(Profile, null, "click")
);

React.createElement 实际创建出来的就是 Virtual DOM,在浏览器环境下最终会编译成HTML代码。因为 直接用 React.createElement 来写一个模板实在是太麻烦了,所以才有了 JSX 会自动帮我们生成这些代码。

仔细看上面的代码会发现,JSX编译的时候其实就是把一个元素里面包裹的所有孩子都作为 createElement 的第 3个参数开始传进去了,比如只有一个孩子就是第三个参数,如果有三个,就分别是第 3 4 5个参数。

JSX 中可以通过 大括号来嵌入JS代码,比如这样:

 var person = <Person name={window.isLoggedIn ? window.name : ''} />;

然后他会被编译成这样:

var person = React.createElement(
  Person,
  {name: window.isLoggedIn ? window.name : ''}
);

JSX 中的JS代码必须是表达式。所以你如果直接写一个 if 语句肯定会报错,因为if语句不是表达式。

实际上,想在JSX中写if语句还真的没有直接的办法,官方给出了一个解决方法是把if逻辑写在JSX外面,参见:https://facebook.github.io/react/tips/if-else-in-JSX.html

React 组件和生命周期

直接看一个官方的例子:

var LikeButton = React.createClass({
  getInitialState: function() {
    return {liked: false};
  },
  handleClick: function(event) {
    this.setState({liked: !this.state.liked});
  },
  render: function() {
    var text = this.state.liked ? 'like' : 'haven\'t liked';
    return (
      <p onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </p>
    );
  }
});

ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

这是一个“赞”按钮。 React.createClass 就是创建了一个类,然后通过 ReactDOM.render 就可以把这个类实例化(或者在另一个组件中实例化也行)。

上面的代码创建了一个类 LikeButton,然后把它实例化。 所以React 组件的生命周期其实分为两部分,一部分是类的生命周期,一部分是实例的生命周期。

类的生命周期比较简单,在创建类的时候只会调用一个方法,就是 getDefaultProps ,来获取一个默认的 props ,并且缓存起来,然后每一个实例如果没有给他指定 props 都会用这个。

实例的生命周期就比较复杂,主要分为三个阶段:

实例化阶段

getInitialState: 获取 this.state 的默认值
componentWillMount: 在render之前调用此方法,在render之前需要做的事情就在这里处理
render: 渲染并返回一个虚拟DOM
componentDidMount: 在render之后,react会使用render返回的虚拟DOM来创建真实DOM,完成之后调用此方法。

其中有几个点需要注意:

1,this.state 只存储原始数据,不要存储计算后的数据
比如 this.state.time = 1433245642536,那么就不要再存一个 this.state.timeString = ‘2015-06-02 19:47:22’ 因为这个是由 time 计算出来的,其实他不是一种state,他只是 this.state.time 的一种展示方式而已。
这个应该放在render中计算出来:
time: {this.formatTime(this.state.time)}

2,componentWillMount 用来处理render之前的逻辑,不要在render中处理业务逻辑。
render就是一个模板的作用,他只处理和展示相关的逻辑,比如格式化时间这样的,如果有业务逻辑,那么要放在 componentWillMount 中执行。
所以render中一定不会出现改变 state 之类的操作。

3,render返回的是虚拟DOM
所谓虚拟DOM,其实就是 React.DOM.div 之类的实例,他就是一个JS对象。render方法完成之后,真实的DOM并不存在。

4,componentDidMount 中处理和真实DOM相关的逻辑
这时候真实的DOM已经渲染出来,可以通过 this.getDOMNode() 方法来使用了。典型的场景就是可以在这里调用jquery插件。

更新阶段

当组件实例化完成,就进入了存在期,这时候一般会响应用户操作和父组件的更新来更新视图。

componentWillRecieveProps: 父组件或者通过组件的实例调用 setProps 改变当前组件的 props 时调用。
shouldComponentUpdate: 是否需要更新,慎用
componentWillUpdate: 调用 render方之前
render:
componentDidUpdate: 真实DOM已经完成更新。

销毁阶段

componentWillUnmount

这里处理一些解绑事件之类的逻辑。

虽然说了这么多,其实我们最常用的还是实例化阶段里面的几个函数。

注意一个小细节就是 React 也是更新了 state 之后会自动更新视图,不过他是通过 setter 方式实现的,也就是你不能直接修改 state ,必须通过 this.setState 或者 this.replaceState 来修改,不然 React 是无法知道 state 已经被更新了的。

组织多个组件

React 中通过组合而不是继承来组织应用。一个应用中从一个组件启动,并在这个组件中创建其他组件,所有的组件最终形成一颗树状结构。

我们依然有父组件和子组件的称呼,不过他们不是继承关系,而是父组件创建了子组件的关系。

这样就涉及到一个重要的问题就是:父子组件的通信问题。

在不借助其他框架的情况下,React 中父子组件是这么通信的:

  • 父组件通过在创建子组件的时候设置 props 来传递数据
  • 子组件通过调用父组件在 props 中设置的回调函数来向父组件传递消息

也就是说,有父子关系的组件其实完全通过 props 属性来通信。那么没有父子关系的怎么办? 官方建议通过 全局的事件系统来通信。

mixins

上面说过 React 中的组件不是继承的,而只是谁创建了谁的关系,那么我们一些公共的逻辑应该怎么处理呢。公共的逻辑应该通过 mixins 来实现。

mixin,可以非常简单的理解,他就是把 一个 mixin 对象上的方法都混合到了另一个组件上,和 $.extend 方法做的事情类似。

不过,react对mixin做了一些特殊处理。

在mixin中写的生命周期相关的回调都会被合并,也就是他们都会执行,而不会互相覆盖掉。
比如 你在mixin中可以定义 componentDidMount 来初始化组件,他不会覆盖掉使用这个mixin的组件。实际执行的时候,会先执行 mixin 的 componentDidMount ,最后执行组件的 componentDidMount 方法。

需要注意的是,因为mixin的作用是抽离公共功能,不存在渲染dom的需要,所以它没有render方法。如果你定义了render方法,那么他会和组件的render方法冲突而报错。
同样,mixin不应该污染state,所以他也没有 setState 方法。mixin应该只提供接口(即方法),不应该提供任何属性。mixin内部的属性最好是通过闭包的形式作为私有变量存在。就像下面这样:

var Timer = function() {
 var t = 1;//私有属性
 return {//xxxx}
}

最好不要放到this上,避免污染。
当然,如果你真的在this上写了一些变量,那么react也会进行mixin,因为react并不会区分你的属性到底是不是函数。

MVC 架构 flux 和 reflux

因为 React 其实只是 View,所以如果组织大型的应用的话因为缺少数据层会非常难以解耦。 React 官方出了一个框架叫 flux。严格说 flux 是一个MVC的规范,而不是一个框架,基于 flux 规范完全可以自己去实现而不需要用 flux 库。

有了flux 之后我们就可以把 View 和 Model 完全分开了。View 就是 React 组件,而 Model 是 flux 中的 store。

盗一张flux官方的流程图:

图中的 View 就是我们 React 组件,它存有store的实例,并且从store中取出数据来创建视图,当需要更新数据的时候,他会触发一个 action。

Store 就是一个存储数据的对象,它会在 dispatch上注册自己感兴趣的事件。

Dispatcher 是flux提供的一个全局的事件分发器,而 action 其实就是自定义的事件而已。

当 发生某些事件的时候,比如用户点击了一个按钮,这个时候View 并不会直接去修改 store ,而是触发一个 action。这个Action 通过 dispatch 进行分发,store已经对自己感兴趣的事件注册过,所以他可以收到这个事件,并更新数据,最终反映到视图上的更新。

所以说白了 flux 只是定义了一个规范而已,它的库特别小,因为其实他就提供了一个全局 Dispatcher 的默认实现。至于 store 要怎么写完全是自己决定的,只要能存数据并且在Dispatcher上注册监听就行了。

另外还有一个类似的叫 reflux ,后序有空了仔细看看个

react native

待续

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值