阅读React官网的 RECENT POSTS的我的翻译/摘要(部分)。javascript
英文片断为官网原文片断。html
原文地址java
目前已经有不少的JavaScript MVC frameworks
出世,可是为何Facebook须要建立React
,而且又是什么缘由致使咱们想要去用它?node
不是一个MVC
框架。是一个可用来构建组件化用户界面的库,同时力挺用于构建那些交互数据随时间改变的可复用的UI组件。python
不是模板。传统的页面渲染(JavaScript
渲染),是使用一些模板工具或者直接使用html
标签进行:填充+拼接+渲染。这些构建UI的模板总体抽象了你想要的。react
Traditionally, web application UIs are built using templates or HTML directives. These templates dictate the full set of abstractions that you are allowed to use to build your UI.
git
React以不一样的方式构建用户界面:分隔整个UI为一块一块小的UI组件UI Components
。这就意味着咱们使用一门真实、充满特点的编程语言来渲染视图,其优于模板渲染有如下几个缘由:github
javaScript是一门灵活、强大的编程语言,其拥有构建抽象对象的能力。这一点在大型应用中很是重要。web
经过将标记(标签)与相应的视图逻辑统一块儿来,React可以更加容易的进行扩展和维护视图。chrome
经过JavaScript code的形式来代替标记(标签)和内容,这个过程没有字符拼接的过程而且减小了XSS漏洞。
同时建立了JSX,它是JavaScript的语法扩展。若是好比原生的JavaScript,你更喜欢HTML的可读性,就使用它吧!
当你的应用数据随着时间变化时,React表现是十分耀眼!
在传统的JavaScript应用中,你须要关注哪些数据变化了而且命令式的改变DOM的状态,以便保持视图和数据的一致。甚至经过指令和数据来提供了一种声明式界面的AngularJS,也是经过绑定须要的函数来手动更新DOM节点。
React采用不一样的方法:
当组件初始化时调用render
方法,而且生成一个轻量级的视图表达式。这个表达式将会返回一个标签字符串,而且插入页面文档中去。
当数据变化的时候,render
方法将被会再次调用。为了尽量的提升更新效率,咱们把先旧数据和新数据传入render
方法进行对比,生成一个最小的变化来操做DOM。(render
方法返回的值不是字符串也不是一个DOM元素,而是一个轻量级的DOM表达式)。
因为React有着本身的轻量级的文本表达式,咱们能够作一些更酷的事情:
FaceBook使用React代替html绘制canvas动态图。
结合React和Backbone.Rounter能够完美的构建单页应用(Instagram)。
咱们在React内部原理处理,使得React App既能在web上运行,也能经过Objective-C bridge驱动原生IOS视图。
You can run React on the server for SEO, performance, code sharing and overall flexibility。
事件行为在浏览器中保持一次,而且符合标准。默认使用事件代理。
咱们将在React v0.4中新增了许多中西,可是与此同时咱们发布了React v0.3.3。这个版本解决了人们在使用中遇到的问题,而且使咱们的工具更加容易使用。
容许反复使用同一个DOMNode渲染不一样的组件。
React.renderComponent(<div/>, domNode)
React.renderComponent(<span/>, domNode)
咱们收到关于React
问题中,有大部分是关于props
的,特别是人们但愿对props
进行验证或者使其有个合理的默认值。
在使用props以前,咱们常常对props的某些参数进行某些特殊的处理,例如:须要肯定这些属性是不是特定的类型、需求限定某些值、某些参数的成分是必须的。这些验证咱们均可以在render里面处理,可是这回使render显得臃肿!
如今,React v.04
带来的内建的验证模块,你能够写你本身的限定
React.createClass({ propTypes: { // An optional string prop named "description". description: React.PropTypes.string, // A required enum prop named "category". category: React.PropTypes.oneOf(['News','Photos']).isRequired, // A prop named "dialog" that requires an instance of Dialog. dialog: React.PropTypes.instanceOf(Dialog).isRequired }, ... });
在以往的例子里面,咱们常常看到如下代码:
React.createClass({ render: function() { var value = this.props.value || 'default value'; return <div>{value}</div>; } });
若是对几个穿插在几个不一样组件的props
进行以上操做,这将会产生大量的冗余代码。在React v0.04
中,你能够以申明的方式提供默认值。
React.createClass({ getDefaultProps: function() { return { value: 'default value' }; } ... });
在render以前,咱们使用这些函数进行处理(咱们也会在render
以前支持全部的验证函数),以保证你在使用的时候拿到的数据是你所须要的。
Both of these features are entirely optional. We've found them to be increasingly valuable at Facebook as our applications grow and evolve, and we hope others find them useful as well.
React v0.4.1
版本只是进行了小小的改动。主要是进行修复。部分代码在底层修改了,可是这不会影响你调用咱们的公共Api。
setState的callback参数,会在组件域调用。
click事件已经在移动Safari上支持。
阻止已经在Object.prototype上存在的事件错误处理。
不用设置先前就已经定义了的DOM属性为undefined。
优化对iframe属性的支持。
增长checksums属性来检测和校验服务端渲染和客户端实际渲染内容的是否一致。
改良环境检测机制,使其能运行在非浏览器环境。
Improved environment detection so it can be run in a non-browser environment.
今天咱们很高兴的宣布PyReact
的初始版本的发布。它使得在你的pyhton
应用中可以很好的使用React
和JSX
。PyReact
设计的宗旨是提供一个转化JSX
为JavaScript
的API。
from react import jsx # For multiple paths, use the JSXTransformer class. transformer = jsx.JSXTransformer() for jsx_path, js_path in my_paths: transformer.transform(jsx_path, js_path) # For a single file, you can use a shortcut method. jsx.transform('path/to/input/file.jsx', 'path/to/output/file.js')
#install $ pip install PyReact #use PIPELINE_COMPILERS = ( 'react.utils.pipeline.JSXCompiler',)
Jordan Walke 实现了一个完整的react
项目react-page。其支持客户端和服务端的渲染、使用模板系统进行转化和打包、实时重载。
为何使用服务端渲染?
提升页面初始化速度
在下载JavaScript
前,一些标签元素将会被展现给用户。
在服务端标签元素的构建远远快于客户端。
更快的开发和构建原型
不用等待任何监听脚本或打包器。
更容易的部署
SEO
服务端渲染是如何进行的?
在服务端构建、发送标签给客户端,这样用户能够很快的看到内容。
而后对应的JavaScript
将会被打包送往客户端。
浏览器运行对应JavaScript
,这样全部的事件句柄、交互、数据更新等将会无缝隙的对服务端生成的标记进行绑定。
肉眼看来这一切好像都是发生在客户端,只是更快。
优化内存使用:经过使用 GC PAUSES
(GC:Garbage Collection,垃圾收集,垃圾回收)机制,减小核心内存分配。
提高性能:为了提供代码运行速度,咱们从V8和Nitro中剔除了一些slow path code
。
在网络设备里面区分不一样的路径是一个很天然的选择,由于网络设备的首要任务是转发网络包,不一样的网络包,在设备里面的处理路径不一样。fast path就是那些能够依据已有状态转发的路径,在这些路径上,网关,二层地址等都已经准备好了,不须要缓存数据包,而是能够直接转发。
slow path
是那些须要额外信息的包,好比查找路由,解析MAC地址等。
first path
是设备收到流上第一个包所走过的路径,好比tcp
里面的syn
包,有些实现把三次握手都放到first path
里面处理,而有些只需处理syn
包,其余包就进入fast path
的处理路径。在NP或者ASIC里面也须要区分
slow path
和fast path
,fast path
上的包都放在SRAM
里面,而slow path
的包放在DRAM
里面。不过这也非绝对。决定处理路径的是对于速度的考虑。处理器的速度,内存的速度等。访问内存的速度由速度和带宽两个因素决定。所以,把SRAM
放到fast path
上,也是很天然的选择。
fast path: Frontend -> simple code generator -> assembler
slow path: Frontend -> IR optimizer (sometimes more than one level of IR) -> code-generator -> assembler
标准化支持:DOM
属性的处理。在处理以前不只有多余的检查和开销,并且仍是混淆使用者。如今咱们会一直控制你的属性值为string
直到渲染。
支持Selection
事件。
支持混合事件(池化哟)。
支持一些额外DOM属性:charSet, content, form, httpEquiv, rowSpan, autoCapitalize
。
支持额外的SVG属性:rx
、ry
。
同时支持getInitialState
和getDefaultProps
。
支持挂载到iframes
。
表单组件的bugfix
。
增长react.version
。
增长react.isValidClass
:用户检查某个值是不是有效的组件构造函数。
删除React.autoBind
。在React v0.4中弃用,如今正式删除。
重命名:React.unmountAndReleaseReactRootNode
为 React.unmountComponentAtNode
。
开始着手于精细的性能分析。
更好的支持服务端渲染。
是React运行于一个严格的安全策略下成为可能。同时使使用React编写chrome插件成为可能。
属性class
改名问className
。
增长警告提示(非生产环境)。
改善Window兼容性。
咱们JSX
解释器处理空格上面进行了改变。简单来讲就是,在一行上面组件与组件以前的空格将会被保留,而换行(文本节点或者组件)会被忽略。
<div> Monkeys: {listOfMonkeys} {submitButton} </div> 0.8- React.DOM.div(null, " Monkeys: ", listOfMonkeys, submitButton ) 0.9+ React.DOM.div(null, "Monkeys:", listOfMonkeys, " ", submitButton )
相信这个新特性会很是有空,能够有效减小匆忙之中带来的非期待的空格。
若是你但愿保留 后面紧跟换行文本节点 后面的空格,那么你能够在JSX
里面使用这种写法:{"Monkeys: }
。
咱们在去年春天发布了React
,可是咱们却有意的没有发布版本React v1.0
。其实咱们已经作好了生产准备,可是咱们计划在内部和外部根据开发者怎么使用React
来发展API和行为特性。在过去的九个月咱们学到了不少,而且思考不少关于1.0对于React意味着什么。在过去的两周内,我在多个项目里面概述了咱们打算进入1.0+的世界。今天我书写一点点给咱们的用户,以便用户更好的了解咱们的计划。
咱们在1.0中主要的目的是阐明咱们的消息模式,而且聚焦在一个与咱们目的相关的API上面进行处理。为了完成这个目的,咱们清除一些已经遇到的不友好的模式,真正的帮助开发者写出更好的代码。
在咱们正式推出React
以前,咱们就思考过如何在React
里面利用ES6
,即类,以此来提升建立React组件的体验。咱们以为使用 React.createClass(…)
不是一个最好的选择。即便在使用方面优劣性没有正确的答案,可是咱们在向ES6
方面靠拢。咱们但愿确保这个过程尽量的简单。例如:
class MyComponent extends React.Component { render() { ... } }
其余一些ES6
的特性咱们在React
核心使用了。确信后面会有更多的特性加入。
JSX
在ES6
方面的支持咱们已经在react-tools
里面搭船上线。已经支持转化大部分的ES6
代码转换为老浏览器支持的代码。
尽管咱们没有在文档中说起过context
,可是它以某种形式在React
确切存在的。
While we haven't documented context, it exists in some form in React already. It exists as a way to pass values through a tree without having to use props at every single point. We've seen this need crop up time and time again, so we want to make this as easy as possible. Its use has performance tradeoffs, and there are known weaknesses in our implementation, so we want to make sure this is a solid feature.
从React0.11开始,getDefaultProps()
只会在 React.createClass()
调用的时候调用一次,替代原来每次组件渲染时调用。这就意味着 getDefaultProps()
不会改变他的返回值,同时任何对象将会在全部实例中共享。这个改变提示了性能,而且使得未来可以更早的在渲染中作 PropTypes check ,这将使咱们可以给出更好的错误提示。
自从React发布以来,开发者基本都遇到过 render nothing 的状况。一般是返回一个空<div/>
或者<span/>
。有些更聪明的人返回<noscript/>
来避免不要的DOM nodes
。咱们对此提供了一个解决办法:return null 。这样可以进一步帮忙开发者写出有意义的代码。在实现上,咱们使用了<noscript>
标签进行处理,尽管咱们目的是不返回任何东西。由于noscript
并不会影响你的布局,因此你能够放心使用。
// Before render: function() { if (!this.state.visible) { return <span/>; } // ... } // After render: function() { if (!this.state.visible) { return null; } // ... }
在JSX
里面支持namespaceing
呼喊咱们已经听到了很长一段时间。考虑到JSX
是JavaScript实现的,因此咱们不肯意使用XML namespaceing
。相反,咱们使用选择JavaScript
标准来实现: object property access
。替代为每一个组件分配一个对象,你能够直接这样使用 <Namespace.Component />
。
// Before var UI = require('UI'); var UILayout = UI.Layout; var UIButton = UI.Button; var UILabel = UI.Label; render: function() { return <UILayout><UIButton /><UILabel>text</UILabel></UILayout>; } // After var UI = require('UI'); render: function() { return <UI.Layout><UI.Button /><UI.Label>text</UI.Label></UI.Layout>; }
根据DOM3,React键盘事件包含了一个标准化的 e.key
,这样容许你在代码中编写一个简单的key,而且可以在全部浏览器运行。
handleKeyDown: function(e) { if (e.key === 'Enter') { // Handle enter key } else if (e.key === ' ') { // Handle spacebar } else if (e.key === 'ArrowLeft') { // Handle left arrow } }
React
键盘事件和鼠标事件也包含了标准化的 e.getModifierState()
。
Flux
是Facebook构建JavaScript
应用的时候使用的基于单项数据流的应用框架。咱们使用Flux
的构建大型应用都是有小的组件组成,Flux
来控制咱们提供的小组件(们)。咱们找到了一个很是棒的代码组织结构,咱们十分激动的分享到开源社区。
好比一个完整的框架,Flux
更像一种模式,让你不用增长太多新代码就可以使用Flux
。直到最近的,咱们尚未发布Flux
模块之一: dispatcher
。可是随着新的Flux code
项目和Flux website
发布,咱们提供咱们在生产项目中使用的dispatcher
。
dispatcher
是一个单例。做为数据控制中心控制Flux
应用的数据流。简单的来讲,他就是提供注册回调函数,而后使用必定的命令调用这些回调函数。每一个store
都经过dispatcher
注册了回调。当dispatcher
中有新数据来,它使用这些回调通知相应的store
。而后相关程序经过dispatch()
启动回调函数。
不管是用户进行界面操做仍是接口返回的新数据进入系统的时候,这些数据将会被打包送入一个 action
(由内容和action type
组成的对象)。对此,咱们经常建立名为ActionCreateors
的辅助库,用来建立action object
和传递action
给dispatcher
。
不一样的actioins
由一个type
属性定义。当全部的stores
收到action
的时候,它们就使用这个属性来决定怎么响应它。在Flux
应用中,stores
和views
彼此自我控制,它们不会被外部影响。操做流经过stores
定义注册的回调进入store
, not through setter methods。
使stores
自我更新可以避免不少一些MVC
应用的复杂状况,例如:各个models
之间的联合更新会致使状态的不稳定而且会致使测试很是困难。objects
在Flux
应用中高度分离,而且严格准守得墨忒耳定律。这样会致使软件更加可维护、适配、测试以及对新工程师来谁更加容易理解。
随着应用的壮大,不一样stores
之间的依赖必然存在。例如:Store A 必须 Store A 先更新,而后 Store A才知道如何去更新本身。这个时候咱们就须要dispatcher
可以调用 Store B的回调,以后再操做Store A。为了申明这种依赖,Store A 须要告诉dispatcher
,我须要等待Store B完成后才能执行这个action
。dispatcher
经过 waitFor()
提供这样的功能。
dispatch()
方法经过回调函数提供了一个简单的、同步迭代功能:依次调用。当waitFor()
在某一个回调中触发,随后中止执行这个回调函数,而且 waitFor()
将提供咱们一个有关依赖的新的迭代周期。等这些依赖执行完之后,回调函数再继续执行。
更者,waitFor()
方法但是在同一个store
中不不一样的actions
间调用。
Problems arise, however, if we have circular dependencies. That is, if Store A needs to wait for Store B, and Store B needs to wait for Store A, we could wind up in an endless loop. The dispatcher now available in the Flux repo protects against this by throwing an informative error to alert the developer that this problem has occurred. The developer can then create a third store and resolve the circular dependency.
If you currently use JSX everywhere, you don't really have to do anything to get these benefits! The updated transformer will do it for you.
If you can't or don't want to use JSX, then please insert some hints for us. Add a React.createFactory call around your imported class when you require it。
咱们为了使新用户更简单的了解DOM
(和React
的不一样之处)。咱们使用术语ReactElement
代替<>
,一样ReactNode
代替renderable
。
咱们提供一个API来建立ReactElement
。
var reactElement = React.createElement(type,props,children);
type
参数可使HTML tag
或者class
。它指示着什么样的HTML tag
或者class
将被渲染和包含哪些props
数据。你也能够只提供一个type
参数建立一个工程函数。
var div = React.createFactory('div'); var reactDivElement = div(props,children);
React Element
的签名就像这样
{ type : string | class, props : { children, className, etc. }, key : string | boolean | number | null, ref : string | null }
React With JSX
若是你使用React
的JSX
转化器,这个升级将会很是简单。
// If you use node/browserify modules make sure // that you require React into scope. var React = require('react');
React
的JSX将会为你建立ReactElement
。
var MyComponent = React.createClass(...); var MyOtherComponent = React.createClass({ render: function() { return <MyComponent prop="value" />; } });
React Without JSX
在不使用JSX
状况下须要调用一个组件做为函数,在调用前你须要明确的建立一个工程函数。
var MyComponentClass = React.createClass(...); var MyComponent = React.createFactory(MyComponentClass); // New step var MyOtherComponent = React.createClass({ render: function() { return MyComponent({ prop: 'value' }); } });
React
为常见的HTML elements
内置了工厂函数。
var MyDOMComponent = React.createClass({ render: function() { return React.DOM.div({ className: 'foo' }); // still ok } });
在v0.12版本后,咱们的工做将转向ES6 classes
。咱们会保持向后兼容(React.createclass
)。若是你已经在使用ES6转译器,你能够按照下面申明你的组件。
export class MyComponent { render(){ ... } };
随着JavaScript
的发展,JSTransform
有点"跟不上时代",Babel
的出现能够彻底将其替代。v0.14之后将不会再维护JSTransform
和react-tools
(react-tools
has always been a very thin wrapper around JSTransform.),React
和React Native
目前都使用三方的Babel
的JSX编译器处理.
esprima-fb
ECMAScript解析器
JSXTransformer
JSXTransformer is another tool we built specifically for consuming JSX in the browser. It was always intended as a quick way to prototype code before setting up a build process. It would look for <script> tags with type="text/jsx" and then transform and run. This ran the same code that react-tools ran on the server.
在React
的世界里面全部都是组件,可是你涉及的代码、工程并非都是由React
来构建的。因此,须要你编写管道代码进行二者的连接。处理这些事情的主要API为:
ReactDOM.render(reactElment,domContainerNode)
这须要你提供一个额外的DOM容器。
当把React
插入在单页应用中时候,须要你手动的控制生命周期。React
不会自动释放元素,须要手动控制:
ReactDOM.unmountComponentAtNode(domComtainerNode)
It is not unique to the DOM. If you want to insert a React Native view in the middle of an existing iOS app you will hit similar issues.
若是你调用ReactDOM.render
屡次,那么对应组件上次的props将会被最新的彻底替代。
ReactDOM.render(<App locale="en-US" userID={1} />, container); // props.userID == 1 // props.locale == "en-US" ReactDOM.render(<App userID={2} />, container); // props.userID == 2 // props.locale == undefined ??!?
在面向对象编程中,全部的状态依赖实例存在,经过控制状态的改变控制应用变化。若是你在一个使用面向对象API的app里面使用React
,你也许会惊讶或者迷茫当你设置一个属性的时候会致使其余属性的消失。
对此咱们提供了一个辅助函数setProps
来容许你一次只更新所指望的属性。
Unfortunately this API lived on a component instance, required React to keep this state internally and wasn't very natural anyway. Therefore, we're deprecating it and suggest that you build it into your own wrapper instead.
class ReactComponentRenderer { constructor(klass, container) { this.klass = klass; this.container = container; this.props = {}; this.component = null; } replaceProps(props, callback) { this.props = {}; this.setProps(props, callback); } setProps(partialProps, callback) { if (this.klass == null) { console.warn( 'setProps(...): Can only update a mounted or ' + 'mounting component. This usually means you called setProps() on ' + 'an unmounted component. This is a no-op.' ); return; } Object.assign(this.props, partialProps); var element = React.createElement(this.klass, this.props); this.component = ReactDOM.render(element, this.container, callback); } unmount() { ReactDOM.unmountComponentAtNode(this.container); this.klass = null; } }
Object-oriented APIs don't look like that though. They use setters and methods. I think we can do better. If you know more about the component API that you're rendering, you can create a more natural object-oriented API around your React component.
class ReactVideoPlayer { constructor(url, container) { this._container = container; this._url = url; this._isPlaying = false; this._render(); } _render() { ReactDOM.render( <VideoPlayer url={this._url} playing={this._isPlaying} />, this._container ); } get url() { return this._url; } set url(value) { this._url = value; this._render(); } play() { this._isPlaying = true; this._render(); } pause() { this._isPlaying = false; this._render(); } destroy() { ReactDOM.unmountComponentAtNode(this._container); } }
Components
、Elements
、Component instrances
三者的不一样也许迷惑着许多初学者。为何三个不一样的事物合做可以在屏幕上绘图。
若是你是初学者,你也许开始是和Component classes
和Component classes Instances
打交道。栗如,建立class
来申明Button
组件。当应用运行时,你可能有Button
组件的多个实例,每一个实例有着本身的属性和本地状态。这是传统的面向对象的UI编程。
在传统的UI模块中,由你来控制建立和销毁组件实例。栗:若是一个Form
组件要渲染一个Button
组件。它须要一个Button
的实例而且手动保持和任何信息的交流。
class Form extends TraditionalObjectOrientedView { render() { // Read some data passed to the view const { isSubmitted, buttonText } = this.attrs; if (!isSubmitted && !this.button) { // Form is not yet submitted. Create the button! this.button = new Button({ children: buttonText, color: 'blue' }); this.el.appendChild(this.button.el); } if (this.button) { // The button is visible. Update its text! this.button.attrs.children = buttonText; this.button.render(); } if (isSubmitted && this.button) { // Form was submitted. Destroy the button! this.el.removeChild(this.button.el); this.button.destroy(); } if (isSubmitted && !this.message) { // Form was submitted. Show the success message! this.message = new Message({ text: 'Success!' }); this.el.appendChild(this.message.el); } } }
上面的伪复合UI代码(或者增长更多)都是按照面向对象的方式使用库,就像Backbone
同样。每一个组件实例都保持DOM node
、Child Component
关联而且在合适的时间建立/销毁(DOM node
、Child Component
)。随着代码量的增长,组件可能的状态数会按平方级增加,而且父级能够直接访问子组件的实例,未来想要解偶就会变的很是困难。
比起上述,React
有什么不一样?
为解决上述问题,React
里面出现了Elements
。An element is a plain object describing a component instance or DOM node and its desired properties.
它包含了一些惟一的信息:组件类型(Button
)、属性(color
)和拥有的Child Elements
。
PlainObject:JSON形式定义的普通对象或者new Object()建立的简单对象。
一个Element
不是一个实际的实例。相反,他是一种告诉React
什么须要渲染在屏幕上的方式。你不能调用任何在Elements
上面的方法,它仅仅是一个拥有两个字段属性的不可变对象(type : ( string | ReactClass )
和props : Object
)。
DOM Elements
当Element
的type
是一个字符串时,它表明着一个拥有props
做为属性的DOM node
(这些就是React
将会渲染的)。栗子:
{ type: 'button', props: { className: 'button button-blue', children: { type: 'b', props: { children: 'OK!' } } } } ⇩ <button class='button button-blue'> <b> OK! </b> </button>
对于Elements
嵌套的问题,按照惯例,咱们但愿建立一个Elements
树,而后指定一个或者多个Child Elements
做为其容器/父元素的children props
。
最重要的一点仍是Child Elements
和Parent Elements
仅仅只一中描述方式并非真实实例。当你建立它们的时候并不涉及在屏幕上面呈现的东西。建立和丢弃他们都是可有可无的。
React elements are easy to traverse, don’t need to be parsed, and of course they are much lighter than the actual DOM elements—they’re just objects!
Component Elements
而后Elements
的type
参数可使一个React Component
对应的函数或者类。
{ type : Button, props : { color : "blue", children : "OK!", } }
这是React
的核心思想。
An element describing a component is also an element, just like an element describing the DOM node. They can be nested and mixed with each other.
这个特性可让你建立一个DangerButton
,它是一个拥有特定color
属性值的Button
组件。而且不用小心Button
是否渲染为DOM <button>
、<div>
或者其余的。
const DangerButton = ({ children }) => ({ type: Button, props: { color: 'red', children: children } });
你能够混合搭配DOM/Component Elements
在一个简单的Element Tree
里面。
const DeleteAccount = () => ({ type: 'div', props: { children: [{ type: 'p', props: { children: 'Are you sure?' } }, { type: DangerButton, props: { children: 'Yep' } }, { type: Button, props: { color: 'blue', children: 'Cancel' } }] });
或者你使用JSX
const DeleteAccount = () => ( <div> <p>Are you sure?</p> <DangerButton>Yep</DangerButton> <Button color='blue'>Cancel</Button> </div> );
这种混合和匹配有助于下降组件之间的耦合度,所以它们彻底能够经过一下的结构同时表达is-a和has-a的关系
Button组件是一个具备特定属性的DOM元素button;
DangerButton组件是一个具备特定属性的Button组件;
DeleteAccount在一个div元素中包含一个Button组件和一个DangerButton组件。
Components Encapsulate Element Trees
当React
收到一个使用函数或者类做为type
值得Element
的时候,它知道询问对应的组件什么样的Element
须要被渲染,并给予相应的props
。
当看到这个Element
的时候
{ type: Button, props: { color: 'blue', children: 'OK!' } }
React
将会询问Button Component
什么须要渲染。Button Component
将会返回
{ type: 'button', props: { className: 'button button-blue', children: { type: 'b', props: { children: 'OK!' } } } }
React
将重复此过程,直到它知道页面上的每一个组件有用的底层DOM
标签元素。
前面提到的Form
实例使用React
能够这样
const Form = ({ isSubmitted, buttonText }) => { if (isSubmitted) { // Form submitted! Return a message element. return { type: Message, props: { text: 'Success!' } }; } // Form is still visible! Return a button element. return { type: Button, props: { children: buttonText, color: 'blue' } }; };
以上,对于React Component
组件,props
是输入,Element Tree
是输出。
The returned element tree can contain both elements describing DOM nodes, and elements describing other components. This lets you compose independent parts of UI without relying on their internal DOM structure.
返回的元素树包括描述DOM node
及描述其它Component
。这可让咱们独立地编写UI部分,而无需依赖它们的内部DOM
结构。
咱们使React
建立、更新、销毁实例。
咱们使用Components
返回的Elements
描述实例,React
负责管理实例。
Components Can Be Classes or Functions
// 1) As a function of props const Button = ({ children, color }) => ({ type: 'button', props: { className: 'button button-' + color, children: { type: 'b', props: { children: children } } } }); // 2) Using the React.createClass() factory const Button = React.createClass({ render() { const { children, color } = this.props; return { type: 'button', props: { className: 'button button-' + color, children: { type: 'b', props: { children: children } } } }; } }); // 3) As an ES6 class descending from React.Component class Button extends React.Component { render() { const { children, color } = this.props; return { type: 'button', props: { className: 'button button-' + color, children: { type: 'b', props: { children: children } } } }; } }
When a component is defined as a class, it is a little bit more powerful than a functional component. It can store some local state and perform custom logic when the corresponding DOM node is created or destroyed.
A functional component is less powerful but is simpler, and acts like a class component with just a single render() method. Unless you need features available only in a class, we encourage you to use functional components instead.
Top-Down Reconciliation
ReactDOM.render({ type: Form, props: { isSubmitted: false, buttonText: 'OK!' } }, document.getElementById('root'));
你当运行上述代码的时候,React
会询问Form
组件返回的Element Tree
,给予对应props
。
// React: You told me this... { type: Form, props: { isSubmitted: false, buttonText: 'OK!' } } // React: ...And Form told me this... { type: Button, props: { children: 'OK!', color: 'blue' } } // React: ...and Button told me this! I guess I'm done. { type: 'button', props: { className: 'button button-blue', children: { type: 'b', props: { children: 'OK!' } } } }
This is a part of the process that React calls reconciliation which starts when you call ReactDOM.render() or setState(). By the end of the reconciliation, React knows the result DOM tree, and a renderer like react-dom or react-native applies the minimal set of changes necessary to update the DOM nodes (or the platform-specific views in case of React Native).
You might have noticed that this blog entry talks a lot about components and elements, and not so much about the instances. The truth is, instances have much less importance in React than in most object-oriented UI frameworks.
Element
只是是PlainObject
,用来描述呈如今屏幕上的DOM node
或其它Component
。Elements
的props中能够包含其它Elements
。建立ReactReact Element
是廉价的,一旦建立了React Element
,就不会再发生变化。
一个组件的申明有几个不一样的方式:class
、function
、React.creatClass
。不管哪一种方式,老是props
为输入,Element Tree
为输出。
An instance is what you refer to as this in the component class you write. It is useful for storing local state and reacting to the lifecycle events.(实例就是对组件类中this的引用。)
最后,建立React Elements
使用React.createElement()
、JSX
或者Element Factory helper
。别在实际代码中编写PlainObect
形式的Elements
(只须要知道在底层他们是PlainObject
就好了)。
文档里面对于componentWillReceiveProps
的陈述为:componentWillReceiveProps
在对应props
被改变的时候调用,并做为rerender
的结果。这致使部分用户认为:componentWillReceiveProps
被调用了对应props
必定会变化。逻辑上这个结论是不正确的。
formal logic/mathematics
:A包含着B,不表明B包含着A。有不少缘由致使componentWillReceiveProps
被调用,即便对应的props
没有改变。
你若是不相信,能够试试使用准确的props
三次调用ReactDOM.render()
,而且监控componentWillReceiveProps
的调用。
class Component extends React.Component { componentWillReceiveProps(nextProps) { console.log('componentWillReceiveProps', nextProps.data.bar); } render() { return <div>Bar {this.props.data.bar}!</div>; } } var container = document.getElementById('container'); var mydata = {bar: 'drinks'}; ReactDOM.render(<Component data={mydata} />, container); ReactDOM.render(<Component data={mydata} />, container); ReactDOM.render(<Component data={mydata} />, container);
以上代码componentWillReceiveProps
会被调用两次。
为了理解为何会这样,咱们须要想一想会发生什么。在初始渲染和两次后续更新之间数据可能已经被改变了,若是代码像下面这样执行:
var myData = { bar: 'drinks' }; ReactDOM.render(<Component data={myData} />, container); myData.bar = 'food'; ReactDOM.render(<Component data={myData} />, container); myData.bar = 'noise'; ReactDOM.render(<Component data={myData} />, container);
数据没有改变,但React并无办法知道。所以,React
须要调用componentWillReceiveProps
方法,由于组件须要新props
来通知(即便新的props
和旧props
彻底相同)。
你可能会认为,React
可使用很巧妙的检测机制来检测是否相等,但这种想法也有一些问题
旧的myData和新的myData其实是相同的物理对象(仅对象内部的值被改变)。因为采用的是triple-equals-equal
,检查是否相同的时候并不会告诉咱们值是否被改变。唯一可能的解决方法就是建立数据的一个深拷贝副本,接着作深比较,但这对比较大的数据结构而言过于昂贵(特别是循环)
myData对象可能包括对函数的引用,该函数经过闭包获取变量。React
没有办法获取闭包内部的变量值,所以也没有办法复制和验证它们是否相等
myData可能包括父级渲染时从新实例化了的实例对象的引用,但概念上是相等的(具备相同的key
和value
)。深比较能够检测到这一点,除过这点又会出现新的问题,由于没有办法比较两个函数在语义上是否相同。
因为语言的限制,有时咱们不可能实现真正意义上相等的语义。在这种状况下,React
会调用componentWillReceiveProps
方法(即便props
可能没有改变),使得组件有机会检测新的props,并采起相应的处理。
这样一来,实现componentWillReceiveProps
方法时候要确保props
不能被修改。若是你想在props
被改变后执行一些操做(如网络请求),你的componentWillReceiveProps
代码须要检查props
是否真正的被改变了。