详情参考官方JSX规范html
虽然JSX是扩展到ECMAScript的类XML语法,可是它自己并无定义任何语义。也就是说它自己不在ECMAScript标准范围以内。它也不会被引擎或者浏览器直接执行。一般会利用很编译器预处理器来将这些JSX转化为标准的ECMAScript。react
吐槽:虽然JSX出发点是好的,并且写起来也很简单,可是对于要在JS中写类HTML格式的内容,个人心里是排斥的,感受很是不习惯。这不是我熟知的web开发啊!有种在开发app的感受,一个个自定义的组件。git
想看看他是怎么编译JSX,因而我看了下用JS的写法写组件,主要的方法就是React.createElement
:github
React.createElement(
type,
[props],
[...children]
)
复制代码
第一个参数type
是类型,也就是名字,好比h1
、div
、自定义组件名
等~ 第二个参数[props]
其实就是各类属性,咱们在JS中怎么写属性的,在这里就怎么写。好比img.src=""
,div.className=""
这样的,那么属性就是这么写的{className:"",src:""}
,属性名和JS保持一致。 第三个参数,其实就是无限延展当前节点的子节点,你想有多少个就有多少个。web
来看一眼官方文档的转化,这个是我用React.createElement
来转义的JSX,这样一个套一个的写法,何时才是个头。强烈的求生欲使我放弃了JS的写法,转投JSX的写法了:数组
相比较这种无限嵌套的写法,JSX友善太多了。从语义化的角度来讲,JSX的可读性也是很好滴。(为本身学习JSX强行找理由。)浏览器
上一节提到,bash
let element=<h1 className="aaa">A爆了</h1>
//等同于
let element=React.createElement(
"h1",
{className:"aaa"},
"A爆了"
)
复制代码
那么是否是我直接let {type,props,children}=element
就能够获得 h1
、{className:"aaa"}
和A爆了
了呢?我仍是太天真了。type
确实是h1
,可是props
打出来是{className: "aaa", children: "A爆了"}
。咦?怎么children
混在了这里,那么后面得children呢?毫无疑问undefined
。也就是说一个React.createElement
或者JSX的对象的结构是这样的:app
{
type:"h1",
props:{
className:"aaa",
children:"A爆了"
}
}
复制代码
JSX有许多中写法,看的我是眼花缭乱,不如来分析分析这些写法的奥秘,为何要这么写,而后找一种本身喜欢的方式来写。这里我会以let element=XXX
为例子,而后你们能够直接ReactDOM.render(element, document.getElementById('root'));
这样渲染。函数
我习惯在写JS的时候,将这些标签写在字符串中,而后拼接起来。看到这么写,真的以为是个bug,浏览器必定会报错的!然而在react中,不会报错的,这是正确的。
let element=<h1 className="aaa">A爆了</h1>
ReactDOM.render(element, document.getElementById('root'));
复制代码
那若是是纯文字呢?华丽丽地报错了。说名JSX仍是须要标签包裹的。
let element=A爆了
复制代码
那么咱们多加几个子元素进,也是OK的,没什么毛病。
let element=<h1 className="aaa">aaa<span>A爆了</span>bbbb</h1>
复制代码
若是是不少个并列地兄弟节点呢?忽然兴奋!报错了~果真不能皮。为何呢?你们都是正正经经的HTML标签啊。
let element=
<h1 className="aaa">A爆了</h1>
<h1 className="aaa">A爆</h1>
复制代码
官方给出的解释是:必须包裹在一个闭合的标签内。意思就是说不能N个闭合标签并列吗?
let element=
<div>
<h1 className="aaa">A爆了</h1>
<h1 className="aaa">A爆</h1>
</div>
复制代码
外面多加一层div就能够。但是这样可能会多出不少个不须要的div啊,咱们干干净净的HTML,会不会变成嵌套地狱啊。官方的求生欲也是很强的,早就想到了这一点,因此有一个官方的组件叫作Fragment
。专门用于包裹这些不须要多加一层div的元素们。这个组件的用法:
//首先别忘了导入,否则直接React.Fragment也是能够的
import React,{Fragment} from 'react';
//而后
let element=
<Fragment>
<h1 className="aaa">A爆了</h1>
<h1 className="aaa">A爆</h1>
</Fragment>
复制代码
前面提到了element
打印的结构:{type:"h1",props:{className:"aaa",children:"A爆了"}}
,好奇心旺盛的我打印了下<Fragment>
的element
是什么样的。结果以下所示,type:Symbol(react.fragment)
,虽然这个根节点是特殊的标签,不是div
,p
这种正正经经的HTML标签,但也是一个节点了。也就是说element
至关于一个根节点,这个根节点只能有一个,而后这个根节点能够有无数的子节点。
{
type:Symbol(react.fragment),
props:{
children:[
{type: "h1", props: {…}}
{type: "h1", props: {…}}
]
}
复制代码
写法四:数组真的不行吗?
好奇心旺盛的我,不肯意屈服于全部的外面都要加一个标签包裹,文档说的是一个闭合的标签,那么[]
这样包裹一个数组可不能够呢?wow~没有报错!也就是说闭合标签不必定指代<></>
也能够是[]
,来表明一个总体。
let element=
[
<li>1</li>,
<li>1</li>,
<li>1</li>
]
复制代码
写法五:长的好看而已
我还看到一种写法,就是在最外层加上()
来包裹整个节点。我一开始觉得这是什么骚操做,会让element
变得不同凡响。因而,我作了个实验,将两个同样的节点进行对比,不一样点在于第一个无()
,第二个有()
,而后结果是true
,也就是说他们本质上没啥不一样。因此小伙伴们,不写()
也不会报错的。
let element=<div><h1 className="aaa">A爆了</h1><h1 className="aaa">A爆</h1></div>
let element1=(<div><h1 className="aaa">A爆了</h1><h1 className="aaa">A爆</h1></div>)
console.log(JSON.stringify(element1)===JSON.stringify(element))
复制代码
那么这个括号有什么用呢?固然有!好看!咱们来看同样Component
里面的render
若是没有()
会怎么样。
render() {
return (
<div className="App">
<p className="App-intro">To get started, edit<code>src/App.js</code> and save to reload.</p>
</div>
)
}
复制代码
本来是有()
的,而后能够换行,把节点排排整齐。看着也很舒服。而后咱们把()
去了会怎么样?整齐是整齐,可是会报错啊!
render() {
return
<div className="App">
.....
}
复制代码
这里就有一个小知识点,js语言设计中return
的内容必须一行完成,不要跟回车,否则就会报错。(真是任性的操做)也就是说像下方这么写就没有问题。可是我就没办法保证整整齐齐的标签了啊!这个排版怎么排都丑。因此这个时候()
就展示出了他的魅力,表明了一个总体,告诉return
我还没结束。因此你们也不要被()
给迷惑了,不要怕他。
render() {
return <div className="App">
.....
}
复制代码
写JSX会发现,虽然我是在写HTML,可是有些属性好奇怪啊,常常写错,好比最多见的className
。我总结出一点咱们写标签的时候是HTML,写属性的时候要用JS思惟。这样就不复杂,也不难记拉!
\\JS中怎么取class属性的呢?
document.getElementById('myid').className
\\遇到特殊的<label id="label" for="xxx"></label>,这个for在JS中怎么获取呢?
document.getElementById('label').htmlFor
复制代码
那么问题来了,有一个名为style
的属性,你要怎么处理?style
就比较复杂了,他不是一个值一个字符串可以搞定搞定的。我先在报错的边缘试探下吧。
试探一:字符串!
let element=<div style="color:green">A爆</div>
复制代码
报错啦!报错啦!官方提示咱们The
styleprop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.
也就是说style
须要从样式属性映射到他的值,字符串是不能够的。因此就须要{marginRight: spacing + 'em'}
像这样的对象才能够。那为何要再加一层{}
?
试探二:单层{}
let element=<div style={color:"green"}>A爆</div>
复制代码
直接编译错误了。也就是说JSX中不能直接包含JS的函数。而要用{}
包裹起来JS函数。因此才有了双层{}
。第一层是表明我是JS,第二层其实就是属性对象自己了。你们不用再试探底线了老老实实:
let element=<div style={{color:"green"}}>A爆</div>
复制代码
若是想再JSX中加注释怎么办?直接<!--XXX-->
确定报错囧。咱们能够用{/*XXX*/}
的方式注释,由于{}
标签里面是js函数,咱们用JS的注释就OK拉!(其实JSX仍是JS啊。)
上文提到{}
中包含的是JS,那么咱们是否是能够玩出更多的花样的?由于{}
中咱们就能够用JS随心所欲了!
好比循环(正确):
let arr=[1,2,3]
let element=(
{arr.map((v,i)=>
<div>{v}-A爆</div>
)}
)
复制代码
若是不想循环直接return
,能够这样,内部加上大括号,再继续写额外操做。别忘了return
,只有=>
函数能够省去return
let arr=[1,2,3]
let element=(
<Fragment>
{arr.map((v,i)=>{
if(v===1){
return <span>A爆了</span>
}else{
return <span>B爆了</span>
}
})}
</Fragment>
)
复制代码
可是若是JS在标签<></>
外部的话,就能够直接使用,而不用加上{}
:
let element=
arr.map((v,i)=>{
if(v==1){
return <div>{v}-A爆</div>
}else{
return <div>{v}-B爆</div>
}
})
复制代码
你们注意了,这里不管如何element
都是一个对象,因此赋予他的值也只能是对象。因此不能直接if/else进行操做,建议再JSX外层操做,而不是直接再JSX中的外层操做。
好比这样,那就只能等吃红牌了。
let element=(
if(v===1){
return <span>A爆了</span>
}else{
return <span>B爆了</span>
}
)
复制代码
最好是这样:
let v=1;
let element;
if(v===1)
element=<span>A爆了</span>
}else{
element=<span>B爆了</span>
}
复制代码
在Comopent的render中,我么能够这么写:
render() {
if(v===1){
return <span>A爆了</span>
}else{
return <span>B爆了</span>
}
}
复制代码
研究完以后,发现JSX就是JS啊,以及每次用JSX“语法”写的元素,都要返回一个数组或者是对象。只要牢记这一点,就能够玩转JSX。