自定义组件

可以通过defineComponentMaker这个工具函数自定义组件资源。

定义组件

以自定义一个按钮组件为例,假设现在有一个用Vue写的按钮组件

// Button.vue
<template>
    <button>我是按钮</button>
</template>
1
2
3
4

通过defineComponentMaker把它低代码化

// ButtonMaker.ts
import { defineComponentMaker } from 'vue-cook'
import Button from "./Button.vue";
export default defineComponentMaker({
    name: "按钮",
    pkg: "test-pkg",
    make: () => Button
})
1
2
3
4
5
6
7
8

cookEditorState中放入这个按钮组件资源

import { createCookEditorState, defaultMakerList } from "vue-cook"
const cookEditorState = createCookEditorState({
    makerList: [
        ButtonMaker, // import ButtonMaker from "./ButtonMaker.ts"
        ...defaultMakerList
    ]
})



 



1
2
3
4
5
6
7

打开示例页面-定义组件,可以在最下方的资源面板中找到新增加的按钮组件,按照下面的步骤,创建一个拥有按钮的页面。

  • 在左侧的页面组件树中点击+
  • 点击新增的页面,中间会打开一个页面编辑面板
  • 鼠标移到页面编辑面板中间,可以看到一个高亮的矩形框,点击选中这个矩形框
  • 左边的组件编辑器面板显示当前选中组件的一些可配置项,其中插槽一栏中,有一个名称为default的框,上面写着拖拽组件到此处添加
  • 从下方的资源面板中,将按钮拖到右上编辑器的框中,可以看到下方的表格发生了变化,出现了一行数据,它就是添加好的按钮组件
  • 在中间的页面编辑面板中,应该可以看到一个有按钮的页面

添加属性配置

通过defineComponentMaker的中的makePropOptions参数,可以为自定义的组件添加属性配置。

假设现在有一个按钮组件,它有一个text属性

// Button.vue
<template>
    <button>{{ text ? text : '我是默认文字' }}</button>
</template>
<script lang="ts" setup>
defineProps({
    text: {
        type: String
    }
})
</script>
1
2
3
4
5
6
7
8
9
10
11

添加makePropOptions

// ButtonMaker.ts
import { defineComponentMaker } from 'vue-cook'
import Button from "./Button.vue";
export default defineComponentMaker({
    name: "按钮",
    pkg: "test-pkg",
    makePropOptions: () => ["text"],
    make: () => Button
})






 


1
2
3
4
5
6
7
8
9

打开示例页面-组件添加属性配置,按照下面的步骤,配置组件的属性

  • 创建一个拥有按钮的页面
  • 选中添加的按钮
  • 可以看到它的属性一栏有一个名称为text的输入框
  • 输入文字后,对应按钮的文字会变成相应的内容

添加事件配置

通过defineComponentMaker的中的makeEventOptions参数,可以为自定义的组件添加事件配置。可以添加如点击、双击这种原生DOM事件,也可以添加使用Vue自定义的事件。

原生DOM事件配置

假设现在有一个按钮组件

// Button.vue
<template>
    <button>点我</button>
</template>
1
2
3
4

添加makeEventOptionsclick代表的是DOM事件的点击事件

// ButtonMaker.ts
import { defineComponentMaker } from 'vue-cook'
import Button from "./Button.vue";
export default defineComponentMaker({
    name: "按钮",
    pkg: "test-pkg",
    makeEventOptions: () => ["click"],
    make: () => Button
})






 


1
2
3
4
5
6
7
8
9

此处,需要自定义一个逻辑来绑定按钮点击后的行为

// AlertMaker.ts
import { defineLogicMaker } from "vue-cook";
export default defineLogicMaker({
    name: "alert",
    pkg: "test-pkg",
    make: () => {
        return () => {
            alert("你好,按钮被点击了")
        }
    }
})
1
2
3
4
5
6
7
8
9
10
11

cookEditorState中放入这个自定义逻辑资源

import { createCookEditorState, defaultMakerList } from "vue-cook"
const cookEditorState = createCookEditorState({
    makerList: [
        ButtonMaker, // import ButtonMaker from "./ButtonMaker.ts"
        AlertMaker, // import AlertMaker from "./AlertMaker.ts"
        ...defaultMakerList
    ]
})




 



1
2
3
4
5
6
7
8

打开示例页面-组件添加DOM事件配置,按照下面的步骤,配置组件的DOM事件

  • 创建一个拥有按钮的页面
  • 选中添加的按钮
  • 可以看到它的事件一栏有一个名称为click的框,上面写着拖拽逻辑到此处添加
  • 资源面板中找到之前放入的AlertMaker,将其拖入到上面的框里,可以看到下方的表格发生了变化,出现了一行数据,它就是添加好的行为逻辑。
  • 关闭页面编辑器的组件选中模式,否则没法触发按钮的点击事件。
  • 点击按钮,可以看到绑定的行为逻辑被成功触发

多个根节点组件DOM事件配置

假设,有一个多根节点组件需要配置DOM事件,则需要在组件中显示指定事件的绑定

// Button.vue
<template>
    <button>点我不触发事件</button>
    <button v-bind="attrs">点我触发事件</button>
</template>
<script setup lang="ts">
import { useAttrs } from 'vue'
const attrs = useAttrs()
</script>
1
2
3
4
5
6
7
8
9

打开示例页面-多个根节点组件DOM事件配置,按照下面的步骤,配置多个根节点组件的DOM事件

  • 创建一个拥有按钮的页面
  • 选中添加的按钮,配置事件的逻辑行为
  • 关闭页面编辑器的组件选中模式
  • 点击右边的按钮可以正常触发事件,点击左侧的按钮则不会触发

Vue中自定义事件配置

假设,有一个自定义了事件的组件

// Button.vue
<template>
    <button @click="delayClick">点我,2秒后触发事件</button>
</template>
<script setup lang="ts">
const emits = defineEmits(["delayClick"])
const delayClick = (e: MouseEvent) => {
    setTimeout(() => {
        emits("delayClick", e)
    }, 2000)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12

makeEventOptions中配置delayClick

// ButtonMaker.ts
import { defineComponentMaker } from 'vue-cook'
import Button from "./Button.vue";
export default defineComponentMaker({
    name: "按钮",
    pkg: "test-pkg",
    makeEventOptions: () => ["delayClick"],
    make: () => Button
})






 


1
2
3
4
5
6
7
8
9

打开示例页面-Vue中自定义事件配置,按照下面的步骤,配置Vue中自定义的事件

  • 创建一个拥有按钮的页面
  • 选中添加的按钮,配置事件的逻辑行为
  • 关闭页面编辑器的组件选中模式
  • 点击按钮,可以在延迟了一段时间后,逻辑被成功执行

事件传参

默认参数传递

可以在自定义逻辑的里面直接接收事件传递的默认参数

// AlertMaker.ts
import { defineLogicMaker } from "vue-cook";
export default defineLogicMaker({
    name: "alert",
    pkg: "test-pkg",
    make: () => {
        return (event: MouseEvent) => {
            alert(`你好,按钮被点击了,位置在${event.x},${event.y}`)
        }
    }
})






 
 



1
2
3
4
5
6
7
8
9
10
11

打开示例页面-事件传参,按照下面的步骤,可以看到事件参数的传递

  • 创建一个拥有按钮的页面
  • 选中添加的按钮,配置事件的逻辑行为
  • 关闭页面编辑器的组件选中模式
  • 点击按钮,可以看到事件的默认参数被正常传递

额外参数传递

也可以传递一些额外的参数

// Button.vue
<template>
    <button @click="overwriteClick">点我</button>
</template>
<script setup lang="ts">
const emits = defineEmits(["overwriteClick"])
const overwriteClick = (e: MouseEvent) => {
    emits("overwriteClick", e, "你好", "世界")
}
</script>
1
2
3
4
5
6
7
8
9
10

在自定义的逻辑中接收这些参数

// AlertMaker.ts
import { defineLogicMaker } from "vue-cook";
export default defineLogicMaker({
    name: "alert",
    pkg: "test-pkg",
    make: () => {
        return (event: MouseEvent, a: string, b: string) => {
            alert(`
你好,按钮被点击了,位置在${event.x},${event.y}
额外信息:${a},${b}
            `)
        }
    }
})






 
 






1
2
3
4
5
6
7
8
9
10
11
12
13
14

打开示例页面-向事件传递额外参数,按照下面的步骤,可以看到事件参数的传递

  • 创建一个拥有按钮的页面
  • 选中添加的按钮,配置事件的逻辑行为
  • 关闭页面编辑器的组件选中模式
  • 点击按钮,可以看到事件的额外参数被正常传递

添加插槽配置

通过defineComponentMaker的中的makeSlotOptions参数字段,可以为自定义的组件添加插槽配置。插槽是组件之间相互结合的纽带,使用它可以很方便的在组件之间进行嵌套。

假设,有一个前置图标插槽的按钮组件,在Vue中,slot可以指定名字,如果不指定的话有一个默认的名字default

// Button.vue
<template>
    <button>
        <span class="icon">
            <slot></slot>
        </span>
        <span>我是按钮</span>
    </button>
</template>




 




1
2
3
4
5
6
7
8
9

添加makeSlotOptions

// ButtonMaker.ts
import { defineComponentMaker } from 'vue-cook'
import Button from "./Button.vue";
export default defineComponentMaker({
    name: "按钮",
    pkg: "test-pkg",
    makeSlotOptions: () => ["default"],
    make: () => Button
})






 


1
2
3
4
5
6
7
8
9

创建一个图标组件,这里为了方便演示,直接使用emoji表情作为图标

// EmojiMaker.ts
import { defineComponent, h } from 'vue';
import { defineComponentMaker } from 'vue-cook'
export default defineComponentMaker({
    name: "emoji",
    pkg: "test-pkg",
    make: () => defineComponent({
        render: () => h(
            'span',
            '😀'
        )
    })
})
1
2
3
4
5
6
7
8
9
10
11
12
13

打开示例页面-组件添加插槽配置,按照下面的步骤,配置组件的插槽

  • 创建一个带按钮的页面
  • 选中添加的按钮
  • 可以看到它的插槽一栏,有一个名称为default的框,上面写着拖拽组件到此处添加
  • 将资源面板中的图标组件拖入,可以看到按钮中添加了一个图标

多个插槽配置

可以添加多个插槽配置

// Button.vue
<template>
    <button>
        <span class="pre-icon">
            <slot name="preIcon"></slot>
        </span>
        <span>我是按钮</span>
        <span class="post-icon">
            <slot name="postIcon"></slot>
        </span>
    </button>
</template>




 



 



1
2
3
4
5
6
7
8
9
10
11
12

添加makeSlotOptions

// ButtonMaker.ts
import { defineComponentMaker } from 'vue-cook'
import Button from "./Button.vue";
export default defineComponentMaker({
    name: "按钮",
    pkg: "test-pkg",
    makeSlotOptions: () => ["preIcon", "postIcon"],
    make: () => Button
})






 


1
2
3
4
5
6
7
8
9

打开示例页面-组件添加多个插槽配置,按照下面的步骤,配置组件的插槽

  • 创建一个带按钮的页面
  • 选中添加的按钮
  • 可以看到它的插槽一栏,出现了两个配置项,可以同时配置前置图标和后置图标,
  • 将资源面板中的图标组件拖入,配置组件的前置图标和后置图标

插槽与属性联动:创建一个动态布局组件

假设,有这样一个布局组件,它根据传入不同的行列值,拥有不同的插槽数量

// Layout.vue
<template>
    <div class="layout">
        <div v-for="m in rowNumber" class="row">
            <div v-for="n in colNumber" class="col">
                <slot :name="getSlotName(m, n)">{{ m }}-{{ n }}</slot>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
import { computed, toRefs } from 'vue';
const props = defineProps({
    row: String,
    col: String
})
const { row, col } = toRefs(props)
const rowNumber = computed(() => Number(row?.value) || 1)
const colNumber = computed(() => Number(col?.value) || 1)
const getSlotName = (m: number, n: number) => `${m}-${n}`
</script>
<style lang="less">
.layout {
    display: flex;
    width: 300px;
    flex-direction: column;
    border: 1px solid black;
    box-sizing: border-box;
    .row {
        display: flex;
        justify-content: space-around;
        border-bottom: 1px solid black;
        box-sizing: border-box;
        &:last-child {
            border-bottom: none;
        }
        .col {
            display: flex;
            flex: 1;
            border-right: 1px solid black;
            box-sizing: border-box;
            &:last-child {
                border-right: none;
            }
        }
    }
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

添加makeSlotOptions

// LayoutMaker.ts
import { defineComponentMaker } from 'vue-cook'
import Layout from "./Layout.vue";
export default defineComponentMaker({
    name: "布局",
    pkg: "test-pkg",
    makePropOptions: () => ["row", "col"],
    makeSlotOptions: (cookState, componentConfig) => {
        const getSlotName = (m: number, n: number) => `${m}-${n}`
        const rowNumber = Number(componentConfig?.props?.row) || 1
        const colNumber = Number(componentConfig?.props?.col) || 1
        const slots = []
        for (let m = 1; m <= rowNumber; m++) {
            for (let n = 1; n <= colNumber; n++) {
                slots.push(getSlotName(m, n))
            }

        }
        return slots
    },
    make: () => Layout
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

打开示例页面-创建一个布局组件,按照下面的步骤,配置布局组件

  • 创建一个带布局组件的页面
  • 选中布局组件
  • 可以看到它的属性栏有两个配置,分别是行和列的数量
  • 修改它的行列数,可以看到它的插槽数量会不断的更改
  • 将按钮组件放入不同的插槽查看效果

更多defineComponentMaker的参数,参考defineComponentMaker