2

原文链接 : Getting to Grips with React (as an Angular developer)
原文作者 : DAVE CEDDIA
译者 : 李林璞(web前端领域)
译者注:翻译如有疏漏,欢迎指出!感谢!转载请保留此头部。

你是一个对 React 感兴趣的 Angular 开发者吗?不用担心,这真的不会让你成为一个背叛者或其他什么,真的。

或许你早就已经开始玩 React 了:阅读了 Facebook 的官方教程,创建了一些组件...

也或许你正处于我几个月前处于的境地:对React完全没有经验,除了听说过它有多快,它有个虚拟 DOM 的概念,单向绑定,和一些像 Flux , ReduxReflux 之类乱七八糟的东西。

在接下来一系列的文章里我将尝试帮助你将你辛苦习来的 “Angular” 主义知识应用到 React 当中。

Angular to React

在 Angular 里,你可能已经习惯去编写各种指令 directive。如果你是按照流行的规范去编码的话,相信你程序的各个部分都是由一个或多个指令构建而成,并到处都使用了隔离的作用域 scope(如果这听起来很熟悉但并不像你所做的话,或许你可以阅读一下将scope转化为controllerAs)。

React 有着相同的概念:编写组件 component。将你的程序按功能拆分成组件块,并尽可能让每个组件块保持独立和可复用性。事实上,这个想法并不是 React 或 Angular 独创的 - 它只是一种很棒的软件开发实践方式。尽可能写一些简短并具备可复用性的代码(函数,组件,指令,随便你怎么称呼它们)。

一个关键的差别在于 React 里 所有东西都是组件 ,从根节点到下面所有。Angular 让你使用 ng-controller 去混合和匹配所有指令,并在之后特定地路由出各自的指令和模板... React 让这个事情变简单了一点。所有东西都是组件,页面,按钮甚至是路由,我们随后会讲到这些。

好了,所以说 React 的“组件”是类似于“指令”的。那么它的代码看起来是怎样的呢?

下面是一个展示歌曲名字的 Angular 指令:

angular.module('demo', []).directive('songName', function() {
  return {
    scope: {
      song: '='
    },
    restrict: 'E',
    template: '<span class="song-name">{{ ctrl.song.name }}</span>',
    controller: function() {},
    controllerAs: 'ctrl',
    bindToController: true
  };
});

接下来是用 React 写的相同功能的组件:

var SongName = React.createClass({
  propTypes: {
    song: React.PropTypes.object.isRequired
  },
  render: function() {
    return <span className='song-name'>{this.props.song.name}</span>;
  }
});

你马上就可以发现一些相似之处:它们都期望得到一个 song 对象,并都似乎有各种各样的模板。

一些不同之处在于:React 相对 Angular 有着更少的代码。我敢说...更简洁了不是吗?React 里,期望的参数(song)有着某种类型验证,并且 HTML 没有引号!

事实上那个看上去没加引号的 HTML 并不是真正的 HTML。(等下就会讲到)

React.createClassangular.directive 类似 - 它创建了一个组件类。这个组件 必须 有一个 render 方法,propTypes 是一个可选对象,但最好把它写上。

还是要对得起 Angular ,它的1.5版本其实介绍了一个 component 方法来缩短指令的长度,所以上面的指令可以简写成下面这样:

angular.module('demo', []).component('songName', {
  bindings: {
    song: '='
  },
  template: '<span class="song-name">{{ $ctrl.song.name }}</span>'
});

更简单的写法。它甚至默认没有控制器!我推荐阅读一下Todd Motto的关于组件方法的文章并在你的代码里尝试一下。

但它还是没有 propTypes ...

propTypes

propTypes是一个验证组件所需参数的方法。你可以把个别参数标记为“必需的”或“可选的”(默认它们都是可选的),把它想象成类型检测吧。

下面是真正很酷的部分:如果你指定了propTypes并说明一个参数是必需的(像上面那样),然后你忘记把它传进来,你就会在控制台得到一个提示信息。

比起 Angular 这真的太棒了!当你忘记给指令传参时你就再也不怕莫名其妙地报错了。

props

什么是 "prop" ?它实际上是 “property” 的简写(感谢 React 的开发者,让我们再也不用打出 this.properties 或者 this.propertyTypes 了)。

你可以把 props 看作是传给组件的属性。就像在指令里,你会在 HTML 元素里传递属性一样 - props 会在 JSX 元素里被当作属性传递。

使用组件

下面是 Angular 里指令的使用方法:

// Notice how you have to mentally translate 'songName' to 'song-name'?
<song-name song="ctrl.song"></song-name>

然后下面是 React 里组件的使用方法:

// Notice how you DON'T need to mentally translate SongName to anything?
<SongName song={theSong}/>    

所有没有子元素的标签在 JSX 里都可以自终止。

但让我们先花几分钟来讲讲 JSX吧...

JS里的HTML?!

在我对 React 了解不多的时候,我知道它是把 HTML 和 JS 混合起来的,然后我就想这样做真的很 丑陋 啊。我意思是,这对于最佳实践的思考来说已经很多年不会这么写了不是吗?从那段使用 jQuery 的黑暗日子开始,我们就已经知道在 JS 里写 HTML 元素是一件很取巧而且开发体验相当糟糕的事情,所以为什么 React 会再次犯同样的错误呢?

所以,这里有两件事情需要搞清楚:

  1. 那并不是字符串
    你注意到它是怎么做到不给 “HTML” 加引号的吗?那是因为它并不是 HTML。不加引号也并不是一种语法糖,React 是不会将那个东西转换成 HTML 的。

  2. 那并不是HTML
    那是 JSX 。我知道它长得很像 HTML ,马上你可能会想:“JSX嘛 ...只是对 HTML 做了一些细微的改变然后换了个名字而已”,好吧,你也可以那么说。我倒觉得它是给我们提供了一种用函数调用去创建 DOM 元素的语法糖。

JSX 编译成 Javascript

创建 DOM 元素的函数调用?是的,看看下面的代码:

// This JSX...
<span className='song-name'>{this.props.song.name}</span>

// Compiles to this function call:
React.createElement('span', {className: 'song-name'}, this.props.song.name);
// React.createElement('tagName', props, children);

看起来还挺有道理的,不是吗? HTML 创建嵌套的 DOM 节点,那我们就用嵌套的函数调用替代了嵌套的 DOM 节点...

// This is valid JSX:
<div>
  <span className='greeting'>
    <strong>Hello</strong>
    <strong>World</strong>
  </span>
</div>

// ...which compiles to this call:
React.createElement('div', null,
  React.createElement('span', {className: 'greeting'},
      React.createElement('strong', null, 'Hello'),
      React.createElement('strong', null, 'World')
    ));    

实现上来说,这些 React.createElement 方法并没有创建真正的 DOM 节点,它们创建了虚拟的 DOM 节点。

但...关注点的分离!

所以说我们并不是把 HTML 字符串写到 Javascript 里的!

但代码逻辑始终还是和表现层混合起来的!那可不能算对!这么多年的软件开发实践告诉我们这样做是很不好的。放心,现在只是还没做完呢,你并没有把视图和逻辑混合起来。

我认为这是一种类似“货物崇拜”的东西,我们经常在没真正搞清楚为什么的情况下去赞同和执行。有很多很好的理由去说明为什么需要把逻辑和视图层给分离开,但当你回头再看会发现同样也有很多很好的理由去合并它们。

或许你已经写过一些带有控制器和分离的模板文件的 Angular 指令了是吧?

告诉我你有多么经常在看不到或者无法修改控制器的情况下去到模板文件里去修改一些东西?又有多么经常在没有接触到模板的情况下去修改控制器?

这些看上去算是你对关注点的分离吗?

我们喜欢把 JS 和 HTML 分离到不同的文件里去让它们“关注点分离”,可复用性我们来了!

但其实它们很少会那样工作。一个指令的控制器和模板通常会紧紧关联在一起,所以很自然地,它们就像是硬币的两面。把代码分离到不同的文件里并不会自动导致关注点分离。

如果你还没注意到的话,其实我是想尝试通过上面的说法告诉你模板和逻辑控制其实是可以共存在一个文件里的,而且这样做或许看起来更有道理一些。

试试看

我敢打赌你还是充满疑惑。正常,我曾经也是的。

个人来说,我发现从长期以来信服的理论当中跳出来到一个看似完全相反的位置确实是一件相当困难的事情。我试过亲自去尝试那么做并证明给自己看这个新方法并不是那么糟糕。

希望你也可以去做同样的事情,只需要一点点时间。去尝试一下 React 的官方教程(不需要什么乱七八糟的工具 - 下载并运行他们的服务就可以开始写代码了),或者也可以试一下我的三分钟输出 Hello World 教程(不需要编译哦!)。

或许就可以像我做的那样,你会发现写 React 组件确实很有意思。又或者你会发现 React 并不是你想要的东西,但至少你尝试并验证过了。

如果你决定使用它了,那就回来这儿吧,我等你!


max0x0042
1.4k 声望16 粉丝