前言:文章不介绍任务背景知识,没有原理说明,偏向于实践的总结和经验分享。
文章所有的代码是基于Vue CLI 3.x版本,不会涉及到一步步通过Webpack来配置JSX所需要的知识点。
在使用Vue开发项目时绝大多数情况下都是使用模板来写HTML,但是有些时候页面复杂又存在各种条件判断来显示/隐藏和拼凑页面内容,或者页面中很多部分存在部分DOM结构一样的时候就略显捉襟见肘,会写大量重复的代码,会出现单个.vue文件过长的情况,这个时候我们就需要更多的代码控制,这时候可以使用渲染函数。
渲染函数想必平时几乎没有人去写,因为写起来很痛苦(本人也没有写过)。更多的是在Vue中使用JSX语法。写法上和在React中差不多,但是功能上还是没有React中那么完善。
在写JSX的过程中不得考虑一个样式的问题,虽然可以直接在.vue文件中不写<tempate>部分,只写<script>和<style>部分,而不用担心样式作用域问题。但是更多的时候还是推荐直接使用.js的方式来写组件,这个时候就涉及到样式作用域的问题了。
在React的生态中,有很多CSS-IN-JS的解决方案,比如styled-jsx、emotion、styled-components等,目前最活跃和用户量最多的是styled-components,目前已经拥有良好的生态圈子。如果需要在样式中作一些像Sass/Less中的颜色计算,可以使用polished来实现,当然不止这么简单的功能。但是在Vue中可使用的方案就太少了,因为Vue使用模板来写HTML本身是开箱即用的样式scoped,在使用JSX写组件的时候就面临着样式问题,一种方案是在组件包裹<div>中取一个特殊的名字,然后样式都嵌套写在这个class下面,但是难免会遇到命名冲突的情况,而且每次还得变着花样取名称。此外,就是引入CSS-IN-JS在Vue对应的实现,但目前来看Styled-components官方提供了一个Vue版本的叫vue-styled-components和emotion的vue-emotion,但是用的人实在太少。像styled-components进行了重大更新和变化,但是Vue版本的还是最初的版本,而且有时候还出现样式不生效的情况。
接下来进入正题,从简单语法到经验分享(大牛请绕行)
基本用法
首先需要约定一下,使用JSX组件命名采用首字母大写的驼峰命名方式,样式可以少的可以直接基于vue-styled-components写在同一个文件中,复杂的建议放在单独的_Styles.js_文件中,当然也可以不采用CSS-IN-JS的方式,使用Less/Sass来写,然后在文件中import进来。
下面是一个通用的骨架:
import styled from 'vue-styled-components' const Container = styled.div` heigth: 100%; ` const Dashboard = { name: 'Dashboard', render() { return ( <Container>内容</Container> ) } } export default Dashboard
插值
在JSX中使用单个括号来绑定文本插值
<span>Message: {this.messsage}</span> <!-- 类似于v-html --> <div domPropsInnerHTML={this.dangerHtml}/> <!-- v-model --> <el-input v-model={this.vm.name} />
在jsx中不需要把v-model分成事件绑定和赋值二部分分开来写,因为有相应的babel插件来专门处理。
样式
在JSX中可以直接使用class="xx"来指定样式类,内联样式可以直接写成style="xxx"
<div>Button</div> <!-- 动态指定 --> <div class={`btn btn-${this.isDefault ? 'default' : ''}`}></div> <div class={{'btn-default': this.isDefault, 'btn-primary': this.isPrimary}}></div> <div style={{color: 'red', fontSize: '14px'}}></div>
遍历
在JSX中没有v-for和v-if等指令的存在,这些全部需要采用Js的方式来实现
{/* 类似于v-if */} {this.withTitle && <Title />} {/* 类似于v-if 加 v-else */} {this.isSubTitle ? <SubTitle /> : <Title />} {/* 类似于v-for */} {this.options.map(option => { <div>{option.title}</div> })}
事件绑定
事件绑定需要在事件名称前端加上on前缀,原生事件添加nativeOn
<!-- 对应@click --> <el-buton onClick={this.handleClick}>Click me</el-buton> <!-- 对应@click.native --> <el-button nativeOnClick={this.handleClick}>Native click</el-button> <!-- 传递参数 --> <el-button onClick={e => this.handleClick(this.id)}>Click and pass data</el-button>
注意:如果需要给事件处理函数传参数,需要使用箭头函数来实现。如果不使用箭头函数那么接收的将会是事件的对象event属性。
高级部分
在Vue中基于jsx也可以把组件拆分成一个个小的函数式组件,但是有一个限制是必需有一个外层的包裹元素,不能直接写类似:
const Demo = () => ( <li>One</li> <li>Two</li> )
必需写成:
const Demo = () => ( <div> <li>One</li> <li>Two</li> </div> )