JSX 支持换行
let jsx = (
<div>
<h1>hello world</h1>
<button/>
</div>
)
div
(后续React推出了不占据Dom结构的Fragment
,同时,<></>
空标签有同样的效果,但是测试后发现这个在vue中不生效,)()
,这样可以方便阅读,并且jsx可以进行换行书写;如果是单标签,必须以/>结尾
{
//我是单行注释
}
{/*我是一段注释*/}
const t = 'hello world';
let jsx = (
<div>
<h1>{t}</h1>
<button/>
</div>
)
const arg1 = 1;
const arg2 = 2;
let jsx = (
<div>
<h1>{arg1 + arg2}</h1>
<button/>
</div>
)
const t = 'hello world';
const arg1 = 1;
const arg2 = 2;
const hasButton = true;
let jsx = (
<div>
<h1>
{
t === 'hello world' ? arg1 : arg2
}
</h1>
{
//如果hasButton为true,则渲染button组件
hasButton && <button/>
}
</div>
)
const t = 'hello world';
const arg1 = 1;
const arg2 = 2;
const hasButton = true;
const func1 = ()=>{ return (<div>func1</div>) }
let jsx = (
<div>
<h1>
{
//如果在render外定义的函数,请注意加this:this.func1()
func1()
}
</h1>
</div>
)
const title = 'hello world';
let jsx = (
<div>
<h1 title={title}>hello world</h1>
<button/>
</div>
)
在jsx中,class属性需要指定为className,因为class在JavaScript中是保留字段
const hasCss = true;
const h1Css = [
'flex',
hasCss ? 'css' : 'noCss',
]
let jsx = (
<div>
<h1 className='flex'>hello world</h1>
<h1 className={h1Css}>hello world</h1>
<button/>
</div>
)
在jsx中,windows风格的命名方式(属性、style、方法、event)都需要转换成驼峰式的写法,比如,正常写一个style指定左边的外边距:margin-left:‘10px’,
这里需要改成:marginLeft:‘10px’
const title = 'hello world';
let jsx = (
<div>
<h1 style={{ marginLeft:'10px',width:'100%' }}>hello world</h1>
<button/>
</div>
)
这条规则不适用于Vue,因为在Vue中对this做了特殊处理
]function func1(){
console.log(this); // undefined
}
render(){
let jsx = (
<div>
<button onClick={this.func1}/>
</div>
)
return jsx;
}
如果我们需要在事件中通过this
来访问React
组件本身属性和方法,有以下几条解决方案:
function func1(arg1, arg2, e){
console.log(this); // ReactCom
console.log(arg1); // param1
console.log(arg2); // param2
console.log(e); // Event from buttonClick
}
render(){
let jsx = (
<div>
<button onClick={this.func1.bind(this,'param1','param2')}/>
</div>
)
return jsx;
}
使用bind绑定的方式除了可以非常简单的获取到事件对象event之外,还可以传递我们想要传递的参数
function func1(arg1, arg2, e){
console.log(this); // ReactCom
console.log(arg1); // param1
console.log(arg2); // param2
console.log(e); // Event from buttonClick
}
render(){
let jsx = (
<div>
<button onClick={(e)=> {
this.func1('param1','param2', e);
}}/>
</div>
)
return jsx;
}
如果想要传递我们自己的参数,还是需要用到bind
]const func1 = (e) => {
console.log(this); // ReactCom
console.log(e); // Event from buttonClick
}
render(){
let jsx = (
<div>
<button onClick={this.func1}/>
<button onClick={this.func1.bind(this,'param1')}/>
</div>
)
return jsx;
}
这里高阶的同学要注意!
如果是在JSX中使用事件绑定,请不要使用箭头函数的方式去声明方法甚至直接在JSX中使用箭头函数绑定事件。
因为根据VR的render渲染机制,如果使用箭头函数,那么每当组件的state发生改变,推动render渲染执行的时候,如果存在箭头函数,每次浏览器都会分配新的内存和额外的开销来执行事件的绑定,组件绑定的层级越深,额外开销越大。
所以,为了最优的性能考虑,请在constructor
构造函数中对需要绑定的事件做显示绑定
constructor() {
this.func1 = this.func1.bind(this);
}
function func1(e){
console.log(this); // ReactCom
console.log(e); // Event from buttonClick
}
render(){
let jsx = (
<div>
<button onClick={this.func1}/>
</div>
)
return jsx;
}
jsx
中,不允许使用if
、if-else
,请使用三元运算符
或者逻辑与&&
for
循环,请使用JS中的高阶函数map
、filter
……const t = 'hello world';
const arg1 = 1;
const arg2 = 2;
const hasButton = true;
const list = [1,2,3,4,5,6,7,8,9];
let jsx = (
<div>
<h1>
{
t === 'hello world' ? arg1 : arg2
}
</h1>
{
//如果hasButton为true,则渲染button组件
hasButton && <button/>
}
<ul>
{
list.map((item) => <li>{item}</li>)
}
</ul>
</div>
)
要更透彻的了解和学习JSX,浅析其本质,那么一定要先了解createElement
因为提到JSX
,不可避免的需要提到createElement
,所以,是不是奇奇怪怪的知识又增加了 : )
JSX实际上仅仅是React.createElement(type, config, children)方法的语法糖,该方法接收三个参数:
我们都知道,JSX是通过babel
进行解析的,而我们编写JSX的时候必须依赖babel
我们可以再babel的官网查看JSX的转换过程:传送门
<!-- 转换示例代码 -->
<div>
<h1 className='flex'>hello world</h1>
<h1 style={{marginLeft:'10px'}}>hello world</h1>
<button/>
</div>
如果我们直接使用React.createElement()来编写代码,就不需要以来bable进行解析也可以正常的渲染显示
render(){
return 'use strict';
/*#__PURE__*/
React.createElement("div", null, /*#__PURE__*/React.createElement("h1", {
className: "flex"
}, "hello world"), /*#__PURE__*/React.createElement("h1", {
style: {
marginLeft: '10px'
}
}, "hello world"), /*#__PURE__*/React.createElement("button", null));
}
我们通过React.createElement()方法最后返回得到的是一个ReactElement
对象,
这个ReactElement
对象作用是什么?
其实React利用ReactElement
对象组成了一个JavaScript对象树,这个对象树就是我们经常讲的一个概念--虚拟DOM(VR DOM)
,我们可以将之前jsx返回的结果进行打印来查看对应的ReactElemnt对象:
render(){
const arg1 = 1;
const arg2 = 4;
let jsx = (
<div>
<div>{arg1 + arg2}</div>
<div className="flex">
<button/>
</div>
<div className={{marginLeft:'10px'}}>hellow world</div>
</div>
)
console.log(jsx);
return jsx;
},
我们编写的JSX代码经过bable编译解析成对应的React.createElement()
方法形式,
经过React.createElement()
方法调用返回我们对应的ReactElement对象树
(虚拟DOM树),对应的ReactElement对象树
经过ReactDOM.render()
方法转换为真正的DOM在我们的浏览器进行渲染。
JSX -> VR DOM -> DOM
React 从来没有说过 “React 比原生操作 DOM 快”。
React 的基本思维模式是每次有变动就整个重新渲染整个应用。
如果没有 Virtual DOM,简单来想就是直接重置 innerHTML。
很多人都没有意识到,在一个大型列表所有数据都变了的情况下,重置 innerHTML 其实是一个还算合理的操作... 真正的问题是在 “全部重新渲染” 的思维模式下,即使只有一行数据变了,它也需要重置整个 innerHTML,这时候显然就有大量的浪费。
我们可以比较一下 innerHTML
vs. Virtual DOM
的重绘性能消耗:
Virtual DOM render + diff 显然比渲染 html 字符串要慢。
但是!它依然是纯 js 层面的计算,比起后面的 DOM 操作来说,依然便宜了太多。
可以看到,innerHTML
的总计算量不管是 js 计算还是 DOM 操作都是和整个界面的大小相关,但 Virtual DOM
的计算量里面,只有 js 计算和界面大小相关,DOM 操作是和数据的变动量相关的。
前面说了,和 DOM
操作比起来,js
计算是极其便宜的。这才是为什么要有 Virtual DOM
:
它保证了:
innerHTML
的思路去写你的应用。