实现效果
需求分析
为啥有这个需求?
通过上一章实现我们实现了组件的封装,过了一些日子我们的组件日渐增多后,并且多次重复涉及时,我们应该如何快速开发呢。
技术分析
当我们有了这个需求,就该考虑如何实现了。
动态渲染需要实现
- 如何挂载组件
- 如何传递数据
- 如何更新数据
- 如何获取他们事件
难点
- 组件来自不同位置 如果处理
- 功能模块数据格式不一致 如何实现双向绑定
动态渲染组件
component:cn.vuejs.org/api/built-i...
实现
getComponent.ts
typescript
import { shallowReactive } from 'vue'
import SnowInput from './components/Input.vue'
import SnowSelect from './components/Select.vue'
import SnowSelects from './components/Selects.vue'
import SnowDatePicker from './components/DatePicker.vue'
import SnowCascader from './components/Cascader.vue'
const componentList: any = shallowReactive({
SnowInput,
SnowSelect,
SnowSelects,
SnowDatePicker,
SnowCascader
})
export const getComponents = (componentName: string) => {
componentName = 'Snow' + componentName.charAt(0).toUpperCase() + componentName.slice(1)
return componentList[componentName]
}
使用
Form.vue
xml
<script setup lang="ts">
import { getComponents } from './getComponents'
const name = 'input'
</script>
<template>
<Component :is="getComponents(name)" />
</template>
<style lang="scss" scoped></style>
v-model:cn.vuejs.org/api/built-i...
到这里相信已经有所了解了接下来就开始直接实现吧
通过v-bind实现数据绑定
v-bind:cn.vuejs.org/api/built-i...
实现手动绑定
list是指向要查找的数据对象的key list是数组按照对象层级往下查找
typescript
// 实现手动查找
const get = (list: any) => {
// modelValue 是父组件传进表单需要渲染的数据
let currentObj: any = modelValue.value
if (typeof list === 'string') {
return currentObj[list]
}
list.forEach((key: string) => {
currentObj = currentObj[key] || ''
})
return currentObj
}
// 创建所需的对象格式
const getProxy = (data: any) => {
if (!data) return {}
let obj: any = {}
if (isString(data) || isObject(data)) {
data = [data]
}
data.forEach((item: any) => {
if (isString(item)) {
obj['modelValue'] = get(item)
} else {
obj[item.key] = get(item.value)
}
})
return obj
}
使用手动绑定
xml
<script setup lang="ts">
import { getComponents } from './getComponents'
const name = 'input'
</script>
<template>
<Component
:is="getComponents(name)"
v-bind="{
...getProxy(item.key),
...item.args
}"
/>
</template>
<style lang="scss" scoped></style>
通过v-on实现数据更新
v-on:cn.vuejs.org/api/built-i...
实现手动更新
typescript
const set = (newValue: any, path: any) => {
if (typeof path === 'string') {
modelValue.value[path] = newValue
return
}
let currentObj = modelValue.value
for (const key of path) {
if (path[path.length - 1] === key) {
currentObj[key] = newValue
return
}
currentObj = currentObj[key] || ''
}
}
const setProxy = (modelValue: any, customEvent?: any) => {
let modelValues = setModelValues(modelValue)
let customEvents = setEvents(customEvent)
return {
...modelValues,
...customEvents
}
}
const setEvents = (events: any) => {
if (!events) return {}
if (isString(events) || isObject(events)) {
events = [events]
}
let obj: any = {}
events.forEach((item: any) => {
if (isString(item)) {
obj[events] = (data: any) => {
emit(`${events}`, data)
}
} else {
obj[item.key] = (data: any) => {
emit(item.value, data)
}
}
})
return obj
}
const setModelValues = (data: any) => {
if (!data) return
if (isString(data) || isObject(data)) {
data = [data]
}
let obj: any = {}
data.forEach((item: Item) => {
if (isString(item)) {
obj['update:modelValue'] = (newValue: any) => {
set(newValue, item)
}
} else {
obj[`update:${item.key}`] = (newValue: any) => {
set(newValue, item.value)
}
}
})
return obj
}
使用手动绑定
xml
<script setup lang="ts">
import { getComponents } from './getComponents'
const name = 'input'
</script>
<template>
<Component
:is="getComponents(name)"
v-on="setProxy(item.key, item.event)"
v-bind="{
...getProxy(item.key),
...item.args
}"
/>
</template>
<style lang="scss" scoped></style>
扩展
这时我们就最小实现双向绑定数据了,可以尝试通过上述函数来实现更多功能。
例如有个场景,需要某个指定值满足xxx才显示。
实现校验数据
kotlin
const validateValue = (data: any): boolean => {
if (isNull(data)) return true
// 严格模式
if (data?.strict) {
return data.value === get(data.target)
}
return data.value == get(data.target)
}
使用校验数据
ini
<script setup lang="ts">
import { getComponents } from './getComponents'
const name = 'input'
</script>
<template>
<Component
v-if="validateValue(item?.validate)"
:is="getComponents(name)"
v-on="setProxy(item.key, item.event)"
v-bind="{
...getProxy(item.key),
...item.args
}"
/>
</template>
<style lang="scss" scoped></style>
总结
感觉没啥人看,就偷懒省掉注释了~ 主要等会就要干饭了 后续有需求在更新吧 祝大家周末愉快