1.动态表单
有时候我们的后台管理系统中会要求动态创建,更改表单。但是我们用template写表单的时候,如果不能通过json配置表单,这种需求就很难实现,即使封装成组件,我们不得不使用无数的v-if,来判断我们该渲染什么组件。
<el-input v-if="item.type === 'input'" />
<el-select v-else-if="item.type === 'select'" >...</el-select>
类似于这样的代码,当然,这的确能完成需求,但是,总有一种繁琐的感觉, 而且如果有什么变态需求, 可能也不太好写。 这时候, jsx就可以帮得上忙。
init() {
let arr = []
this.items.forEach((element) => {
arr.push(this.type[element.type]())
})
},
......
render() {
return <div>
{this.init()}
</div>
}
在vue-cli 3.0以上的版本中,已经可以直接使用jsx.
写起来也非常简单, 我们创建一个vue文件, 不用写template标签, 直接写一个script标签
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
message: 'HelloWorld'
}
},
render() {
return <div>{this.message},{this.msg}</div>
}
}
</script>
这就是这个文件的全部内容, 然后我们只要正常的当作组件引入其他文件里就可以了.
使用也是普通组件的使用方式.
当我们正常引入elementui后, 就可以使用ui中的组件来构建我们的界面了, 就像下边这样
// render.vue
<script>
export default {
data() {
return {
msg: 'Hello World',
initForm: {
input: this.createInput,
select: this.createSelect,
},
form: {},
}
},
props: {
items: {
type: Array,
default: () => [],
},
},
methods: {
onClick() {
console.log(this.form)
},
createInput(options) {
return <elInput v-model_trim={this.form[options.name]}></elInput>
},
createSelect(options) {
const option = options.option.map((item) => (
<el-option label={item.label} value={item.value}></el-option>
))
return <el-select v-model={this.form[options.name]}>{option}</el-select>
},
init() {
let arr = []
this.items.forEach((element) => {
arr.push(this.initForm[element.type](element.options))
})
return arr
},
returnForm() {
return this.form
},
},
render() {
return (
<div>
{this.init()}
<el-button onClick={this.onClick}>提交</el-button>
</div>
)
},
}
</script>
// test.vue
<template>
<div>
<render :items="items"></render>
</div>
</template>
<script>
import render from '../components/render.vue'
export default {
components: {
render,
},
data() {
return {
items: [
{
type: 'input',
options: {
name: 'name',
},
},
],
}
},
}
</script>
可以看到, 通过一个数组, 我们成功的创建了一个输入框, 并且绑定了v-model, 通过内置方法, 也可以将数据传给其他组件, 摆脱了繁重的template, 用判断替代了v-if, 用for替代了v-for。 获得了更灵活地创建页面的方法。但因为js限制, 要使用v-model.trim这种语法糖, 我们只能将它化为v-mode_trim。
在实际运用中,我们肯定会用到插槽, 而jsx的插槽也很简单
// test.vue
<render :items="items">
<div>测试</div>
</render>
// render.vue
return (
<div>
{this.init()}
{this.$slots.default}
</div>
)
就像这样, 我们通过this.$slot中的信息(也就是VNode)渲染出了需要的组件, 我们甚至可以用forin来循环$slot,这样就算父组件使用了再多具名插槽, 我们也不用在子组件中一一写出。
同样的, 我们看到,vue组件的信息基本都能在this中找到, 当我们使用jsx时, 有需要的话就可以使用其中的信息。如this.$attr, this.$listeners等, 熟练使用可以写出扩展性极佳的组件。
2.动态表格
同样, 我们可以用jsx来封装表格, 就像封装表单一样, 值得注意的是, 我们对表格有着自定义单元格的需求, 但是有了this和render, 这点不难做到。
自定义单元格我们可以视为一个slot, 通过slot直接渲染, 我们也可以通过json(严格来说不是json了)直接渲染, 因为可以直接在数组中使用jsx, 我们只要把渲染所需要的数据传出去就好了。
// table.vue
initTable() {
const columns = this.tableHeaders.map(i => {
if (i.slot) {
return this.$slots[i.slot][0]
} else if (i.render) {
const custom = scope => i.render(scope, scope.row)
return <el-table-column label={i.label}>{custom}</el-table-column>
} else {
return <el-table-column prop={i.prop} label={i.label}></el-table-column>
}
})
return columns
}
······
render() {
const prop = {
attrs: this.$attrs,
on: this.$listeners
}
return (
<el-table data={this.tableData} {...prop} style='width: 100%'>
{this.initTable()}
</el-table>
)
},
// test.vue
<my-table :tableData="tableData" :tableHeaders="tableHeaders">
<template v-slot:date>
<el-table-column
label="日期"
width="180"
>
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span style="margin-left: 10px">{{ scope.row.date }}</span>
</template>
</el-table-column>
</template>
</my-table>
<script>
tableHeaders = [
{label: '日期', prop: 'date', slot: 'date'},
{label: '名字', prop: 'name'},
{label: '地址', prop: 'address', render: (index, row) => {
return <span style="color: red">{row.address}</span>
}
},
]
</script>
通过jsx, 我们能很轻松的完成动态需求。