JSX的背后
这个过程一般在前端会称为“转译”,但其实“汇编”将是一个更精确的术语。
React开发人员敦促你在编写组件时使用一种称为JSX的语法,混合了HTML和JavaScript。但浏览器对JSX及其语法毫无头绪,浏览器只能理解纯碎的JavaScript,所以JSX必须转换成JavaScript。这里是一个div的JSX代码,它有一个class name和一些内容:
<div className='cn'>
Content!
</div>
以上的代码,被转换成“正经”的JavaScript代码,其实是一个带有一些参数的函数调用:
React.createElement(
'div',
{
className: 'cn' },
'Content!'
);
让我们仔细看看这些参数。
- 第一个是元素的type。对于HTML标签,它将是一个带有标签名称的字符串。
- 第二个参数是一个包含所有元素属性(attributes)的对象。如果没有,它也可以是空的对象。
- 剩下的参数都可以认为是元素的子元素(children)。元素中的文本也算作一个child,是个字符串’Content!’ 作为函数调用的第三个参数放置。
你应该可以想象,当我们有更多的children时会发生什么:
<div className='cn'>
Content 1!
<br />
Content 2!
</div>
React.createElement(
'div',
{
className: 'cn' },
'Content 1!', // 1st child
React.createElement('br'), // 2nd child
'Content 2!' // 3rd child
)
我们的函数现在有五个参数:
一个元素的类型
一个属性对象
三个子元素。
因为其中一个child是一个React已知的HTML标签(
),所以它也会被描述为一个函数调用(React.createElement(‘br’))。
到目前为止,我们已经涵盖了两种类型的children:
简单的String
另一种会调用React.createElement。
然而,还有其他值可以作为参数:
基本类型 false, null, undefined, true
数组
React Components
可以使用数组是因为可以将children分组并作为一个参数传递:
React.createElement(
'div',
{
className: 'cn' },
['Content 1!', React.createElement('br'), 'Content 2!']
)
当然了,React的厉害之处,不仅仅因为我们可以把HTML标签直接放在JSX中使用,而是我们可以自定义自己的组件,例如:
function Table({
rows }) {
return (
<table>
{
rows.map(row => (
<tr key={
row.id}>
<td>{
row.title}</td>
</tr>
))}
</table>
);
}
组件可以让我们把模板分解为多个可重用的块。在上面的“函数式”(functional)组件的例子里,我们接收一个包含表格行数据的对象数组,最后返回一个调用React.createElement方法的
元素,rows则作为children传进table。无论什么时候,我们这样去声明一个组件时:
<Table rows={
rows} />
从浏览器的角度来看,我们是这么写的:
React.createElement(Table, {
rows: rows });
注意,这次我们的第一个参数不是String描述的HTML标签,而是一个引用,指向我们编写组件时编写的函数。组件的attributes现在是接收的props参数了。
把组件(components)组合成页面(a page)
所以,我们已经将所有JSX组件转换为纯JavaScript,现在我们有一大堆函数调用,它的参数会被其他函数调用的,或者还有更多的其他函数调用这些参数…这些带参数的函数调用,是怎么转化成组成这个页面的实体DOM的呢?
为此,我们有一个ReactDOM库及其它的render方法:
function Table({
rows }) {
/* ... */ } // defining a component
// rendering a component
ReactDOM.render(
React.createElement(Table, {
rows: rows }), // "creating" a component
document.getElementById('#root')