通过 JSX Control Statements 编写 JSX

    xiaoxiao2021-08-18  172

    通过 JSX Control Statements 编写 JSX

    一、前言

    一些开发者,特别是有使用过「模板技术」的开发者(例如 Handlebars),刚开始尝试使用 React 编写应用时,可能会惊讶的发现,JSX 竟然没有内建支持类似其它模板引擎似的任何结构控制语句或指令。但这就是 JSX,它就是这么设计的,JSX 不是传统的模板,也不需要某个模板引擎去解析。大体上,可以将 JSX 看成普通 JavaScript 表达式的语法糖。

    下边的的代码是一个「普通的 JavaScript 代码示例」

    render () { return (if(true){ ... }else{ ... }); }

    如上代码不合法的,同样,下边的 jsx 代码也是不合法的

    render () { return (<div> {if(true) <span>1</span> else <span>2</span> } </div>); }

    因为,JavaScript 不能在一个「表达式」中嵌入「控制语句」。

    二、能做什么?

    JSX-Control-Statements 为 JSX 增加了基本的结构控制语句,比如条件和循环控制语句。通过将插件将「组件风格」的控制语句最终转换为普通 JS 代码,例如:

    <If condition={condition()}>Hello World!</If>

    将会转换为

    condition() ? 'Hello World!' : null

    三、实现原理及说明

    这看起来,似乎很容易实现,通过一系列表组件好像就能实现,但事实上这在 React 中这是不可行的,如果用「组件」的方式实现,类似如下代码

    <ul> <ForComponent each="item" index="index" of={list}> <li key={index}>{item.title}</li> </ForComponent> </ul>

    组件的方式实现上边的代码,将会抛出来一个错误 Cannot read property 'title' of undefined

    因为 React 在执行到 ForComponent 时就是会执行它的 children ,想达到目的,需要延后执行 FormComponent 的 children,那么只能用一个 function 包裹,并在这个函数中返回 jsx 表达式,但这将比直接写 map 方法还麻烦。

    所以,JSX Control Statements 是一个 Babel 插件,采取了在「编译阶段」将「控制标签」转换为「普通的 JavaScript 表达式」的方案,最终编译结果和原来的写法没有什么不同。

    四、安装

    需要先行安装好 Babel,之后通过 npm 安装 jsx-control-statements

    npm install --save-dev jsx-control-statements

    还需要在 .babelrc 中配置插件,如下

    { ... "plugins": ["jsx-control-statements"] }

    五、核心语法

    If 标签

    用来表示最简单的条件判断逻辑

    // 简单示例 <If condition={ true }> <span>IfBlock</span> </If> // 包括多个子元素及表达式 <If condition={ true }> one { "two" } <span>three</span> <span>four</span> </If>

    <If>

    if 的 body 部分只有在 condition 为 true 才会渲染

    属性名类型必须conditionboolean是

    <Else /> (deprecated)

    Eles 已废弃,不推荐使用,它会破坏 JSX/XML 的语法,并且影响自动格式化。

    转换

    If 标签将会预编译为「三元表达式」

    // 转换前 <If condition={ test }> <span>Truth</span> </If> // 转换后 { test ? <span>Truth</span> : null }

    Choose 标签

    Choose 是比 If 更复杂分支结构写法

    <Choose> <When condition={ test1 }> <span>IfBlock</span> </When> <When condition={ test2 }> <span>ElseIfBlock</span> <span>Another ElseIfBlock</span> <span>...</span> </When> <Otherwise> <span>ElseBlock</span> </Otherwise> </Choose> // default block is optional; minimal example: <Choose> <When condition={true}> <span>IfBlock</span> </When> </Choose>

    <Choose>

    Choose 的子元素只允许出现 When 和 Otherwise,其中最少需要有一个 When,而 Otherwise 是可选的

    <When>

    和 <If> 类似

    属性名类型必须conditionboolean是

    <Otherwise>

    没有任何一个 When 满足条件时,将会渲染 Otherwise

    转换

    Choose 标签同样将会预编译为「三元表达式」

    // 转换前 <Choose> <When condition={ test1 }> <span>IfBlock1</span> </When> <When condition={ test2 }> <span>IfBlock2</span> </When> <Otherwise> <span>ElseBlock</span> </Otherwise> </Choose> // 转换后 { test1 ? <span>IfBlock1</span> : test2 ? <span>IfBlock2</span> : <span>ElseBlock</span> }

    For 标签

    For 的命名用方法,如下

    // 注意,需要指定 key 属性 <For each="item" of={ this.props.items }> <span key={ item.id }>{ item.title }</span> </For> <For each="item" index="idx" of={ [1,2,3] }> <span key={ idx }>{ item }</span> <span key={ idx + '_2' }>Static Text</span> </For> 属性名类型必须说明ofArray/collection是列表或包含 map 方法的集合eachstring 循环「项」变量名indexstring 循环「索引」变量名

    注意,For 不能作为根元素

    转换

    // 转换前 <For each="item" index="index" of={ items )}> <span key={ item.id }>{ index }. { item.title }</span> </For> // 转换前 { items.map( function(item, index) { <span key={ item.id }>{ index }. { item.title }</span> }) }

    With 标签

    用于将值赋给局部变量

    // 简单用法 <With foo={ 47 } bar={ 'test' }> <span>{ foo }</span> <span>{ bar }</span> </With> // 嵌套使用 <With foo={ 47 }> <With bar={ 'test' }> <span>{ foo }</span> <span>{ bar }</span> </With> </With> 属性名类型必须说明anyany 将值赋给指定名称的局部变量

    注意,定义的「变量」仅在 With 块中可用。

    转换

    <With> 将会转换为一个「匿名的立即执行函数」

    // 转换前 <With foo={ 47 }> <span>{ foo }</span> </With> // 转换后 { (function(foo) { return <span>{ foo }</span> }).call(this, 47) }

    六、对比普通 JS/JSX 写法的对比

    带来的好处

    更直观、对于习惯于模板语法的开发人员或设计人员,更接近于传统模板写法减少 JS/JSX 相互隔断导致的代码「支离破碎」更好的可读性和整洁,不过这取决于你的个人喜好。

    一点小问题

    多了一步编译,会多用一点点构建时间依赖 Bable 6,并需要配置一下 .babelrc

    七、如何进行语法检查?

    ESLint

    所有结构控制标签都是通过 Babel 插件进行转义的, 不需要 require or import,所有在进行「语法检查」时,将会出现「变量未定义」的警告或错误。

    但是,有一个 ESlint 插件可处理这个问题ESLint plugin for JSX-Control-Statements

    -- end --

    相关资源:七夕情人节表白HTML源码(两款)

    最新回复(0)