七周七种前端框架: 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

待续

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom