JSX详解

什么是JSX

JSX是JavaScript XML,是React提供的Syntax Sugar, 能让我们可以在JS中写html标记语言。

其表现是如何:

  1. 常规的html代码都可以写,可以通过{props}往html中插入变量或任意有效的JS表达式,而无须加上$
  2. 此外还可以插入带参数的函数{func(props)}
<h1>Hello, {getName(props)}</h1>
  1. JSX被编译后,是一个函数调用,返回值为JS对象,故JSX也可作为表达式,例如用于If判断
  2. 可以在标签中添加属性,属性值若是字符串,则加上引号,若是对象或表达式,则加上{}. ""与{}不能混用。由于JSX更贴近JS,故属性的key建议使用驼峰法写法
const element = <div tabIndex="0"></div>;

const element = <img src={user.avatarUrl}></img>;
  1. 若JSX元素没有子元素/节点,可以单闭合
const element = <img src={user.avatarUrl} />;
  1. 可以给html添加类但class需改写成className,另外若添加自定义的要渲染的属性,最好以data-开头

JSX将XML语法加入到JavaScript中,在JS中写了JSX将会被预处理成React Element

为什么React要创建JSX:
渲染的逻辑处理与UI逻辑其实是耦合的, event, state, data互相关联,既然如此,那么就把html标记语言与逻辑处理相关的js内容放在一起,组成一个松耦合的模块,这个模块就是JSX元素

写JSX实际做了什么

首先怎么才能写JSX呢,在普通的JS文件中需引入react,reactDOM(若要对DOM进行操作)以及babel或者通过Babel在线编译

JSX其实是一个对象,而且这个对象内的值都被进行了转义(escape),见以下的例子:

var esca = <a href='https://baidu.com'><span>5&gt;3{true && '--this is true'}</span></a>

console.log(esca)

ReactDOM.render(
  esca,
  document.getElementById('root')
);

输出结果如下


JSX转义.png

什么? 我的true呢?
让我们把代码放到Babel看下JSX生成了什么东西

//Babel输出
var esca = React.createElement(
  'a',
  { href: 'https://baidu.com' },
  React.createElement(
    'span',
    null,
    '5>3 ',
    true && '$gt;this is true'
  )
);

可以看到react将JSX代码片段分为几块,类型(元素名), 属性值props(包含children和属性参数等), key和ref, owner和store
我们发现没有被{}包住的默认是字符串,会进行转义,而{}包住的则会被当作表达式,不被转义,由于true是真,故显示后面的值,但问题来了,若我们想在表达式里要进行转义成HTML呢?
方法一: 使用Unicode编码

var esca = <a href='https://baidu.com'><span>5&gt;3 {true && '\u003Ethis is true'}</span></a>
//这样在表达式里也可以进行转义了

注意,若是直接使用var esca = React.createElement('span', null, '5&gt;3 this is true;')创建的,字符串和表达式内的均不会进行转义

方法二: 使用dangerouslySetInnerHTML

var esca = <a href='https://baidu.com'>5&gt;3{true &&<span dangerouslySetInnerHTML={{__html: ' &gt;this is true'}}></span>}</a>

//或者定义一个新的JS元素,将需要转义的内容放入
<AllowedHtml html={data}/>

等价于

var esca = React.createElement(
  'a',
  { href: 'https://baidu.com' },
  '5>3',
  true && React.createElement('span', { dangerouslySetInnerHTML: { __html: ' &gt;this is true' } })
);



方法三:在{}通过数组将字符串和表达式包裹在一起

<div>{['First ', <span>&middot;</span>, ' Second']}</div>

此外对于某些字体图标,可以使用以下的方法
<i data-icon={String.fromCharCode( "f00f" )} />

关于JSX防范XSS攻击

XSS是跨站脚本注入攻击, 更多解释可查看这里

  1. 由于当你尝试通过{html}进行插入html代码时, React会自动将html转为字符串,故React可部分防止XSS攻击
    例如以下代码
const username = "<img onerror='alert(\"Hacked!\")' src='invalid-image' />";

class UserProfilePage extends React.Component {
  render() {
    return (
      <h1> Hello {username}!</h1>
    );
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));

显示为


xss_{}.png
  1. JSX中是通过传入函数作为事件处理方式,而不是传入字符串,字符串可能包含恶意代码

尽管如此,还是可以通过一些手段进行XSS攻击,如:<a href="{...}" />, <img src={...} />, <iframe src="{...} />,css注入style={...} prop,还可利用上述所说的一些方法

const aboutUserText = "<img onerror='alert(\"Hacked!\");' src='invalid-image' />";

class AboutUserComponent extends React.Component {
  render() {
    return (
      <div dangerouslySetInnerHTML={{"__html": aboutUserText}} />
    );
  }
}

ReactDOM.render(<AboutUserComponent />, document.querySelector("#app"))

或者通过设置a的href为javascript:xxx

const userWebsite = "javascript:alert('Hacked!');";

class UserProfilePage extends React.Component {
  render() {
    return (
      <a href={userWebsite}>My Website</a>
    )
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));

以及使用base64 编码的数据进行替换

const userWebsite = "data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGFja2VkISIpOzwvc2NyaXB0Pg==";

class UserProfilePage extends React.Component {
  render() {
    const url = userWebsite.replace(/^(javascript\:)/, "");
    return (
      <a href={url}>My Website</a>
    )
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));

又或从用户处接受了被恶意控制的props

const customPropsControledByAttacker = {
  dangerouslySetInnerHTML: {
    "__html": "<img onerror='alert(\"Hacked!\");' src='invalid-image' />"
  }
};

class Divider extends React.Component {
  render() {
    return (
      <div {...customPropsControledByAttacker} />
    );
  }
}

ReactDOM.render(<Divider />, document.querySelector("#app"));

更多可参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容