Vue框架结合elementUI组件可以进行相应使用数据来驱动的组件化管理,但是在实际动作中,会出现很多需要自定义DOM的部分,但是因为这些自定义的DOM实在是太小了,没必要因此做一个组件,因为就需要使用Vue函数式组件结合JSX的方式来开发,这样就可以把自定义的DOM以及这个DOM绑定的事件留在处理数据js脚本中,这样可以避免页面或者组件中的DOM数据过大影响后期的维护。JSX和函数式编程组合起来也解决替代了VNode复杂的render函数
JSX:
JSX是对JavaScript的扩展,JS+XML的集合,既具有js的灵活,又有Html的语义化的特性。
- 安装
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
- 配置
.babelrc
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
['@vue/babel-preset-jsx',
{
'injectH': false
}]
]
}
基础示例
render() {
return (
<div>
<h3>内容</h3>
{/* 纯文本 */}
<p>hello, I am Gopal</p>
{/* 动态内容 */}
<p>hello { this.msg }</p>
{/* 输入框 */}
<input />
{/* 自定义组件 */}
<myComponent></myComponent>
</div>
);
}
动态属性
render() {
return <input placeholder={this.placeholder}/>
}
插槽
- 具名插槽
父组件的写法和单文件组件模板的类似,通过
slot="header"这样方式指定要插入的位置。子组件通过this.$slots.header方式指定插槽的名称,其中header就是插槽的名称。
父组件:
render() {
{/* 具名插槽 */}
<myComponent>
<header slot="header">header</header>
<header slot="content">content</header>
<footer slot="footer">footer</footer>
</myComponent>
}
子组件:
render() {
return (
<div>
{/* 纯文本 */}
<p>我是自定义组件</p>
{this.$slots.header}
{this.$slots.content}
{this.$slots.footer}
</div>
);
}
- 作用域插槽
子组件中通过
{this.$scopedSlots.test({ user: this.user })}指定插槽的名称是test,并将user传递给父组件。父组件在书写子组件标签的时候,通过scopedSlots值指定插入的位置是test,并在回调函数获取到子组件传入的user值
父组件:
render() {
{/* 具名插槽 作用域插槽 */}
<myComponent {
...{
scopedSlots: {
test: ({user}) => (
<div>{user.name}</div>
)
}
}
}>
</myComponent>
}
子组件:
render() {
return (
<div>
{this.$scopedSlots.test({
user: this.user
})}
</div>
);
}
Vue指令在JSX中的应用
render() {
{/* 指令 */}
{/* v-model */}
<div><input vModel={this.newTodoText} /></div>
{/* v-model 以及修饰符 */}
<div><input vModel_trim={this.tirmData} /></div>
{/* v-on 监听事件 */}
<div><input vOn:input={this.inputText} /></div>
{/* v-on 监听事件以及修饰符 */}
<div><input vOn:click_stop_prevent={this.inputText} /></div>
{/* v-html */}
<p domPropsInnerHTML={html} />
}
函数式组件
对于要编写一个没有响应式数据,也无需监听状态,只接受props值的简单组件,就可以直接使用函数式组件,在这样的场景下,我们可以将组件标记为
functional,这意味它无状态 (没有响应式数据),也没有实例 (没有this上下文)。函数式组件在实际运用中主要是针对,大型组件中的一些小型自定义DOM组件。
Vue.component('my-component', {
functional: true,
// Props 是可选的
props: { // ... },
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {
// ... 可以在这里结合JSX语法,渲染一个简单复用得DOM组件
}
})
自定义函数式组件的简单示例:
<template>
<ex-slot
v-if="obj.render"
:render="obj.render"
:row="obj.row"
:index="3"
:column="obj.column"
/>
</template>
<script>
//自定义函数式组件 ==START
let exSlot = {
functional: true,//代表这是一个函数式组件
props: {
row: Object, //接受父组件传来得数据
render: Function, //接受父组件传来的回调函数,用于把JSX编写的DOM以及DOM上的事件抽取出来到数据脚本页面,尽量不污染外部组件,使组件更加的灵活轻便可复用。
className: String,//接受父组件传来的样式class,配合css更改样式
index: Number, // 接受父组件的数据
column: { // 接受父组件的数据
type: Object,
default: null,
},
},
render: (h, context) => { //函数式组件用来渲染相应的DOM,有两个参数,h:render渲染函数,context:上下文对象,可以获取相应父组件传到props中的数据
const params = {
row: context.props.row,
index: context.props.index,
className: context.props.row.className,
};
if (context.props.column) params.column = context.props.column;
//使用父组件传来的render回调函数,将JSX编写的DOM对象抽取出去,保持组件的灵活性。*****关键*****
return context.props.render(h, params);
},
};
//自定义函数式组件 ==END
export default {//父组件
components: { exSlot },
data:{
return{
obj:{
row:{
a:1,
b:2
},
className:'flex-cus',
column:{
prop:'operation',
label:'操作'
},
render:(h, params) =>{
//这里返回的是JSX编写的DOM数据,这样就把本来应该写在html的DOM抽出来到JS中,然后也可以对这个自定义组件进行处理。这样exSlot这个组件可以进行最大程度的重用。可以保持html的相对简洁。
return (
<div class="operate">
<div onClick = {()=>this.handleCancelClick(params)}>取消</div>
<div onClick = {()=>this.handleSeeClick(params)}>查看</div>
</div>
)
}
}
}
}
methods:{
handleCancelClick(param){
console.log('handleCancelClick',param)
},
handleSeeClick(param){
console.log('handleSeeClick',param)
}
}
}
</script>