Loading…

最近尝试了下Facebook的React,感觉特别好用,花了两天,写了个简单的组件,等把手上的项目里的几个安全漏洞修了,过来写篇文章分享一下。

一如既往的,我这人比较啰嗦,文章或许比较长,还请海涵。这里不得不提一下@题叶,多亏他孜孜不倦的在国内推广React,我才有机会认识它,同时这篇文章里也摘要了一些他之前译文中的内容,一并感谢。

什么是React.js

背景,为什么我需要React

React.js是Facebook公司在2014年前后推出的一款前端的UI库,它并不像Foundation或者bootstrap那些围绕jQuery以及CSS而开发的UI脚手架,你仍然得实打实的为你的模块编写代码,只是开发的模式和以前大有不同。

就像我之前在博客中提到过的一样,2010年后,由于社会上对F2E需求的爆发性增长,不少顶级互联网公司对都FE这个领域开始了不同程度的探索。于是,人们开始在前端领域谈MVC、谈MVVM、谈SPA、谈components,而就这些特性而言,Google的Angular(下称ng)已然是众多框架中的翘楚,它允许Dev可以以MVVM的方式很快的实现各种诸如数据双向绑定、自定义标签等等的需求。但所谓有利就有弊,ng对普通Jser的依旧有着很大的要求,而且ng本身是一个高度复杂的框架,而有时候在一些webapp上,我只是想可以快一点、再"快"一点的写view层的模块,并不需要ng这样重量级框架。

而面临这样需求,React.js则应运而生了,也因为这个朴素初衷Fb对它的描述仅仅是一句:

A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES

而它的愿景也很简单:

COMPLEX WEB APPS MADE EASY

HTML5 与 Web Components

从我学习HTML的第一天开始,我就一直坚信着组件化的HTML才是正义。然而过去由于针对浏览器以及web标准本身各大厂商有各自不同的分歧或者利益纠纷导致了或多或少不同程度上的实现差异,因此在web端的开发一直很难像传统的客户端界面那样顺手。好在随着B/S架构的互联网产品越来越多,我们所期待的"组件化",也一直在以不同的形式不断的迭代着。

于我而言,

第一次见是IE特有的htc,但那时候还小,不懂web,也不会写程序,等回过神来看懂了htc,时过境迁,Chrome已然崛起,IE也不再是雄主了。

第二次见是决定做FE之后,在学习jQuery的过程中了解到了John Resig设计的插件体系从而衍生的一系列UI插件,这也可以一定程度上达到组件化的目的,但现在想来,碍于其实现的机制,如果页面上有各种繁多jQuery UI Plugin必然会大幅度拖慢页面的性能。

第三次见则是仔细阅读了HTML5标准新增的诸如Header、Footer、Nav等语义化标签,虽然只是语义,但良好设计的语义当然也应该是组件的一部分。

第四次见就是H5规范中后来出现的Web Components标准以及面向此标准的Polymer实现,而几乎同时期(<=1 years),也诞生了另外一种可重用的Components设计模式——React.js。

但文章所限,今天就不对Polymer做介绍了,更多的谈谈React。

React的优势

兼容性:

React是一个view层的Library,人们通常会把他和Polymer相比较,但Goolge的 Polymer因为过于超前,所以在完成Polyfill后最低也只能兼容到IE10,移动端平台倒是可以一试。相比之下React则可以兼容到IE8,就国内的浏览器现状来说,着实是一个不小的优势。

前后端统一,SEO Friendly :

这两年随着Node.js的崛起,前后端统一这个议题都被说烂了,但这里我想说的不是编程语言上的统一(其实,我觉得这个本身也没什么意义),而是前后端渲染模板上的统一。

众所周知,Angular.js在开发webapp时会配合大量后端提供的数据接口从而达到动态、异步填充的特性,客户端渲染速度确实会快,体验也会更好,但同时也导致了互联网爬虫无法正确地检索到页面的真实数据而只能爬取到一些模板字段。开发者为了解决这个问题,通常情况下会在server端重写一套渲染逻辑专门供爬虫检索。而使用React的话则可以一定程度上改善这个问题,比如Express上就有一款视图引擎express-react-views可以实现服务端渲染React组件,类似的在.NET平台上还有Reactjs.Net项目,藉此developer可以实现前后端全部组件化的组织。

一次编写,两端运行,同时又能提供良好的SEO。

同时,由于React这样的framework可以同时运行于server side 以及 client side,藉此还引申出了一种名为 “Isomorphic”的App设计风格,但这里暂且不提。

Virtual DOM & Performance :

所有程序员都爱关注性能,FE也不例外,自从Chrome挑起第二次浏览器大战以来,JavaScript的速度已经越来越快,时至今日,JavaScript的执行速度已经超越了大部分动态语言。但理想很丰满,现实很骨感,在现有的前端体系中,碍于文件对象模型的树形实现,我们不得不面临一个现状,就是:JS很快,但DOM太慢。

React为了解决这个问题因此在现有的两层模型中额外引入了一个过渡层。

现有的操作模型 React的操作模型

用Virtual DOM这个过渡层来表达JS对DOM修改操作,通过引入这个过渡层,所有对DOM的操作不会立刻显示在DOM树上,而是等待事件完成所有的DOM修改之后,通过React内部实现的diff算法来计算出最小diff,然后再以最小的步骤将 diff 作用到真实的 DOM 上。

最后得益于「组件」这个概念,每个React组件都拥有一个完整的生命周期,对DOM状态的操作都会批量更新,以期尽可能的减少页面重绘,来追求更好的性能。

React in Action

Hello React App

说了那么多,总要实地写两句代码。每个Example都是从Hello World开始的。首先先让我们来自定义一个HelloWorld组件。

var  
     React = require('React')
    ,Hello = null
    ,node  = null


Hello      = React.createClass({  
    render : function(){
        return (
            <div>Hello {this.props.message}</div>
        )
    }
})

node = document.getElementById('Hello')  
React.render(<Hello message="this is a react Component" />,node) 
<!doctype html>  
<html>  
<head>  
  <meta charset="UTF-8">
  <title>Hello React</title>
</head>  
<body>  
    <div id="Hello"></div>
</body>  
</html>

效果:

上述的写法是使用React附带的JSX Compiler时才支持的DSLs,当然React同时还支持Native JavaScript的写法,直接编写JSX Compiled后的代码,因为DSLs的写法更直观,所以我相对喜欢第一种code style。

var Hello = React.createClass({displayName: "Hello",  
  render  : function() {
    return React.createElement("div", null, "Hello ", this.props.message);
  }
});

React.render(React.createElement(Hello, {message: "this is a React Component"}), node);  

Flux Architecture

写完了简单的「Hello React」,让我们再来讨论一个略微复杂一些的项目,同时再来讨论一下Facebook随着React同时发布的Flux架构以及React的单向数据流。

React-Easy-datePicker(Live Demo) 是我前阵子为了研究Flux时写的日历组件,写完这个组件后算是对Flux有了个大概的认识。

FB在推出Flux这个架构设计的初衷实际上是为了取代我们传统意义上MVC的概念的,他们认为MVC架构中View与Model之间的关系过于复杂导致其中维护成本很高,因为视图、模型之间可能有多个关联操作(set/get),维护起来如履薄冰,必须小心翼翼,因而提出了Flux。

仔细来看的话,Flux这个架构实际上就是所谓单向数据流的载体。它把一个App的数据循环所经历的几个过程拆分别拆分成不同的部件来负责单一的功能模块并且唯一化模块内数据的出口最后各司其职形成Flux的

One Direction Flow

但是正所谓理想是丰满的,现实是骨感的,当我粗略的按照规范所描述的实现过一遍Flux后,我发现数据流向虽然是清晰了,可同样也会感觉到FB的工程师对于Store这个模块的设计或者说描述的非常不清晰,而Flux本身又类似于后端的领域驱动模型,当组件事件丰富后,Store模块内会显得相当拥挤、不优雅。因此我在实现datePicker的时候花了不少时间去探索Flux现有的多种实现并试图寻找一个相对合适的实践,在开发的早期用Native Flux实现了一遍,在后期又用Reflux重构了一次。

后来有一次偶然的机会在网上和一位网友对Flux的设计做了一些讨论,我们倾向认为Flux与其说是一种架构,倒不如说是一种特向化的设计约束,通过明确定义的行为约束(HTML行为与Action的一一对应)来理清组件内部的关系,形成数据闭环。

当然虽然Flux本身还有些内容需要去探索和实践,但无论如何,React的出现都降低了人们开发组件化webapp的成本,大规模简化了数据与视图的渲染操作。而除此以外,其背后支撑的一些理念也是非常优秀的,值得借鉴。其中之一就是Immutable——数据不可变,关于这一点也是为什么React受到了一些函数式编程爱好者的推崇的原因。由于React+Flux的组合做到了组件内所有数据自动形成单向的闭环,藉此开发者对app和页面拥有了超越过去的更好的控制力,同时由于React项目内不同组件的状态都借由不可变的props自顶向下的赋值来传递,所以还可以确保同样的数据输入始终能得到同样的渲染,也就所谓的「幂等渲染」,这也意味着界面的状态会更易描述;而如果还有看过Facebook Github上别的一些项目的代码组织,FB的工程师在开发某些项目时候不仅会使用React,更会额外引入Immutable.js来做协同开发,借由这个库对JS内的一些数据类型(比如数组)进行了增强,一定程度上做到了Data的Immutable,使得数据的操作更简化,并且获得更好的查错能力,也使App获得健壮性上的加强。

开发工具

在In Action 以及整篇文章的最后,再简短介绍几个有助于开发的工具可以在制作React组件时如虎添翼。

  • Gulp [ 前端预处理器 ]
  • browserify [ 基于commonjs标准的打包工具,使得开发者可以像node一样组织项目模块 ]
  • gulp-reactify [ React JSX 的预处理插件 ]

Ref

2015.1.2 Update

最近把线上App的漏洞都给修全了,所以来补作业了,毕竟埋了坑总是要填的。

Tags

Facebook , HTML5 , Web Components , React.js , Angular.js

About the author
comments powered by Disqus