JSX详解

作者: Shaw007 | 来源:发表于2019-01-08 16:39 被阅读2231次

    什么是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"));
    

    更多可参考

    相关文章

      网友评论

        本文标题:JSX详解

        本文链接:https://www.haomeiwen.com/subject/teqbrqtx.html