再论React与Angular-从数据绑定谈起

A2R

前言

以前的项目一直使用Angular2,但同时一直保持对React的关注,自己也在工作之余找一些React的项目资料来学习,当时有一些对两者的感悟,也写了一些文章,散落在之前发布的博客中。只所以想再论有两个原因:一是把之前散落在其他文章中的关于两个框架的比较收集在一篇文章中。二是真正在新项目中使用React也有一段时间了,有一些新的感悟。把新旧知识点一起梳理一下,提炼出对React和Angular两个流行前端框架更高层次的认识,如何做技术选型,不同的框架背后的思想是什么以及适用于什么样的业务场景。

注:这里的Angular指的是Angular2以及以上版本

React是MVC中的View层,Angular是一个MVVM框架

Angular学习曲线比React陡峭的原因也就在这里了。开发者需要掌握Angular MVVM的全部使用方法,包含并不局限于Typescript,依赖注入,RxJs等等。相比之下React只需要关注View层面的一些知识,比如JSX。

但是我想强调的一点是光使用React是无法开发一个完整的项目,还需要以中间件的形式引入一些其他的第三方库来补足其所缺失的Model和Controller层,比如Redux等,而第三方库的学习成本却没有包含进来,而有时候这些学习成本也是惊人的。所以我认为,在Angular和React中做选择时,学习成本这个因素可以先不考虑。

变化监测

Virtual Dom可以说是React的一个标签了,关于它的文章可以说是汗牛充栋,这里就不详述了。

Angular用的是一套截然不同的变化检测方法,关于这部分的细节在我之前的博客里:
Angular2 组件变化检测总结 (Change Detection)

React: One-Way binding vs Angular2: Two-Way Binding

这点是我真正参与React项目与之前Angular项目对比感受最深的地方。数据绑定的不同方式体现了两种框架的根本不同之处。

首先我们要了解前端是什么?不同的人也许有不同的认识,就我而言,我认为前端主要是数据的展示与交互。对于用户来说,这一切都发生在View层。前端工程师做的职责就是展示数据并响应交互。可见数据(Data)和界面(View)是紧密相关的,一个好的前端框架就是用一种好的方式实现数据的绑定,同时提供好的用户体验和开发体验。

就当前业界的实现方式来看分为两种,分别是: 单向数据绑定(React)和双向数据绑定(Angular2)。不同的绑定方式决定了不同的状态管理思想,因此数据绑定方式的不同是React和Angular根本的不同

先抛开框架,通过两张图体会一下单向数据绑定和双向数据绑定的含义:

单向数据绑定

双向数据绑定

通过上图可见,单向绑定和双向绑定:

共同点:Model -> Template -> UI

Model层数据的变化会引起View层变化。

在React中,更新Model的动作即setState。

不同点:捕捉到用户输入时View层(即UI)的更新机制

单向绑定:

用户输入时会触发事件(onChange, onKeyDown etc..),在事件回调函数中更新Model。React中有两个栗子可以验证:

1. 原生React

// 非常简单的一个栗子,但是通过与Angular的比较,可以看出一些不一样的东西
class Hello extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
        inputValue: this.props.name
    }
  }
  render() {
    // 输入框绑定了state.inputValue,这里是单向绑定,当用户输入时输入框不会有
    // 任何变化,依然是初始化时传入的hello world
    return <input value={this.state.inputValue} onChange={this.onInputChange}></input>;
  }

  // 将注释代码生效,用户输入时输入框中的值也同步更新,这是因为
  // seState改变了Model层数据,进而触发UI更新。
  onInputChange = (e) => {
    // let value = e.target.value;
    // this.setState({inputValue: value});
  }
}

ReactDOM.render(
  <Hello name="Hello World" />,
  document.getElementById('container')
);

2. React-Redux

Redux的模版代码比较多,我就code了。Redux全局提供一个store,初始化时各个component通过connect方法获取所需要的数据。当全局store中对应的数据发生变化时,触发component render。

User Input -> InputHandler {dispatch(action)} -> Redux Store 变化 -> render component

可见View层的重新渲染的前提也是Model层(Store)的数据发生变化。

双向绑定:

Angular2:

import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `
    <input [(ngModel)]="inputValue"></input>
   `
})
export class AppComponent {
  // two-way: 1) model层(inputValue)的变化会引起ui的变化(同React)
  //          2) 用户输入触发事件直接修改dom重新渲染UI,同时更新Model
  //             (该情境下,UI变化并非由Model引起)
  inputValue = 'Hello World';
}

结论就是:双向绑定和单向绑定的根本区别就是UI层的变化是否有Model层的变化驱动

适用场景

双向绑定中,开发者不必去关注UI和数据层的交互细节,只需要专注于业务的开发。但是我们从之前的阐述可以注意到,对于UI层来说,它的触发来源有两个,一个是用户输入(这部分实现对开发者来说是黑箱),一个是Model层变化,当项目很大,业务越来越复杂,需要管理的component越来越多的时候,状态管理就会成为一个难题,很难跟踪定位,那么随之而来的就是一些可能看起来莫名奇妙的bug。

单向绑定中,UI层的触发来源就一个Model层,只要抓住Model层的变化,就抓住了状态的变化,这也正是Redux可以时光旅行的关键所在。但是Redux有一个广为诟病的缺点就是模版代码太多,哪怕是一个小的应用,开发者也不得不增加几个文件比如action, reducer。

有种好的实践方法就是,component内部的状态可以用双向绑定,因为一个好的component必定是高内聚的。组件间通信可以使用单向绑定数据流,保证所有的不同组件都有一个公共的Store,而不是各自为战。

借鉴与融合

每一种优秀的前端框架都有值得别人借鉴的思想,随着前端技术的发展,大家也都在借鉴友框商的优点为我所用。技术毕竟是用来支撑业务,不同的技术思路总是诞生与不同的应用场景。而不应该像华山派的剑宗与气宗一样势不两立。
对于双向绑定和单向绑定,比如Redux成名在于React中的良好实践,但是本身的单向数据流的思想并不是依赖于某个框架,事实上,Angular现在也有自己相关的实现:

从对Redux的使用看Angular2和React的设计差异

当然,MVVM也不是Angular的专属,先不说Vue,就React而言,我就见过一个React的MVVM架构,尽管我没用过,但是说明的确有人再做借鉴融合的尝试。

React-MVx 2.0

从数据绑定切入去看React和Angular,让我对两者之间的区别以及适用场景有了更深的认识。那么问题来了,小编自己更青睐哪一个框架?我不告诉你:)

猜你喜欢

转载自blog.csdn.net/Napoleonxxx/article/details/81352433
今日推荐