深度学习JSX–JSX in deep
JSX是一种JavaScript的延伸,看起来好像XML。你可以在React里使用一种简单的JSX语法的变形。
Why JSX?
虽然你也可以在React中使用原生的JavaScript,但是我们还是建议你使用JSX。因为它是一种更简洁直接和熟悉的语法去构建带属性的树结构。
不管是对设计者还是开发者都对它更熟悉。
它的标准的开闭标签有助于阅读一大颗树。
它不会改变JavaScript语义。
HTML Tags vs. React Components
React既可以render HTML 标签(字符串),也可以render React Component(classes)。
render HTML 标签,可以使用JSX里的小写标签:
var myDivElement=<div className='foo' />;
ReactDOM.render(myDivElement,document.getElementById('example'));
render React组件,首先使用React.createClass()
创建组件,大写开头,然后再render。
var MyComponent=React.createClass({/*...*/});
var myElement=<MyComponent someProperty={true} />;
ReactDOM.render(myElement,document.getElementById('example'));
注意,JSX是JavaScript,所以class,for是保留字,不能作为属性名来使用,我们推荐使用className,htmlFor来作为属性名。
变形
可以在这里输入JSX代码来看看转换的原生JavaScript代码。
首先,React JSX是从一种类XML语言转换成JavaScript代码的。XML元素,属性和子节点都会转换为JS的createElement函数参数。如下:
var Nav;
var app=<Nav color='red' />;//JSX input
var app=React.cretaElement(Nav,{color:'red'});//JS output
JSX允许使用XML的语法指定子节点,如下:
var Nav,Profile;
var app=<Nav color='red'><Profile>click</Profile></Nav>;//JSX input
var app=React.createElement(
Nav,
{color:'red'},
React.createElement(Profile,null,"click")
);//JS output
JSX会推断类的displayName如果不定义的话:
var Nav=React.createClass({});//JSX input
var Nav=React.createClass({displayName:”Nav”});
JSX表达式在React会被认为是一个ReactElement
命名空间组件
如果你在构建一个有很多孩子的组件(比如一个form),你可能陷入下面这种尴尬的境地:
var Form=myFormComponent;
var FormRow=Form.Row;
var FormLable=Form.label;
var FormInput=Form.Input;
//不得不声明大量变量
var app=(
<Form>
<FormRow>
<FormLabel />
<FormInput />
</FormRow>
</Form>
);
为了避免这种尴尬的境地,命名空间组件允许某个组件把其他组件作为属性:
var Form = MyFormComponent;
var App = (
<Form>
<Form.Row>
<Form.Label />
<Form.Input />
</Form.Row>
</Form>
);
//使用命名空间组件避免声明大量变量
为了做到命名空间组件,你必须声明某个组件的某个属性为一个组件:
var Form=React.createClass({...});
var Form.Row=React.createClass({...});
var Form.Label=React.createClass({...});
var Form.Input=React.createClass({...});
JSX会这么处理你的代码:
var app=(
React.createElement(Form,null,
React.createElment(Form.Row,null,
React.createElement(Form.label,null),
React.createElement(Form.Input,null)
)
)
)
这个功能需要v0.11及以上版本
JavaScript表达式
属性表达式
把JavaScript表达式圈在一对花括弧{}里会被解释成属性。例:
var person=<Person name={window.isLoggedIn?window.name:''} />;//JSX input
var person=React.createElement(
Person,
{name:window.isLoggedIn?window.name:''}
);//JS output
Boolean属性
如果你定义了一个属性但是没有指定它的值那么JSX会认为这个属性的值为true。所以如果一个属性值为false时必须指定attr:{false}
或者不定义这个属性。boolean属性往往会出现在HTML表单元素中。
<input type="button" disabled />;
<input type="button" disabled={true} />;
//这两条等价,用于使一个按键失效
<input type="button" />;
<input type="button" disabled={false} />;
//这两条等价,按键不失效
子节点表达式
同样,JavaScript表达式也可用来表达子节点:
var content=<Container>{window.isLoggedIn?<Nav />:<Login />}</Container>;
//JSX input
var content=React.createElement(
Container,
null,
window.isLoggedIn?React.createElement(Nav) :
React.createElement(Login);
);
JS output
注释
JSX注释就是JavaScript里的注释,要注意的就是就是在注释与子元素并行时要使用{}
var content=(
JSX传播属性(spread Attribute)
如果你知道你要指定的组件的所有属性,那很简单:
var component=<Component foo={x} bar={y} />;
可变的属性是bad
如果你不知道你想要指定的属性,你可能会想在后面把属性赋给对象(组件)本身。
var component=<Component />;
conponent.props.foo=x;//bad
conponent.props.bar=y;//also bad
这样是不好的,因为我们一开始无法正确解析组件的属性节点。引入原文:
This is an anti-pattern because it means that we can’t help you check the right propTypes until way later. This means that your propTypes errors end up with a cryptic stack trace.
The props should be considered immutable. Mutating the props object somewhere else could cause unexpected consequences so ideally it would be a frozen object at this point.
传播属性
你可以使用JSX里的spread attribute
var props={}
props.foo=x;
props.bar=y;
var component=<Component {...props} />;//会自动展开的意思吧
你可以反复使用这个语法,也可以跟其他的属性结合,不过顺序很重要,同名的属性以后面出现的为准。
var props={foo:'default'};
var component=<Component {...props} fop={'override'} />;
console.log(component.props.foo);//override
… 操作符是ES6中新支持的语法。传播语法,点击查看
JSX陷阱
JSX看起来像HTML可是有一些不同你可能会感到迷惑。
HTML实体
在JSX里,你可以通过字面量文本插入HTML实体,像这样:
<div>First · Second</div>
如果你想用变量(表达式)来显示一个HTML,你可能会陷入双重转移的问题。以为React会自动转义你想显示的字符串。这样是为了避免XSS攻击。比如:<div>{'First · Second'}</div>
会显示“First · Second”。
避开这个陷阱的方法有:
- 直接写Unicode字符,像这样
<div>{'First · Second'}</div>
,这种方法必须保证文件是utf-8编码的并可以获取utf-8字符集 - 更安全的方式是通过String.fromCharCode(code)取得字符,
<div>{'First'+String.fromCharCode(183)+' Second'}</div>
- 你可以混合字符串和JSX元素组成数组,每个数组里的JSX元素都必须有一个唯一的key
<div>{['First',<span key="middot">·</span>,' Second']}</div>
- 最后一种比较吓人的做法dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{__html:'First ·Second'}} />
自定义HTML属性
如果你想在原生的HTML元素上使用一个自定义属性,那么应该加上前缀data-
:
<div data-custom-attribute="foo" />
然而在自定义的元素上,任何属性命名都是可以的<x-my-component custom-attribute="foo" />
网页无障碍属性应该以aria-
开头<div aria-hidded={true} />