предварительное содержание
Вы должны быть знакомы с двусторонней привязкой Vue Почему вы должны упоминать основы входа? Потому что это основа для инкапсуляции компонентов!
Вот вопрос 👇 Попробуйте свои основы 😏
pageIndex.vue
<template>
<el-card>
<dynamic-form v-model="testModel" />
</el-card>
</template>
<script>
export default {
// ...省去不关键的代码
data() {
return {
testModel: 'Init Value'
}
}
}
</script>
dynamic-form.vue
<template>
<div>
<el-input v-model="value"></el-input>
</div>
</template>
<script>
export default {
name: 'DynamicForm',
props: {
value: {}
},
}
</script>
Рендеринг 💗
Проблема с приведенным выше кодом очевидна, есть две проблемы:
- Мы нарушили одноэлементный поток данных vue, дочерний компонент не должен изменять значение родительского компонента
- После того, как значение, привязанное дочерним компонентом v-model, изменяет значение компонента, родительский компонент не достигает эффекта двусторонней привязки
В настоящее время у нас обычно есть два решения
Вариант первый
pageIndex.vue
- <dynamic-form v-model="testModel" />
+ <dynamic-form :value.sync="testModel" />
dynamic-form.vue
- <el-input v-model="value"></el-input>
+ <el-input v-model="newValue"></el-input>
+ export default {
+ computed: {
+ newValue: {
+ get({ value }) {
+ return value
+ },
+ set(newVal) {
+ this.$emit('update:value', newVal)
+ }
+ }
+ },
+ }
Рендеринг 💗
Такое решение часто используется, когда мы одновременно инкапсулируем бизнес-компоненты на основе библиотек компонентов с открытым исходным кодом.Вот объяснение, почему это так.
Мы передаем вычисляемые свойства
get
использовать значение родительского компонента,set
Подкинуть изменение нового значения, чтобы дочерний компонент напрямую не менял значение родительского компонента, родительский компонент проходит.sync
Родительский компонент изменяет значение дочернего компонента, а вычисленный пересчет в дочернем компоненте возвращает новое значение.
.sync синтаксический сахар эквивалентен этому
pageIndex.vue
<dynamic-form :value.sync="testModel" />
<dynamic-form :value="testModel" @update:value="testModel = $event" />
Этот способ эквивалентен,.sync
Этот метод также используется достаточно часто, он часто возникает, когда мы инкапсулируем компоненты "вложение компонентов", но по сути это то же самое, что иv-model
Это то же самое, и принцип тот же: например,v-model
настраиваемая модельprop/event
v-образный способ
pageIndex.vue
<dynamic-form
v-model="testModel"
@customValue="testModel = $event"
/>
dynamic-form.vue
<template>
<div>
<el-input v-model="newValue"></el-input>
</div>
</template>
<script>
export default {
name: 'DynamicForm',
model: {
prop: 'customValue',
event: 'customEvent'
},
props: {
customValue: {},
},
computed: {
newValue: {
get({ customValue }) {
return customValue
},
set(newVal) {
this.$emit('customValue', newVal)
}
}
},
}
</script>
.синхронный способ
pageIndex.vue
<dynamic-form
:value="testModel"
:eventName="eventName"
v-on:[eventName]="testModel = $event"
/>
<script>
export default {
data() {
return {
eventName: 'update:newValue',
}
}
}
dynamic-form.vue
<template>
<div>
<el-input v-model="newValue"></el-input>
</div>
</template>
<script>
export default {
name: 'DynamicForm',
model: {
prop: 'customValue',
event: 'customEvent'
},
props: {
customValue: {},
},
computed: {
newValue: {
get({ customValue }) {
return customValue
},
set(newVal) {
this.$emit('customValue', newVal)
}
}
},
}
</script>
Нетрудно заметить, что принцип реализации одинаков, так зачем же нам проектировать иv-model
Есть ли API, который делает то же самое?
Потому что v-model нельзя использовать несколько раз на одном и том же компоненте! Но с синтаксисом .sync все в порядке, мне просто нужно добавить другое «обновление: xxx» в дочерний компонент!
Передача v-модели в Vue3
arguments
Чтобы достичь цели многократного использования, а также вы можете настроить модификатор, поэтому этот синтаксис был устранен в Vue3, вы можете напрямую просмотреть документацию для контента, связанного с Vue3 😶
Вариант 2
pageIndex.vue
<dynamic-form
v-model="testModel"
/>
dynamic-form.vue
<el-input
v-bind="$attrs"
v-on="$listeners"
>
</el-input>
Рендеринг 💗
Двусторонний принцип связывания: через
v-bind="$attrs"
Прозрачно передать значение родительского компонента вel-input
Привязка значения реализована по верхнему;v-on="$listeners"
Будуv-model
Внутренне реализовано@input
передача событийel-input
на ответ значения реализации.
Этот метод является наиболее гибким, а также наиболее часто используемым методом инкапсуляции компонентов, поскольку в нашей повседневной работе значение, привязанное v-model к пользовательскому компоненту, как правило, не является значением базового типа. bind являются объектом,v-model
Что может пойти не так с привязанными объектами?
Научит вас элегантно инкапсулировать динамические формы
основные навыки
indexPage.vue
<template>
<el-card>
<dynamic-form
v-model="formModel"
v-bind="formConfig"
/>
</el-card>
</template>
<script>
import DynamicForm from './common/DynamicForm'
export default {
name: 'Model',
components: {
DynamicForm
},
data() {
return {
formModel: {},
formConfig: {
labelWidth: '100px',
formItemList: [
{
label: '用户名',
type: 'input',
prop: 'userName',
},
{
label: '密码',
type: 'password',
prop: 'passWord',
'show-password': true
},
{
label: '备注',
type: 'textarea',
prop: 'remark',
maxlength: 400,
'show-word-limit': true,
'auto-size': { minRows: 3, maxRows: 4 }
}
]
}
}
},
}
</script>
dynamic-form.vue
<template>
<el-form v-bind="$attrs">
<template v-for="formItem of formItemList">
<dynamic-form-item
v-model="value[formItem.prop]"
:key="formItem.prop"
v-bind="formItem"
/>
</template>
</el-form>
</template>
<script>
import DynamicFormItem from './DynamicFormItem'
export default {
name: 'DynamicForm',
props: {
value: {
type: Object,
required: true
},
formItemList: {
type: Array,
default: () => ([])
}
},
components: {
DynamicFormItem,
},
created() {
this.initFormItemValue()
},
methods: {
initFormItemValue() {
const formModel = { ...this.value }
this.formItemList.forEach((item) => {
// 设置默认值
const { prop, value } = item
if (formModel[prop] === undefined || formModel[prop] === null) {
formModel[prop] = value
}
})
this.$emit('input', { ...formModel })
},
}
}
</script>
dynamic-form-item.vue
<template>
<el-form-item :label="label">
<el-input
v-if="inputType.includes(type)"
:type="type"
v-bind="$attrs"
v-on="$listeners"
>
</el-input>
</el-form-item>
</template>
<script>
/* 这些配置项实际工作中可以单独提取成配置文件 */
const inputType = [/* input支持的类型 */]
const selectType = [/* select支持的类型 */]
const customType = [/* 自定义的表单项组件 */]
export default {
name: 'DynamicFormItem',
props: {
label: {
type: String,
required: true
},
type: {
type: String,
require: true,
validator: (type) => {
return [...inputType, ...selectType, ...customType]
.includes(type)
}
}
},
data() {
return {
inputType: Object.freeze(inputType),
}
},
}
</script>
Рендеринг 💗
Навыки, которые вы можете получить:
- пройти через
v-for
Шаблон уменьшает создание ненужных узлов dom, чего также можно достичь таким образом.v-for/v-if
Эффекты, используемые вместе - Не требует адаптивной передачи данных
Object.freeze
Freeze, Vue не добавит это свойство при инициализацииget/set
, что также является точкой оптимизации производительности
Поддержка свойств глубины
В реальной разработке атрибуты объектов формы также могут быть объектами.Возьмите общие системы CRM/ERP, как правило, такие как **[договор аренды, депозитный договор, настройки проекта]** и т. д. Эти основные справочные основные данные Все они являются каскадными ссылками. В настоящее время компонент формы должен поддерживать эту функцию.
indexPage.vue
<script>
export default {
data() {
return {
formModel: {
depositAgreement: {
depositAmount: 100,
businessType: 'traditionWork',
signDate: '2021-7-18'
}
},
formConfig: {
labelWidth: '100px',
formItemList: [
{
label: '金额',
type: 'number',
prop: 'depositAgreement.depositAmount',
},
{
label: '协议类型',
type: 'select',
prop: 'depositAgreement.businessType',
options: [
{
dictKey: 'traditionWork',
dictValue: '专属办公',
}
],
},
{
label: '签订日期',
type: 'depositAgreement.date',
prop: 'signDate',
}
]
}
}
},
}
</script>
Рендеринг 💗
Для поддержки атрибута глубины требуются два метода.
_getDeepAttr(model, deepPath)
_setDeepAttr(model, deepPath, val)
реализация _getDeepAttr
let data = {
obj: {
v1: 'v1-val'
}
}
_getDeepAttr(data, 'obj.v1') // => v1-val
/**
* @description: 深度获取属性
* @param {Object} model 表单对象
* @param {String} deepPath 深度属性
* @return {any}
*/
function _getDeepAttr(model, deepPath) {
if (!deepPath) return
if (deepPath.indexOf('.') !== -1) {
const paths = deepPath.split('.')
let current = model
let result = null
for (let i = 0, j = paths.length; i < j; i++) {
const path = paths[i]
if (!current) break
if (i === j - 1) {
result = current[path]
break
}
current = current[path]
}
return result
} else {
return model[deepPath]
}
}
Реализация _setDeepAttr
let data = {
obj: {
dep1: {
v1: 'v1',
v2: 'v2'
},
name: '北歌'
}
}
_setDeepAttr(data, 'obj.dep1.v1', 'v1-newVal')
// data.obj.dep1.v1 => v1-newVal
// data.obj.dep1.v2 => v2
/**
* @description: 设置深度属性
* @param {Object} model 表单对象
* @param {String} deepPath 深度属性
* @param {any} val 要设置的值
*/
function _setDeepAttr(model, deepPath, val) {
// 路径
let paths = deepPath.split('.')
// 目标值,后面这个值会存放符合路径下的所有属性
let targetVal = {}
// 陆续查找每个对象的prop
let pathsNew = [...paths]
let prop
for (let i = paths.length - 1, j = i; i >= 0; i--) {
prop = paths[i]
// 最后一层要设定的值
if (i === j) {
targetVal[prop] = val
} else if (i === 0) {
// 先获取根属性的值
const originalVal = model[prop]
// 第一层需要直接替换的根属性
model[prop] = Object.assign(originalVal, targetVal)
} else {
// 更新每一个层级的值(去除存起来的值)
let curDeppObj = _getDeepAttr(model, pathsNew.join('.'))
// 将当前层级的值存储起来
targetVal[prop] = Object.assign({}, curDeppObj, targetVal)
// 删除上个路径存储的值
delete targetVal[paths[i + 1]]
}
// 将处理过的路径去除
pathsNew.pop()
}
}
_getDeepAttr
О чем говорить, вот главный разговор_setDeepAttr
идея реализации
Реализовать идеи
- Разделить путь: получить имя атрибута под каждым уровнем, потому что его нужно искать по слоям, а массив разделенных слоев резервируется.
- Обратный цикл: Наша конечная цель — присвоить значения атрибутам последнего слоя, что требует
j
чтобы определить, является ли текущий цикл последним слоем - условие оценки в теле цикла
-
i === j
: Последний слой, то есть первый цикл, мы непосредственновременный объектустановить на указанное значение -
i === 0
: Первый слой, который является последним слоем цикла, в это время мы напрямую читаем модель [атрибут первого слоя], чтобы получить корневой атрибут, значение, очевидно, является объектом, а затем напрямую заменяем корневой атрибут сбросомвременный объект - Обработка промежуточного уровня: необходимо получить объекты свойств текущего уровня, объединить объекты текущего уровня с временными объектами, воздействовать только на целевое свойство, а другие свойства того же уровня не могут быть потеряны, и, наконец, удалить свойства предыдущего уровня
-
Карта отладки 💗
Настоятельно рекомендуется снова выполнить отладку, как только вы ее изучите, она будет бесполезна~😃
dynamic-form.vue
<template>
<el-form v-bind="$attrs">
<template v-for="formItem of formItemList">
<dynamic-form-item
- v-model="value[formItem.prop]"
+ :value="value[formItem.prop] | _formatterItemVal(value, formItem, _getDeepAttr)"
+ @input="bindItemValue(value, formItem, $event)"
:key="formItem.prop"
v-bind="formItem"
/>
</template>
</el-form>
</template>
<script>
import DynamicFormItem from './DynamicFormItem'
export default {
name: 'DynamicForm',
props: {
value: {
type: Object,
required: true
},
formItemList: {
type: Array,
default: () => ([])
}
},
components: {
DynamicFormItem,
},
- created() {
- this.initFormItemValue()
- },
+ filters: {
+ /**
+ * @description:
+ * @param {any} curVal 当前表单的值(如果是深度属性就为空)
+ * @param {Object} value form的值
+ * @param {Object} item 当前表单配置项
+ * @param {Function} _getDeepAttr 格式化item值的方法
+ * @return {*}
+ */
+ _formatterItemVal: (curVal, value, item, _getDeepAttr) => {
+ if (curVal) {
+ return curVal
+ }
+
+ // 往下走就是深度属性的情况,需要格式化获取值
+
+ // 提供给用户格式化value的的方法: 如trim、number等作用
+ const formater = item.formatter
+
+ return typeof formater === 'function'
+ ? formater(_getDeepAttr(value, item.prop))
+ : _getDeepAttr(value, item.prop)
+ }
},
methods: {
- initFormItemValue() {
- const formModel = { ...this.value }
- this.formItemList.forEach((item) => {
- // 设置默认值
- const { prop, value } = item
- if (formModel[prop] === undefined || formModel[prop] === null) {
- formModel[prop] = value
- }
- })
- this.$emit('input', { ...formModel })
- },
+ /**
+ * @description: 实现双向绑定
+ * @param {Object} model 表单对象
+ * @param {String} deepPath 深度属性
+ * @param {any} val 要设置的值
+ */
+ bindItemValue(model, item, val) {
+ // 深度属性需要格式化
+ if (~item.prop.indexOf('.')) {
+ this._setDeepAttr(model, item.prop, val)
+ } else {
+ model[item.prop] = val
+ }
+
+ const _model = { ...model }
+ this.$emit('input', _model)
+ }
+ _getDeepAttr(){ /*... */ }
+ _setDeepAttr(){/*... */}
}
}
</script>
dynamic-form-item.vue
<template>
<el-input
v-if="inputType.includes(type)"
:type="type"
v-bind="$attrs"
v-on="$listeners"
/>
<dynamic-select
v-else-if="selectType.includes(type)"
v-bind="$attrs"
v-on="$listeners"
/>
<el-date-picker
v-else-if="dateType.includes(type)"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
Компонент элемента формы просто добавляет еще две опоры для типа элемента формы и не имеет другой логики.dynamic-select
Компоненты инкапсулированыselect/treeSelect
Компонент расширения, который динамически получает параметры через URL-адрес, вы можете увидеть, если вы заинтересованыэта статья в колонке
Пользовательский контент (слоты)
Предварительное знание 👉Говорящие слоты
Также необходимо поддерживать упакованные компоненты после того, как эл-форма упакованаform-item
Слоты, только эти три:
Наши обычаи в нашей работе — это форма формы. Например: использование компонентов бизнес-пакета в качестве формы
Короче говоря, независимо от того, какой компонент, содержимое этого элемента формы должно быть настраиваемым, и на этот раз вы должны использовать слот
- Поддержка двух методов рендеринга
- шаблон шаблона
- Способ написания функции рендеринга в элементе конфигурации
Приоритет: мы следуем тем же правилам, что и vue, так как функция рендеринга выше, чем шаблон шаблона.
{
label: '自定义',
type: 'slot',
prop: 'custom'
}
Как шаблон записывает слоты
<dynamic-form
v-model="formModel"
v-bind="formConfig"
>
<template #custom="{value}">
<el-button>{{value}}</el-button>
</template>
</dynamic-form>
Как элемент конфигурации записывает функцию рендеринга
{
label: '自定义',
type: 'slot',
prop: 'custom',
render: ({ value, $createElement: h }) => {
return h('el-button', value)
}
}
dynamic-form.vue
dynamic-form
Этот компонент почти не изменился, поэтому этот пункт добавлен для инжекта текущего инстанса вниз
export default {
provide() {
return {
formThis: this
}
},
}
dynamic-form-item.vue
<template>
<!-- 添加对type: slot的支持 -->
<slot-content
v-else-if="isRenderSlot({type, prop})"
v-bind="$attrs"
:render="generateSlotRender()"
/>
</template>
<script>
export default {
/**
* @description: 是否渲染自定义内容
* @param {String} type
*/
isRenderSlot({ type, prop }) {
if (type !== 'slot') {
return false
}
/*
支持两种渲染方式
1. template模板的方式
2. 在配置项中写render函数的方式
*/
return [
typeof this.formThis.$scopedSlots[prop],
typeof this.$attrs.render
].includes('function')
},
// 渲染自定内容的render函数 优先级:在配置项中写render函数的方式 > template的方式
generateSlotRender() {
// normalizeScopedSlot
return ({ value, $createElement }) => {
// 给插槽传递参数
const slotScope = { ...this.$attrs, value, $createElement }
const renderSlot = this.$attrs.render || this.formThis.$scopedSlots[this.prop]
return renderSlot(slotScope)
}
},
}
</script>
Навыки, которые вы можете получить:
- Этот способ читается дольше, чем написание старого
||
читай яснее
[
typeof xxx,
typeof xxxx,
].includes('function')
- Метод в слоте области $scopeSlots используется для возврата VNode, мы можем передать параметры для передачи данных в слот
Рендеринг 💗
Поддержка встроенного слота для меток/ошибок
Необходимо изменить перед реализацией этих двух слотовel-form-item
, чтобы пользователям было проще писать при настройке слотов со свойством depth.
比如用户设置了
{
prop: 'depositAgreement.depositAmount',
...
}
В это время пользователь хочет использоватьdynamic-form
Как использовать компонент для настройки содержимого элемента формы
<!-- depistiAmountSlot是在data中定义的变量 depistiAmountSlot: 'depositAgreement.depositAmount' -->
<template #[depistiAmountSlot]></template>
<!-- 这种方式是不行的 -->
<template #depositAgreement.depositAmount></template>
При использовании шаблона для написания компонентов все, что мы пишем, должно быть скомпилировано.Если в шаблоне есть специальные символы, необходимо выполнить дополнительные рассуждения, поэтому, наслаждаясь скоростью, обеспечиваемой синтаксисом шаблона шаблона, он также теряет некоторую гибкость.
dynamic-form-item.vue
<template>
<el-form-item
:rules="rules"
:label="label"
:prop="prop"
>
<template
v-if="formThis.$scopedSlots[realProp + 'Label']"
#label
>
<slot-content
v-bind="_attrs"
:render="formThis.$scopedSlots[realProp + 'Label']"
/>
</template>
<template
v-if="formThis.$scopedSlots[realProp + 'Error']"
#error="{error}"
>
<slot-content
v-bind="{..._attrs, error}"
:render="formThis.$scopedSlots[realProp + 'Error']"
/>
</template>
</el-form-item>
</template>
<script>
export default {
computed: {
// 传递给插槽的数据
_attrs({ value, label, rules, realProp, $attrs }) {
return {
value,
label,
rules,
realProp,
...$attrs
}
},
// 对于深度属性做特殊处理 data.val => dataVal
realProp({ prop }) {
return prop.replace(/\.([^.]+)+?/g, (...arg) => {
const [, execProp] = arg
return execProp[0].toUpperCase() + execProp.substr(1)
})
}
}
}
</script>
Рендеринг 💗
проверка формы
Поддержка автоматической проверки
Если это не проверка значения атрибута глубины, она должна быть только вe-form
плюсmodel
свойства, вel-form-item
плюсprop/rules
Он был реализован, но если это атрибут глубины, в это время необходимо выполнить некоторую дополнительную обработку.
{
label: '金额',
type: 'text',
prop: 'depositAgreement.depositAmount',
rules: [
{
required: true,
message: '金额不能为空',
trigger: 'blur'
},
{
validator: (rule, val, callback) => {
if (val < 100) {
return callback(new Error('金额不能小于100'))
}
return callback()
},
trigger: ['change', 'blur']
}
]
},
dynamic-form.vue
{
computed: {
// 需要校验的item
validateItem({ formItemList }) {
return formItemList.filter(i => i.rules)
},
// 传递的form的数据源,用于处理深度属性校验问题
model({ validateItem, value, isDeepPath, _getDeepAttr }) {
const _model = { ...value }
if (!validateItem.length) return _model;
validateItem.forEach(({ prop }) => {
if (isDeepPath(prop)) {
_model[prop] = _getDeepAttr(_model, prop, _getDeepAttr)
}
})
return _model
}
},
}
Рендеринг 💗
Рассчитанная нами модель дает только
el-form
Используется атрибут модели, и только когда пользователь задает атрибут глубины и устанавливается модель проверки, будет вычисляться атрибут для проверки.
Поддержка ручной проверки
Просто установите для атрибута ref значение el-form. Чтобы упростить пользовательскую настройку, ref не является жестко закодированным напрямую.
{
props: {
// 自定义elForm的ref属性
elFormRef: {
type: String,
default: 'elForm'
}
},
methods: {
/**
* @description: 表单整体校验
*/
validate(callback) {
return this.$refs[this.elFormRef].validate(callback)
},
/**
* @description: 单个字段校验
*/
validateField(props, callback) {
return this.$refs[this.elFormRef].validateField(props, callback)
},
/**
* @description: 清除校验
*/
clearValidate(props) {
return this.$refs[this.elFormRef].clearValidate(props)
},
/**
* @description: 表单重置,清除校验
*/
resetFields() {
return this.$refs[this.elFormRef].resetFields()
},
}
}
На самом деле выше написанного можно не писать, дабы облегчить пользователю работу, не пишите старейшинам
$refs
Слой ссылок, позволяющий пользователям работать самостоятельно
Яма v-on="$linsters", будьте осторожны!
Следующий шаг — последняя ссылка.Нам нужно поддерживать пользовательские события для всех элементов формы во внешний мир.Это непросто.v-on="$listeners"
Разве не было бы все кончено, если бы инцидент был передан?
использовать
<dynamic-form
ref="dynamicForm"
v-model="formModel"
v-bind="formConfig"
@input="handleInput"
>
dynamic-form.vue
<dynamic-form-item
:key="formItem.prop"
:value="value[formItem.prop] | _formatterItemVal(value, formItem, _getDeepAttr)"
v-bind="formItem"
+ v-on="$listeners"
@input="bindItemValue(value, formItem, $event)"
/>
Присмотритесь к этому коду, действительно ли там нет проблемы? ? ?
насv-on="$listeners"
Кто-то случайно передал встроенное событие ввода v-model? Будет ли проблема?
История начинается так:
Процесс взаимодействия эл-ввода и элемента динамической формы:
- Изменяем значение поля ввода
- el-input вызывает событие ввода и содержит базовое значение текущего значения элемента формы (тип Sting)
- Компонент dynamic-form-item использует el-input для записи v-on="$listeners" для прозрачной передачи всех событий, используемых в dynamic-form-item, на el-input.
- Мы написали собственный обработчик событий ввода при использовании элемента динамической формы.
На данный момент мы реализовали двустороннюю привязку динамического элемента и электронного ввода.
Взаимодействие элементов динамической формы и динамической формы
- В динамической форме мы отслеживаем, какой элемент формы в настоящее время изменяется, прослушивая @input, повторно обновляем значение объекта формы и выбрасываем событие ввода.
- Пользователь помогает нам добиться двусторонней привязки с помощью события мониторинга ввода и метода обработки событий, реализованного внутри v-model.
История такая законченная.
Но я прописал событие @input внешне, и это событие тоже прозрачно передается на el-input!Тогда я должен следить за изменением элементов формы, а реализованную в v-model обработку события ввода нельзя переназначить?
В результате formModel в v-model="formModel" переназначается внутренне, с объекта на базовое значение (значение, сгенерированное el-input).
История заканчивается здесь. страница неверна
Итак, динамическая форма — это программа с двумя входными событиями, как это увидеть? Распечатайте это в динамической форме, давайте посмотрим
dynamic-form.vue
слегка модифицированный
export default {
// 解决$listeners透传事件导致双绑问题
model: {
// 自定义v-model的监听事件名
event: 'dyInput'
},
}
Если инцидент брошен, мы можем бросить его так
this.$emit('dyInput', {...value})
Рендеринг 💗
На данный момент функция завершена, но внешний мир следит заinput
Событие не может быть привязано к какому элементу формы, это событие в принципе бесполезно! Нам нужно добавить идентификатор во внешний мир, чтобы сообщить ему, что элемент формы вызывает событие.
Есть еще одна проблема.По мере увеличения количества элементов формы появляется все больше и больше типов определенных типов, а также все больше и больше типов событий.Имена событий, выдаваемые многими элементами формы, одинаковы, а внешний мир не очень хорошо контролировать эти события., и все события записываются на этот компонент.Если вы хотите различать, какой компонент бросает событие, вы должны перейти к входному событию и вывести его? Это не хорошо. . .
示例代码
<dynamic-form
ref="dynamicForm"
v-model="formModel"
v-bind="formConfig"
@input="handleInput"
@change="handleChange"
@visible-change="handleVisibleChange"
@...
/>
мы надеемсяdynamic-form
Толькоinput/change
События, другие события записываются в соответствующих элементах конфигурации.
{
label: '协议类型',
type: 'select',
// ...省略options,props配置
prop: 'depositAgreement.businessType',
listeners: {
'visible-change': (isShow) => {
console.log(isShow, 'select');
}
}
},
dynamic-form.vue
<dynamic-form-item
v-on="_listeners"
/>
<script>
export default {
computed: {
// 能在dynamic-form监听的事件
_listeners({ $listeners }) {
// 支持往下透传的事件
let supportEvent = ['input', 'change']
return supportEvent.reduce((_listeners, eventName) => {
_listeners[eventName] = $listeners[eventName] || (() => { })
return _listeners
}, {})
}
}
}
</script>
dynamic-form-item.vue
<dynamic-form-item
v-on="_listeners"
/>
<script>
export default {
computed: {
// 整合配置项中的listeners,最终向下透传的事件
onEvent({ $listeners, listeners }) {
// 配置项中的事件优先级大于在dynamic-form中监听的事件
return { ...$listeners, ...listeners }
}
}
}
</script>
Расширение: Анализ исходного кода v-модели
transform component v-model data into props & events
src\core\vdom\create-component.js
// transform component v-model data into props & events
// v-model特殊处理
if (isDef(data.model)) {
transformModel(Ctor.options, data)
}
transformModel
// transform component v-model info (value and callback) into
// prop and event handler respectively.
function transformModel (options, data: any) {
/*
model: {
prop: 'xx',
event: 'xxx,
}
*/
const prop = (options.model && options.model.prop) || 'value'
const event = (options.model && options.model.event) || 'input'
// 给attrs的指定model的prop赋值
;(data.attrs || (data.attrs = {}))[prop] = data.model.value
const on = data.on || (data.on = {})
const existing = on[event]
const callback = data.model.callback
if (isDef(existing)) {
if (
Array.isArray(existing)
? existing.indexOf(callback) === -1
: existing !== callback
) {
on[event] = [callback].concat(existing)
}
} else {
// 给事件里面添加v-model解析出来的内置事件
on[event] = callback
}
}
На этом обмен этой статьей закончен.Большинство компонентов формы реализовано, а макет формы не реализован.На самом деле необходимо использоватьel-row/el-col
Элементы формы под пакетом, и особой логики нет, код выкладываться не будет, если интересно, то можноНажмите, чтобы увидеть код
напиши в конце
Если есть часть статьи, которая написана не очень хорошо или имеет проблемы, вы можете указать на нее, и я продолжу исправлять ее в следующих статьях. Я также надеюсь, что смогу расти вместе с вами по мере моего прогресса. Друзья, которым нравятся мои статьи, также могут обратить внимание
Я был бы признателен, если бы первым подписался на меня.В это время молодые я и ты шли в бой налегке, потом богатые ты и я возвращались с полным грузом.
В игре пренебрегают усердием
серия статей
Научу вас, как играть с функцией рендеринга "компонентная инкапсуляция-динамический-выбор"
Научу вас, как играть с функцией рендеринга "компонентная инкапсуляция-динамический-ввод"
Научите, как играть с функцией рендеринга "инкапсуляция компонента-динамическая-флажок"
Научу вас, как играть с функцией рендеринга "компонентная инкапсуляция-динамический-каскадер"
Прошлые статьи
[Рекомендуется продолжить] Создавайте промежуточные и конечные проекты с модульными идеями.
[Разработка мидл- и бэкенд-проектов с модульными идеями] Глава 1
[Внешняя система] Расскажите о понимании EventLoop из вопроса на собеседовании.(Обновлен анализ четырех дополнительных вопросов)
[Фронтальная система] Построить многоэтажку с фундамента
[Фронтальная система] Сценарий приложения регулярного в разработке — это не просто проверка правил