是时候理清 React 开发中的一些疑惑了
为什么要使用 React这是一个老生常谈的问题了,可能大家在众多的教程、文章里已经了解过了React的好处,比如说它的虚拟DOM可以被高效的渲染,比如说它的组件化使得项目结构非常清晰,代码复用非常容易,比如说它的数据管理机制也能让你清晰的知晓数据的状态,而React本身就是被这种清晰的数据所驱动的。
详细谈论这些优点前,我想说说React给我带来的改变。 在使用React之前,我也一直在使用jQuery,它对节点的操作非常方便,如果仅仅只是普通网页的开发,jQuery无疑是非常好的,但是如果开发的是WebApp,jQuery并不能增强你对全局的把控能力,在学习使用React没多久以后突然有一天我感觉以前所做的开发都好像在玩一些小打小闹的游戏,而使用React也让自己明白了为什么我被称作前端工程师,这个框架让我找到了工程师的归属感,当我再看自己的项目时和一个建筑工程师看自己的设计图的感觉没了太多差别,我知道我的这个组件是房子的总体框架,这个组件是大厅,这个组件是椅子,还有与大厅同级的卧室组件,厨房组件,与椅子同级的桌子组件,家电组件。 我可以用优雅的桌子,椅子,床,台灯来布置一个温馨的卧室; 其实想想,如果只考虑客厅和卧室(不考虑里面的那些桌子椅子之类的组件)那么除却它们的长、宽、摆放位置这些参数不同,它们又有什么区别呢? 回到React,它即带给我们对整体的把控能力,也让我们可以通过修改数据(参数)以表现不同的细节达到不同的效果,从最大的房子的框架到每个桌子椅子的样子,一切都在我们的掌握。下面就慢慢说说React是如何帮我们达到把握全局,了解细节的。 从虚拟 DOM(Virtual DOM)说起想象这么一个场景,客厅里有一把我们不是很喜欢的椅子,想换一把,最合适的做法当然就是改造一下,或者把这把丢了重新买一把新的,为了换一把椅子而重新组装整个房子一看就是不聪明的做法。Virtual DOM为我们提供了一种高效的渲染机制,使得我们可以只改变我们想改变的地方,而尽量不去影响其它无关的组件。它是React高性能的基础。 虚拟 DOM 究竟是什么?要说明Virtual DOM究竟是什么,不得不提到React DOM模块的一个方法, 这个方法就像打开一道门的钥匙,门的两边就是Virtual DOM和Html DOM,我们在浏览器中看到的肯定是Html DOM,Virtual DOM存在于隔着这道门的系统内存之中,Html DOM和Virtual DOM之间存在着映射关系。 每个React组件中还有另外一个 React node
React node其实并非真实的节点,实际上它们可以看做是真实节点在Virtual DOM中的代表,Virtual DOM就是由ReactNodes构成,真实的DOM就是依据它们所构建; 在需要改变真实的DOM时,React其实是先修改虚拟DOM,然后和真实的DOM做比较,在真实DOM中只改变需要改变的地方,这种补丁机制只改变局部,不改变整体,因此对系统性能的消耗较小,对虚拟DOM的修改会在状态改变时触发,后文会详细说明这种状态机制。可能大家也已经听说了二者之间的比较是基于diff算法,知乎上有一篇详细解析React的这个算法的文章,推荐大家阅读。 一般来说创建React node有两种方法,如下 // 方法1,使用React 内置的工厂方法创建 var reactNodeLi = React.DOM.li({id:'li1'},'one'); //方法2,使用JavaScript创建node的方法 var reactNodeLi = React.createElement('li',null,'one'); 最近有一本开源的电子书React Enlightenment里有一章对React node有详细的介绍,也推荐大家阅读。 React提供的另外一种简洁,直观的创建React node的方法,那就是JSX,其实提到React,大家好像都会想到JSX,因为它实在是太方便了,其实使用React其实并非必须使用JSX,不过使用它真的能让我们的工作更加轻松。 JSXvar App = React.createClass({ render: function() { return <p>My name is { this.props.name }</p>; } }); 上面例子里return中的那一部分就是JSX了,初看JSX的语法,可能大家会想到前端开发中经常使用到的模板,不过JSX并非模板,它应该算是React对JS语法的拓展,需要编译后才能正确使用它,JSX的构建是非常简洁明了的,在此就不再赘述。 再说 Babel刚刚已经提及JSX是需要编译才能被浏览器识别的,它就是被Babel编译的,具体说来是被babel-preset-react来编译的。不过Babel的最主要目的其实并非编译JSX,Babel应该算是一个编译平台,其主要目的是转换你在代码中使用了的ES6甚至ES7语法为浏览器识别的ES5语法(babel-core,babel-preset-es2015模块),编译React倒像是其的附加功能。初学者有时候会觉得使用React困难,配置合适的开发环境可是就是原因之一。以前翻译过一篇基础的配置webpack的文章,具体可以点这里。 说到组件了(Component)除却高性能,组件是另外一个React非常吸引人的地方,组件的可复用性,可组合性以及其对模块化开发的天然适应性,使得我们的项目非常直观,便于理解和管理。拿到一个项目,最开始要想的就是如何来划分组件。当然划分肯定需要一些依据,先来看看React自己对组件的分类。 划分并创建组件我在最初使用React时,我的项目里的所有的组件都是通过 Stateful Component之前在使用React重构百度新闻webapp前端看到智能组件和木偶组件二词,我觉得它们可能可以分别对应到
这是一个完整的组件,在这种组件里可能会出现所有的React提供的方法(包括各种生命周期函数 创建: //ES5 写法 var App = React.createClass({ getInitialState(){ return{ name:"Tom",... } },componentDidMount(){ this.setState({ name:"Jim" }) },render: function() { return <p>My name is { this.state.name }</p>; } }); // ES6 写法 class SearchBar extends React.Component { constructor(props) {//props需要作为参数传入 super(props);//需要使用super,如果没有this就会是undefined this.state = { searchTerm: '',}; this.handleInputChange = this.handleInputChange.bind(this);//为事件绑定this,这是ES6语法所要求的,ES5并没相关要求 } handleInputChange(event) { this.setState({ searchTerm: event.target.value,}); } render() { return <input onChange={this.handleInputChange} />; } } 两种写法其实没有本质区别,ES6语法也会通过Babel转换为ES5语法后被执行,但是两种写法里确实存在一些不一样的地方,比如说使用ES6时需要单独绑定 Stateless Component
这种组件里只会出现,React提供的 创建: //ES5 var HelloMessage = React.createClass({ render(){ return <div>Hello {props.name}</div> //多个节点时需要加括号 } }) //ES6 class HelloMessage extends React.Component { constructor(props) {//props需要作为参数传入 super(props);//需要使用super,如果没有this就会是undefined } render() { return <div>Hello {props.name}</div>; } } //Stateless Functions function HelloMessage(props) { return <div>Hello {props.name}</div>; } //ES6 Stateless Functions const HelloMessage = (props) => <div>Hello {props.name}</div>; 模块和组件如若需要,所有的React组件都是可以当做模块被导出的,不过就就我本人看来,一般所导出的模块都是由一个或者若干个组件组成的功能单元。不过说到这里更想说明的一点时,React其实是很依赖类似于webpack这样的模块管理工具的,所以想要用好React,其实也需要对模块的定义,以及模块管理工具有一点的了解。 有生命的组件React里的组件是活的,组件不仅仅有类似于出生,成长,死亡的过程,还有心脏和血液。 生命周期函数 life cycle methods组件的生命周期函数可以分为三个阶段:
关于各个函数的具体意义,在此不在赘述,一个比较容易出错的地方是弄明白各个函数的执行顺序,下面给出一个参考列表。 - Mounting Phase: 1. Initialize / Construction 2. getDefaultProps() (React.createClass) or MyComponent.defaultProps (ES6 class) 3. getInitialState() (React.createClass) or this.state = ... (ES6 constructor) 4. componentWillMount() 5. render() 6. Children initialization & life cycle kickoff 7. componentDidMount() - Updating Phase follows this order: 1. componentWillReceiveProps() 2. shouldComponentUpdate() 3. render() 4. Children Life cycle methods 5. componentWillUpdate() - Unmount Phase follows this order: 1. componentWillUnmount() 2. Children Life cycle methods 3. Instance destroyed for Garbage Collection 组件的生命之源-state用过React的人都知道, state在getInitialState()阶段被初始化,之后通过其它生命周期函数( 我在最初使用React时总觉得,使用了过多的 组件的血液-props为什么把
配合 在开发时,props还可以配合 const AlbumList = (props) => { const albums = props.albums.map((album) => <li>{album.name}</li>); return ( <ul> {albums} </ul> ); }; AlbumList.propTypes = { albums: React.PropTypes.array.isRequired,}; 一点小结本文只总结了我对React的最基础的部分的一些思考,类似于高阶组件,Redux这类的目前我并未接触过多的知识和以及一些类似于React中的事件这类的较容易理解的知识没做过多的叙述,至于Routing这类构建app的知识,以后有机会一定会再和大家分享。 对于刚刚接触React的童鞋,可能看完依旧是云里雾里,不过本文实在算不上教程,初学者可能还是得看比较靠谱的教程。 之前看过一个一个比较好的React学习路径推荐在此也分享给大家,希望对大家的React学习有帮助
最后还要做一个小广告,或者其实也算是对自己的一个激励和监督,之前和 React Enlightenment这本开源书的作者联系,他也非常愿意自己的书能让更多人有收获,所以就同意我把这本书翻译为汉语了。这本书目前一共八章,这本书,上周我看就看完了,感觉有很大收获,对初学者也比较友好,应该好好看一道,React肯定就入门了,我打算是每三四天翻译一章,然后也发布在此处,欢迎大家关注,希望和大家一起进步。 参考
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |