React 初窥:JSX 详解
JSX我们在上文中已经很多次的提及了 JSX,大家也对于基本的基于 JSX 编写 React 组件所有了解。实际上在 JSX 推出之初饱受非议,很多人觉得其很怪异。的确虽然与正统的 HTML 相比其都是类 XML语法的声明式标签语言,但是其对于类名强制使用 className、强制要求标签闭合等特点会让不少的传统前端开发者不太适应。JSX 的引入对笔者之前的工作流的冲击在于不能够直接使用 UI 部门提供的页面模板,并且因为组件化的分割与预编译,UI 比较麻烦地直接在浏览器开发工具中调整CSS样式然后保存到源代码中。JSX 本质上还是属于 JavaScript,这就避免了我们重复地学习不同框架或库中的指令约定,而可以直接使用 JavaScript 来描述模板渲染逻辑;而在前端框架的工作流中,往往将 JSX 的转化工作交托于 Babel 等转化工具,我们可以通过如下方式指定 JSX 使用的构建函数: /** @jsx h */ JSX 的前世今生JSX 语言的名字最早出现在游戏厂商 DeNA,不过其偏重于加入增强语法使得JavaScript 变得更快、更安全、更简单。而 React 则是依赖于 ECMAScript 语法本身,并没有添加扩充语义。React 引入 JSX 主要是为了方便 View 层组件化,承载了构建 HTML 结构化页面的职责。这一点与其他很多的 JavaScript 模板语言异曲同工,不过React 将 JSX 映射为虚拟元素,并且通过创建与更新虚拟元素来管理整个 Virtual DOM 系统。譬如我们 JSX 语法声明某个虚拟组件时,会被转化为 // 必须要在 JSX 声明文件中引入 React import React from 'react'; <MyButton color="blue" shadowSize={2}> Click Me </MyButton> 会被编译为: React.createElement( MyButton,{color: 'blue',shadowSize: 2},'Click Me' ) 而如果我们直接声明某个DOM元素,同样会转化为createElement函数调用: React.createElement( 'div',{className: 'sidebar'},null ) 实际上除了最著名的 Babel JSX 转换器之外,我们还可以使用 /** @jsx JSXDOM */ var defaultValue = "Fill me ..."; document.body.appendChild( <div> <input type="text" value={defaultValue} /> <button onclick="alert('clicked!');">Click Me!</button> <ul> {['un','deux','trois'].map(function(number) { return <li>{number}</li>; })} </ul> </div> ); 这里我们还想讨论另一个问题,为什么需要引入 JSX。在 ECAMScript 6 的 ECMA-262 标准中引入了所谓的模板字符串(Template Literals),即可以在 ECMAScript 中使用内嵌的 DSL 来引入 JavaScript 变量,不过虽然模板字符串对于较长的嵌入式 DSL 作用极佳,但是对于需要引入大量作用域中的 ECMAScript 表达式会造成大量的噪音副作用,譬如如果我们要声明某个评论框布局,使用 JSX 的方式如下: // JSX var box = <Box> { shouldShowAnswer(user) ? <Answer value={false}>no</Answer> : <Box.Comment> Text Content </Box.Comment> } </Box>; 而使用模板字符串的方式如下: // Template Literals var box = jsx` <${Box}> ${ shouldShowAnswer(user) ? jsx`<${Answer} value=${false}>no</${Answer}>` : jsx` <${Box.Comment}> Text Content </${Box.Comment}> ` } </${Box}> `; 其主要缺陷在于因为存在变量的嵌套,需要在作用域中进进出出,很容易造成语法错误,因此还是 JSX 语法为佳。 JSX 语法JSX 的官方定义是类 XML 语法的 ECMAscript 扩展,完美地利用了 JavaScript 自带的语法和特性,并使用大家熟悉的 HTML 语法来创建虚拟元素。JSX 基本语法基本被 XML 囊括了,但也有很多的不同之处。React 在定义标签时,标签一定要闭合,否则无法编译通过。这一点与标准的 HTML 差别很大,HTML 在浏览器渲染时会自动进行补全,而强大的 JSX 报错机制则直接在编译阶段就以报错的方式指明出来。HTML 中自闭合的标签(如 变量使用
在 HTML 中,我们会使用 render() { return ( <div> <!-- This doesn't work! --> </div> ) } 我们需要以 JavaScript 中块注释的方式进行注释: {/* A JSX comment */} {/* Multi line comment */}
JSX 允许使用任意的变量,因此如果我们需要使用数组进行循环元素渲染时,直接使用 map、reduce、filter 等方法即可: function NumberList(props) { const numbers = props.numbers; return ( <ul> {numbers.map((number) => <ListItem key={number.toString()} value={number} /> )} </ul> ); }
在JSX中我们不能再使用传统的if/else条件判断语法,但是可以使用更为简洁明了的Conditional Operator运算符,譬如我们要进行if操作: {condition && <span>为真时进行渲染</span> } 如果要进行非操作: {condition || <span>为假时进行渲染</span> } 我们也可以使用常见的三元操作符进行判断: {condition ? <span>为真时进行渲染</span> : <span>为假时进行渲染</span> } 如果对于较大的代码块,建议是进行换行以提升代码可读性: {condition ? ( <span> 为假时进行渲染 </span> ) : ( <span> 为假时进行渲染 </span> )} 元素属性
JSX 中的 style 并没有跟 HTML 一样接收某个 CSS 字符串,而是接收某个使用 camelCase 风格属性的 JavaScript 对象,这一点倒是和DOM 对象的 style 属性一致。譬如: const divStyle = { color: 'blue',backgroundImage: 'url(' + imgUrl + ')',}; function HelloWorldComponent() { return <div style={divStyle}>Hello World!</div>; } 注意,内联样式并不能自动添加前缀,这也是笔者不太喜欢使用 CSS-in-JS 这种形式设置样式的的原因。为了支持旧版本浏览器,需要提供相关的前缀: const divStyle = { WebkitTransition: 'all',// note the capital 'W' here msTransition: 'all' // 'ms' is the only lowercase vendor prefix }; function ComponentWithTransition() { return <div style={divStyle}>This should work cross-browser</div>; }
React 中是使用
因为
HTML 表单元素中我们经常会使用 disabled、required、checked 与 readOnly 等 Boolean 值性质的书,缺省的属性值会导致 JSX 认为 bool 值设为 true。当我们需要传入 false 时,必须要使用属性表达式。譬如
如果在 JSX 中向 DOM 元素中传入自定义属性,React 是会自动忽略的: <div customProperty='a' /> 不过如果要使用HTML标准的自定义属性,即以 <div data-attr='attr' /> 子元素JSX 表达式中允许在一对开放标签或者闭合标签之间包含内容,这即是所谓的子元素,本部分介绍 JSX 支持的不同类别的子元素使用方式。
我们可以将字符串放置在一对开放与闭合的标签之间,此时所谓的 <MyComponent>Hello World!</MyComponent> 就是合法的 JSX 声明,此时 <div>Hello World</div> <div> Hello World </div> <div> Hello World </div> <div> Hello World </div>
<MyContainer> <MyFirstComponent /> <MySecondComponent /> </MyContainer> 我们可以混合使用字符串与 JSX,这也是 JSX 很类似于 HTML 的地方: <div> Here is a list: <ul> <li>Item 1</li> <li>Item 2</li> </ul> </div> 某个 React 组件不可以返回多个 React 元素,不过单个 JSX 表达式是允许包含多个子元素的;因此如果我们希望某个组件返回多个并列的子元素,就需要将它们包裹在某个
<MyComponent>foo</MyComponent> <MyComponent>{'foo'}</MyComponent> 这种模式常用于渲染 HTML 列表: function Item(props) { return <li>{props.message}</li>; } function TodoList() { const todos = ['finish doc','submit pr','nag dan to review']; return ( <ul> {todos.map((message) => <Item key={message} message={message} />)} </ul> ); }
// Calls the children callback numTimes to produce a repeated component function Repeat(props) { let items = []; for (let i = 0; i < props.numTimes; i++) { items.push(props.children(i)); } return <div>{items}</div>; } function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>This is item {index} in the list</div>} </Repeat> ); }
<div /> <div></div> <div>{false}</div> <div>{null}</div> <div>{undefined}</div> <div>{true}</div> 避免 XSS 注入攻击最后需要提及的是,React 中 JSX 能够帮我们自动防护部分 XSS 攻击,譬如我们常见的需要将用户输入的内容再呈现出来: const title = response.potentiallyMaliciousInput; // This is safe: const element = <h1>{title}</h1>; 在标准的 HTML 中,如果我们不对用户输入作任何的过滤,那么当用户输入
function createMarkup() { return {__html: 'First · Second'}; } function MyComponent() { return <div dangerouslySetInnerHTML={createMarkup()} />; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |