摘要
JSX(JavaScriptXML)提供了一种在JavaScript中编写声明式的XML的方法,使用JSX可以提高组件的可读性,React允许做简单的JSX语法转化。
简介
JSX像是在JavaScript代码里直接写XML的语法,每一个XML标签都会被JSX转换工具转换成纯JavaScript代码,React 官方推荐使用JSX,这个看个人习惯, 如果你喜欢纯JavaScript代码也是可以的,只是使用JSX会给我们带来如下好处:
- 是原生的JavaScript;
- 程序结构更容易被直观化;
- 提供更加语义化且易懂的标签;
- 抽象了React Element的创建过程;
- 允许使用熟悉的语法来定义HTML元素树;
- 可以随时掌控HTML标签以及生成这些标签的代码;
定义第一个组件
简单的理解组件就是对数据和方法的简单封装,目的就是模块化功能。在React当中组件是用来分离关注点的,而不是被当做模板或处理显示逻辑的,在使用React开发应用过程中,往往HTML标签以及生成这些标签的代码之间存在着内在的紧密联系,其实这一坨代码就可以理解为是一个组件。
接下来看一个简单的DEMO,定义我们的第一个组件,按照以往的游戏规则,我们就给他起一个文雅又响亮的名字——“HelloWorld”(React的安装包可以到官网去下载):
<!DOCTYPE html>
<html>
<head>
<title>Hello React</title>
<!--React核心库-->
<script src="build/react.js"></script>
<!--react-dom.js提供与DOM相关功能-->
<script src="build/react-dom.js"></script>
<!--browser.js将 JSX 语法转为 JavaScript 语法-->
<script src="build/browser.min.js">/script>
</head>
<body>
<HelloWorld>Hello World!</HelloWorld>
<!--为了把 JSX 转成标准的 JavaScript,我们用 `<script type="text/babel">` 标签,然后通过Babel转换成在浏览器中真正执行的内容-->
<script type="text/babel">
// 定义组件HelloWorld
var HelloWorld = React.createClass({
render : function(){
return (
<div>
<h1>this.props.children</h1>
</div>
);
}
})
</script>
</body>
</html>
关于上例中的几点说明:
- React中组件名必须以大写字母开头;
- React中的组件只能包含一个顶层标签,否则会报错;
- JSX将两个花括号之间的内容
{...}
渲染为动态值,花括号指明了一个JavaScript上下文环境,它会将其中内容进行求值,然后渲染为标签中的若干节点;this.props.children
是组件的特殊属性,保存了开始标签与结束标签之间的所有子节点,上例中this.props.children = ["Hello World!"];
上述代码如果不使用JSX语法,写法如下:
...
// 定义组件HelloWorld
var HelloWorld = React.createClass({displayName:"HelloWorld ",
render : function(){
return (
React.createElement("div",null);
React.createElement("h2",null,this.props.children);
);
}
})
...
不管使不使用JSX,HelloWorld组件最终的页面渲染结果都是一样的,如下所示:
<div>
<h1>Hello World!</h1>
</div>
JSX与HTML有何不同
“这个规范(JSX)并不尝试去遵循任何XML或HTML规范。JSX是作为一种ECMAScript特性来设计的,至于大家觉得JSX像XML这一事实,那仅仅是因为大家比较熟悉XML。”——以上内容摘自http://facebook.github.io/jsx/
由此我们可以看出JSX仅仅是像HTML而已,接下来看下他们之间的关键区别。
属性
在HTML中我们往往通过内联的方式设置标签的属性,JSX在支持这种方式的基础上,还支持动态的设置标签的属性,具体实现形式如同我们上个DEMO中的{...}
,我们可以将属性值定义为JS变量或者是函数。如下所示:
<!--在HTML中标签属性示例-->
<div id="demo" class="myStyle"></div>
<!--在JSX中标签属性示例-->
var demoId = this.props.id;
var demoClass = "myStyle";
function getName(){
...
}
<div id={demoId} name={this.getName()} className={demoClass}></div>
在React渲染组件的过程中,我们上面定义的变量和函数会被求值,最终生成的DOM结构会反映出这个新的状态。
非DOM属性
下列属性只在JSX中存在:
- key:可选的唯一标示符,用来唯一的标识一个组件;
- ref :允许父组件在render之外保持对子组件的一个引用;
- dangerouslySetInnerHtml:提供插入纯 HTML 字符串的功能,主要为了能和生成 DOM 字符串的库整合。
接下来详细看一下这几个特殊属性的作用。
键(key)
在程序运行过程中,由于用户与应用间的交互等原因,一个组件在组件树中的位置很有可能发生改变,最常见的例子就是某列表记录的增、删操作。当然这种情形下组件可能并不需要被销毁并重新创建。
通过给组件设置一个唯一的标识,且保证它在一个渲染周期中保持一致,这样React就能智能的决定该重用哪一个组件,或者销毁并重新创建一个组件,避免不必要的重新渲染,得到性能的提升。
引用(ref)
在JSX中可以通过在属性中设置期望的引用名来定义一个引用。
var App = React.createClass({
getInitialState: function() {
return {userInput: ''};
},
handleChange: function(e) {
this.setState({userInput: e.target.value});
},
clearAndFocusInput: function() {
// 清空输入框
this.setState({userInput: ''},
function() {
// 这段代码会在组件重新渲染后执行,使输入框重获焦点
this.refs.theInput.getDOMNode().focus();
});
},
render: function() {
return (
<div>
<div onClick={this.clearAndFocusInput}>
点我!点我!!点我!!!
</div>
<input ref="theInput" value={this.state.userInput} onChange={this.handleChange} />
</div>
);
}
});
然后我们就可以在组件中的任何地方使用这个引用了。通过引用获取到的这个对象叫做支持实例。他并不是一个真的DOM,而是React在需要时创建的一个描述对象。你可以通过this.refs.theInput.getDomNode()
来访问真实的DOM节点。
设置原始的HTML
dangerouslySetInnerHTML—— 顾名思义,从属性名当中就能看出来,以此来警告它的值( 一个对象而不是字符串 )应该被用来表明净化后的数据。在彻底的理解安全问题后果并正确地净化数据之后,生成只包含唯一 key __html
的对象,并且对象的值是净化后的数据,示例如下:
function createMarkup() {
return {__html: 'First · Second'};
};
<div dangerouslySetInnerHTML={createMarkup()} />
这么做的意义在于,当你不是有意地使用 <div dangerouslySetInnerHTML={getUsername()} />
时候,它并不会被渲染,因为 getUsername()
返回的格式是 字符串 而不是一个{__html: ''}
对象。{__html:...}
背后的目的是表明它会被当成 "type/taint"
类型处理。 这种包裹对象,可以通过方法调用返回净化后的数据,随后这种标记过的数据可以被传递给dangerouslySetInnerHTML
。 基于这种原因,我们不推荐写这种形式的代码:<div dangerouslySetInnerHTML={{__html: getMarkup()}} />
这个功能主要被用来与 DOM 字符串操作类库一起使用,所以提供的 HTML 必须要格式清晰(例如:传递 XML 校验 )
注释
由于JSX本质上就是JavaScript,因此也支持JavaScript的注释方式,在JSX中可以用以下两种方式添加注释:
- 当做一个元素的子节点;
- 内联在元素的属性中;
示例如下:
// 作为一个元素的子节点
<div>
{/*多行注释*/}
<h1>This is a h1 tag.</h1>
</div>
// 内联在元素的属性中
<div>
<h1
/*
* 多行注释
*/
> 多行注释 </h1>
</div>
<div>
<h1
//单行注释
>单行注释</h1>
</div>
特殊属性
由于JSX会转化为JavaScript函数,所以有些关键词我们不可以使用,比如for
和class
。
这两个属性分别可以用htmlFor
和className
替换,参考如下示例:
<label htmlFor="name" ...>
<input calssName={classes} ...>
样式
React把所有的内联样式都规范化为驼峰形式,同样类似于JavaScript中DOM的style属性,要给组件添加自定义属性,如下:
var styles = {
width:100px;
height:100px;
}
React.renderComponent({<div style={styles}>...</div>,node})
参考
【1】《React引领未来的用户界面开发框架》
【2】 React中文官网