005@关于JSX语法
转(有所更改):React入门:关于JSX语法
可以参考的资料:
React.js学习笔记之JSX解读
JSX=JavaScriptXML。 JSX是React的核心组成部分,它使用XML标记的方式去直接声明界面,界面组件之间可以互相嵌套。
SX可以看作JavaScript的拓展,看起来有点像XML。使用React,可以进行JSX语法到JavaScript的转换。
//使用JSX
React.render(
<div>
<div>
<div>content</div>
</div>
</div>,
document.getElementById('example')
);
//不使用JSX
React.render(
React.createElement('div', null,
React.createElement('div', null,
React.createElement('div', null, 'content')
)
),
document.getElementById('example')
);
那么也就是说,我们写一个XML标签,实质上就是在调用React.createElement
这个方法,并返回一个ReactElement对象。
ReactElement createElement(
string/ReactClass type,
[object props],
[children ...]
)
这个方法的第一个参数可以是一个字符串,表示是一个HTML标准内的元素,或者是一个ReactClass类型的对象,表示我们之前封装好的自定义组件。第二个参数是一个对象,或者说是字典也可以,它保存了这个元素的所有固有属性(即传入后基本不会改变的值)。从第三个参数开始,之后的参数都被认作是元素的子元素。
JSX语法
SX本身就和XML语法类似,可以定义属性以及子元素。唯一特殊的是可以用大括号来加入JavaScript表达式,例如
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(<HelloMessage name="xiaowang" />, mountNode);
一、元素名
自定义出的组件标签名,React 的 JSX 里约定分别使用首字母大、小写来区分本地组件的类和 HTML 标签。render渲染时,会把大写的组件名定义为自定义组件,把小写的组件名定义为HTML自带的标签名进行渲染。
var HelloMessage =
JSX的标签与函数名都是使用的驼峰命名。
htmlFor和className
for和class为js的保留字,在书写for与class时需要修改为htmlFor何className,注意都是使用的驼峰命名。
自闭合标签
在 JSX 中, <MyComponent /> 是合法的,而 <MyComponent> 就不合法。 所有的标签都必须闭合,可以是自闭和的形式,也可以是常规的闭合。
注意:所有 React component 都可以采用自闭和的形式,包括div等
二、子节点
组件与组件之间就像标签与标签之间可以有嵌套关系,与HTML不同的是可以在子节点中使用求值表达式。目前, 一个 component 的 render,只能返回一个节点。如果你需要返回一堆 div , 那你必须将你的组件用 一个div 或 span 或任何其他的组件包裹。
** 切记,JSX 会被编译成常规的 JS; 因此返回两个函数也就没什么意义了,同样地,千万不要在三元操作符中放入超过一个子节点。**
节点属性:
使用标签的时候指定属性和属性值传入标签内部,使标签内部可以使用属性值。
注意:
如果往原生 HTML 元素里传入 HTML 规范里不存在的属性,React 不会显示它们。如果需要使用自定义属性,要加 data- 前缀。
<div data-custom-attribute="foo" />
然而任意属性支持自定义元素
<x-my-component custom-attribute="foo" />
三、求值表达式
要使用 JavaScript 表达式作为属性值,只需把这个表达式用一对大括号 ( { } ) 包起来,不要用引号 ( " " )。求值表达式本身与JSX没有多大关系,是JS中的特性。它是会返回值的表达式,与语句有本质上的不同,在编写JSX时,在 { } 中不能使用语句(if语句、for语句等等)。我们不能直接使用语句,但可以把语句包裹在函数求值表达式中运用。建议把函数表达式独立出来,在 { } 调用。
你没法在JSX中使用 if-else 语句,因为 JSX 只是函数调用和对象创建的语法糖。在 { } 中使用,是不合法的JS代码,不过可以采用三元操作表达式
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name ? this.props.name : "World"}</div>;
}
});
ReactDOM.render(<HelloMessage name="xiaowang" />, document.body);
可以使用比较运算符“ || ”来书写,如果左边的值为真,则直接返回左边的值,否则返回右边的值,与if的效果相同。
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name || "World"}</div>;
}
});
ReactDOM.render(<HelloMessage name="xiaowang" />, document.body);
也可以使用变量来书写
var HelloMessage = React.createClass({
getName : function() {
if (this.props.name)
return this.props.name
else
return "world"
}
render: function() {
var name = this.getName();
return <div>Hello {name}</div>;
}
});
ReactDOM.render(<HelloMessage name="xiaowang" />, document.body);
其中可以把变量去掉,直接在 { } 中调用函数
render: function() {
return <div>Hello {this.getName()}</div>;
}
函数表达式
( )有强制运算的作用
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {
(function(obj){
if(obj.props.name)
return obj.props.name
else
return "World"
}(this))
}</div>;
}
});
ReactDOM.render(<HelloMessage name="xiaowang" />, document.body);
外括号“ )”放在外面和里面都可以执行。唯一的区别是括号执行完毕拿到的是函数的引用,然后再调用;括号放在外面的时候拿到的事返回值。需传入(this)。
四、注释
JSX 里添加注释很容易;它们只是 JS 表达式而已。你只需要在一个标签的子节点内(非最外层)小心地用 { } 包围要注释的部分。
var content = (
<Nav>
{/* 一般注释, 用 {} 包围 */}
<Person
/* 多
行
注释 */
name={window.isLoggedIn ? window.name : ''} // 行尾注释
/>
</Nav>
);
转化
React JSX将类似XML的语法转化到原生的JavaScript,元素的标签、属性和子元素都会被当作参数传给React.createElement
方法:
#JSX
var Nav;
var app = <Nav color="blue" />;
#native JS
var Nav;
var app = React.createElement(Nav, {color:"blue"});
JSX转化器
要把带有JSX语法的代码转化为纯Javascript代码,有多种方式,对于内联与HTML中的代码或者是未经过转化的外部文件,在script标签中要加上type="text/jsx",并引入JSXTransformer.js文件即可,不过这种方式并不建议在生产环境使用,建议的方法是在代码上线前就将代码转换好,可以使用npm全局安装react-tools:
npm install -g react-tools
并使用命令行工具转化即可(具体用法可以参考jsx -h):
jsx src/ build/
HTML 标签 vs. React 组件
React.render方法可以渲染HTML结构,也可以渲染React组件。
渲染HTML标签,声明变量采用首字母小写
var myDivElement = <div className="foo" />;
React.render(myDivElement, document.body);
渲染React组件,声明变量采用首字母大写
var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
React.render(myElement, document.body);
React 的 JSX 使用大写和小写字母来区分本地的组件类和 HTML 标签.
不过需要注意的是class和for这两个属性,JSX语法最终是要被转换为纯Javascript的,所以要和在Javascript DOM中一样,用className和htmlFor。
//使用JSX
React.render(
<label className="xxx" htmlFor="input">content</label>,
document.getElementById('example')
);
//不使用JSX
React.render(
React.createElement('label', {className: 'xxx', htmlFor: 'input'}, 'content'),
document.getElementById('example')
);
还有一点是,在创建HTML标准内的元素时,JSX转化器会丢弃那些非标准的属性,如果一定要添加自定义属性,那么需要在这些自定义属性之前添加data-前缀。
<div data-custom-attribute="foo" />
命名空间式组件
比如开发组件的时候,一个组件有多个子组件,你希望这些子组件可以作为其父组件的属性,那么可以像这样用:
var Form = MyFormComponent;
var App = (
<Form>
<Form.Row>
<Form.Label />
<Form.Input />
</Form.Row>
</Form>
);
这样你只需将子组件的ReactClass作为其父组件的属性:
var MyFormComponent = React.createClass({ ... });
MyFormComponent.Row = React.createClass({ ... });
MyFormComponent.Label = React.createClass({ ... });
MyFormComponent.Input = React.createClass({ ... });
而创建子元素可以直接交给JSX转化器:
var App = (
React.createElement(Form, null,
React.createElement(Form.Row, null,
React.createElement(Form.Label, null),
React.createElement(Form.Input, null)
)
)
);
Javascript表达式
在JSX语法中写Javascript表达式只需要用{}即可,比如下面这个使用三目运算符的例子:
JSX是HTML和JavaScript混写的语法,当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。
// Input (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// Output (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
属性表达式
React.render(
<div className={2 > 1 ? 'class-a' : 'class-b'}>content</div>,
document.body
);
子表达式
var Nav = React.createClass({
render: function () {
return <div>nav</div>
}
});
React.render(
<div>
{2 > 1 ? <Nav/> : <div>div</div>}
</div>,
document.body
);
不过要注意的是,JSX语法只是语法糖,它的背后是调用ReactElement的构造方法React.createElement的,所以类似这样的写法是不可以的:
// This JSX:
<div id={if (condition) { 'msg' }}>Hello World!</div>
// Is transformed to this JS:
React.createElement("div", {id: if (condition) { 'msg' }}, "Hello World!");
可以从转化后的Javascript代码中看出明显的语法错误,所以要不用三目运算符,要不就这样写:
if (condition) <div id='msg'>Hello World!</div>
else <div>Hello World!</div>
传播属性(Spread Attributes)
如果提前就知道了组件的属性的话,写起来很容易。例如component组件有两个动态的属性foo和bar:
var component = <Component foo={x} bar={y} />;
而实际上,有些属性可能是后续添加的,我们没办法一开始就确定,我们可能会写出下面不好的代码:
var component = <Component />;
component.props.foo = x; // bad
component.props.bar = y; // also bad
这样写是错误的,因为我们手动直接添加的属性React后续没办法检查到属性类型错误,也就是说,当我们手动添加的属性发生类型错误时,在控制台是看不到错误信息的。
在React的设定中,初始化完props后,props是不可变的。改变props会引起无法想象的后果。
延伸属性
为了解决这个问题,React引入了属性延伸
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
//或者
var props = { foo: x, bar: y };
var component = <Component { ...props } />;
这样就相当于:
var component = <Component foo={x} bar={y} />
当需要拓展我们的属性的时候,定义个一个属性对象,并通过{…props}的方式引入,在JSX中,可以使用...运算符,表示将一个对象的键值对与ReactElement的props属性合并,这个...运算符的实现类似于ES6 Array中的...运算符的特性。,React会帮我们拷贝到组件的props属性中。重要的是—这个过程是由React操控的,不是手动添赋值的属性。
它也可以和普通的XML属性混合使用,需要同名属性,后者将覆盖前者:
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'
JSX 陷阱
style属性
在React中写行内样式时,要这样写,不能采用引号的书写方式
React.render(
<div style={{color:'red'}}>
xxxxx
</div>,
document.body
);
HTML转义
比如我们有一些内容是用户输入的富文本,从后台取到数据后展示在页面上,希望展示相应的样式
var content='<strong>content</strong>';
React.render(
<div>{content}</div>,
document.body
);
结果页面直接输出内容了:
<strong>content</strong>
React默认会进行HTML的转义,避免XSS攻击,如果要不转义,可以这么写:
var content='<strong>content</strong>';
React.render(
<div dangerouslySetInnerHTML={{__html: content}}></div>,
document.body
);