在vue中要想实现一个props,然后再渲染这个props不像react当中那么简单,react中这种jsx的语法,传递渲染函数和普通参数一样没什么区别,例如antd中的a-table -> column
中的render:
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text) => <a>{text}</a>,
},
{
title: 'Action',
key: 'action',
render: (_, record) => (
<Space size="middle">
<a onClick={editHandler}>编辑</a>
<a>删除</a>
</Space>
),
},
];
在react中实现这样的一个功能还是很容易的,但是在Vue中通常可能需要给column设置一个slot name,然后template
中使用作用域插槽。
实现类似功能:
父组件:
<template>
<my-table :columns="columns" :dataSource="dataSource"></my-table>
</template>
<script>
export default{
data() {
return {
columns: [{
label: '姓名',
prop: 'name',
width: 160
}, {
label: '年龄',
prop: 'age',
formatter: (value) => value * 2
}, {
label: '操作',
prop: 'action',
context: this,
bindMethodKeys: ['remove'],
customerRender(row){
return `<div><el-link type="primary" @click="remove(row, index)">删除</el-link></div>`
}
}],
}
},
methods: {
remove(row, index) {
console.log(row,index,column)
this.$message.success(`删除第${index+1}行成功!`)
}
}
}
</script>
子组件实现我这边用的是自己实现render
函数,用模板应该也可以。
注意上面需要传入context
上下文和bindMethodKeys
这两个参数,否则拿不到父组件的上下文和绑定的函数。
<script>
export default{
props: {
columns: Array,
dataSource: Array,
}
render(h){
// ...
return h(
'el-table',
{
props: {
border: true,
stripe: true,
data: this.dataSource,
},
style: { width: '100%' }
},
[...createColumns(this.columns)]
)
}
}
</script>
实现一下createColumns
的逻辑:
const columnRender = (column, props) => {
if(!column.customerRender && !column.slot){
let text = column.formatter ? column.formatter(props.row[column.prop], props.row, props.$index) : props.row[column.prop]
return h('span', text)
}
// 这边是处理作用域插槽的逻辑
if(column.slot && this.$scopedSlots[column.slot]){
return this.$scopedSlots[column.slot]({
$index: props.$index,
row: props.row,
column: props.column
})
}
// 处理渲染字符串逻辑
if(column.customerRender){
const context = column.context || this
const outterHtml = column.customerRender(props.row, column)
const res = Vue.compile(outterHtml)
const methods = column.bindMethodKeys.reduce((result, key) => {
result[key] = context[key]
return result
}, {})
const com = Vue.extend({
name: 'customerRenderCell',
render: res.render, // res.render.bind(context),
staticRenderFns: res.staticRenderFns,
methods: methods,
data() {
return { row: props.row, column: props.column, index: props.$index }
}
})
return h('span', {}, [h(com)])
// 这样也可以实现
/* const com = Vue.extend({
template: outterHtml, // 模板字符串
methods: methods,
data() {
return { row: props.row, column: props.column, index: props.$index }
}
})
return h('div', { style: { textAlign: 'center' } }, [h(com)]) */
}
}
为了调用父组件的函数, 实现逻辑我一开始是想用res.render.bind(context)
绑定到父组件传的context
上下文,这样就可以调用父组件的方法了, 但是这样的话那些当前行和下标之类的就没办法传了, 所以改为了从context中直接把对应的函数拿过来再传给这个新创建的子组件,就可以实现调用父组件的函数了。
还有这个新创建的子组件,data中绑定了row和column和index下标,所以父组件那边函数的参数也可以接受的到了。
还有要注意一点的是,Vue分为runtime-only
版本和runtime-with-compile
版本,因为这个需要实时编译,所以需要使用带编译版本的。