Давайте поговорим о том, как работает двусторонняя привязка Vue 3.

TypeScript Vue.js
Давайте поговорим о том, как работает двусторонняя привязка Vue 3.

Эта статьяРасширенная серия Vue 3.0Третья статья, прежде чем читать эту статью, рекомендуется прочитатьВ чем суть директив Vue 3.0а такжеЧто произошло после Vue 3.0 $emitэти две статьи. Прежде чем рассмотреть конкретный пример, брат Абао кратко представит двустороннюю привязку, состоящую из двух односторонних привязок:

  • Модель -> Просмотр привязки данных;
  • Вид -> Привязка события модели.

Следуйте «Дороге полного совершенствования», чтобы прочитать 4 оригинальных бесплатных электронных книги (загружено более 30 000) и9 Учебные пособия по Vue 3 Advanced Series.

во Вью:valueДостигнутомодель для просмотрапривязка данных,@eventДостигнутовид на модельПривязка события:

<input :value="searchText" @input="searchText = $event.target.value" />

А в форме с помощью встроенногоv-modelдирективу, мы можем легко реализовать двустороннюю привязку, такую ​​как<input v-model="searchText" />. После ознакомления с приведенным выше содержанием брат А Бао возьмет простой пример в качестве отправной точки, чтобы шаг за шагом раскрыть секреты двустороннего связывания.

<div id="app">
   <input v-model="searchText" />
   <p>搜索的内容:{{searchText}}</p>
</div>
<script>
   const { createApp } = Vue
   const app = createApp({
     data() {
       return {
         searchText: "阿宝哥"
       }
     }
   })
   app.mount('#app')
</script>

В приведенном выше примере мыinputПоле ввода поиска применяетсяv-modelинструкция, когда содержимое поля ввода изменяется,pСодержимое метки будет обновляться синхронно.

раскрытьv-modelСекреты инструкций, которые мы можем использоватьVue 3 Template ExplorerОнлайн-инструменты, посмотрите на результаты компиляции шаблона:

<input v-model="searchText" />

const _Vue = Vue
return function render(_ctx, _cache, $props, $setup, $data, $options) {
  with (_ctx) {
    const { vModelText: _vModelText, createVNode: _createVNode, 
      withDirectives: _withDirectives, openBlock: _openBlock, createBlock: _createBlock } = _Vue

    return _withDirectives((_openBlock(), _createBlock("input", {
      "onUpdate:modelValue": $event => (searchText = $event)
    }, null, 8 /* PROPS */, ["onUpdate:modelValue"])), 
    [ 
      [_vModelText, searchText] 
    ])
  }
}

существует<input v-model="searchText" />В функции рендеринга, сгенерированной шаблоном, мы видимРасширенное изучение инструкций Vue 3.0описано в статьеwithDirectivesфункция, которая используется для добавления информации об инструкциях вVNodeобъект, он определяется вruntime-core/src/directives.tsВ файле:

// packages/runtime-core/src/directives.ts
export function withDirectives<T extends VNode>(
  vnode: T,
  directives: DirectiveArguments
): T {
  const internalInstance = currentRenderingInstance
  // 省略部分代码
  const instance = internalInstance.proxy
  const bindings: DirectiveBinding[] = vnode.dirs || (vnode.dirs = [])
  for (let i = 0; i < directives.length; i++) {
    let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]
    // 在 mounted 和 updated 时,触发相同行为,而不关系其他的钩子函数
    if (isFunction(dir)) { // 处理函数类型指令
      dir = {
        mounted: dir,
        updated: dir
      } as ObjectDirective
    }
    bindings.push({ // 把指令信息保存到vnode.dirs数组中
      dir, instance, value, 
      oldValue: void 0, arg, modifiers
    })
  }
  return vnode
}

Кроме этого, в функции рендеринга, сгенерированной шаблоном, мы видимvModelTextИнструкция, по названию мы догадываемся, что инструкция относится к модели, поэтому сначала разберем ееvModelTextинструкция.

1. Инструкция vModelText

vModelTextинструкцияObjectDirectiveДиректива type, определяющая 3 функции-ловушки:

  • created: Вызывается перед применением свойства или прослушивателя событий связанного элемента.
  • mounted: вызывается после монтирования родительского компонента связанного элемента.
  • beforeUpdate: вызывается перед обновлением виртуального узла, содержащего компонент.
// packages/runtime-dom/src/directives/vModel.ts
type ModelDirective<T> = ObjectDirective<T & { _assign: AssignerFn }>

export const vModelText: ModelDirective<
  HTMLInputElement | HTMLTextAreaElement
> = {
  created(el, { modifiers: { lazy, trim, number } }, vnode) {
    // ...
  },
  mounted(el, { value }) {
    // ..
  },
  beforeUpdate(el, { value, modifiers: { trim, number } }, vnode) {
    // ..
  }
}

Затем Brother A Bao проанализирует каждую функцию крючка одну за другой.createdЗапускается функция ловушки.

1.1 созданный хук

// packages/runtime-dom/src/directives/vModel.ts
export const vModelText: ModelDirective<
  HTMLInputElement | HTMLTextAreaElement
> = {
  created(el, { modifiers: { lazy, trim, number } }, vnode) {
    el._assign = getModelAssigner(vnode)
    const castToNumber = number || el.type === 'number' // 是否转为数值类型
    // 若使用 lazy 修饰符,则在 change 事件触发后将输入框的值与数据进行同步
    addEventListener(el, lazy ? 'change' : 'input', e => { 
      if ((e.target as any).composing) return // 组合输入进行中
      let domValue: string | number = el.value
      if (trim) { // 自动过滤用户输入的首尾空白字符
        domValue = domValue.trim()
      } else if (castToNumber) { // 自动将用户的输入值转为数值类型
        domValue = toNumber(domValue)
      }
      el._assign(domValue) // 更新模型
    })
    if (trim) {
      addEventListener(el, 'change', () => {
        el.value = el.value.trim()
      })
    }
    if (!lazy) {
      addEventListener(el, 'compositionstart', onCompositionStart)
      addEventListener(el, 'compositionend', onCompositionEnd)
      // Safari < 10.2 & UIWebView doesn't fire compositionend when
      // switching focus before confirming composition choice
      // this also fixes the issue where some browsers e.g. iOS Chrome
      // fires "change" instead of "input" on autocomplete.
      addEventListener(el, 'change', onCompositionEnd)
    }
  },
}

дляcreatedметод, он будет получен путем деструктурированияv-modelмодификаторы, добавленные в директиву, вv-modelможно добавить на.lazy,.numberа также.trimмодификатор. Здесь мы кратко представим роль трех модификаторов:

  • .lazyМодификатор: По умолчаниюv-modelкаждый разinputСинхронизируйте значение поля ввода с данными после запуска события. можете добавитьlazyмодификатор, превращающийchangeСинхронизация после события.

    <!-- 在 change 时而非 input 时更新 -->
    <input v-model.lazy="msg" />
    
  • .numberМодификатор: если пользователь хочет автоматически ввести значение в числовой тип, может датьv-modelДобавить кnumberмодификатор. Это часто бывает полезно, потому что даже вtype="number", значение элемента ввода HTML также всегда возвращает строку. Если это значение не может бытьparseFloat()синтаксический анализ возвращает исходное значение.

    <input v-model.number="age" type="number" />
    
  • .trimМодификатор: если вы хотите автоматически фильтровать начальные и конечные пробельные символы, введенные пользователем, вы можете датьv-modelДобавить кtrimмодификатор.

    <input v-model.trim="msg" />
    

пока вcreatedВнутри метода он пройдетgetModelAssignerприобретение функцииModelAssigner, который используется для обновления объектов модели.

// packages/runtime-dom/src/directives/vModel.ts
const getModelAssigner = (vnode: VNode): AssignerFn => {
  const fn = vnode.props!['onUpdate:modelValue']
  return isArray(fn) ? value => invokeArrayFns(fn, value) : fn
}

Для нашего примера поgetModelAssignerполучается функциейModelAssignerобъект$event => (searchText = $event)функция. в полученииModelAssignerПосле объекта мы можем обновить значение модели.createdОстальные коды метода относительно просты, и брат Абао не будет подробно их описывать. Здесь мы представляемcompositionstartа такжеcompositionendмероприятие.

Китайский, японский, корейский и т. д. необходимо сочетать с методом ввода Даже на английском языке можно использовать комбинированный ввод для выбора слов и других операций. В некоторых практических сценариях мы хотим дождаться, пока пользователь объединит часть введенного текста, прежде чем выполнять соответствующую операцию, вместо того, чтобы выполнять соответствующую операцию каждый раз, когда вводится буква. Например, в сценарии поиска по ключевому слову подождите, пока пользователь завершит ввод.апоСделайте поиск позже вместо того, чтобы печатать буквыaЗатем начните поиск. Для этого нам необходимо использоватьcompositionstartа такжеcompositionendмероприятие. Кроме того, следует отметить, что,compositionstartсобытие произошло вinputПеред мероприятием используйте его, чтобы оптимизировать ввод данных на китайском языке.

пониматьcompositionstart(начинается комбинированный ввод) иcompositionend(конец ввода комбинации) событие, давайте посмотрим на него еще разonCompositionStartа такжеonCompositionEndЭти два обработчика событий:

function onCompositionStart(e: Event) {
  ;(e.target as any).composing = true
}

function onCompositionEnd(e: Event) {
  const target = e.target as any
  if (target.composing) { 
    target.composing = false
    trigger(target, 'input')
  }
}

// 触发元素上的指定事件
function trigger(el: HTMLElement, type: string) {
  const e = document.createEvent('HTMLEvents')
  e.initEvent(type, true, true)
  el.dispatchEvent(e)
}

При объединении входов вonCompositionStartВ обработчике события будетe.targetдобавить на объектcomposingсвойство и установите значение этого свойства равнымtrue. пока вchangeсобытие илиinputВ функции обратного вызова события, если найденоe.targetобъектcomposingсобственностьtrueвернется напрямую. Когда ввод комбинации завершен, вonCompositionEndВ обработчике событияtarget.composingЗначение установлено наfalseи запустить его вручнуюinputмероприятие:

// packages/runtime-dom/src/directives/vModel.ts
export const vModelText: ModelDirective<
  HTMLInputElement | HTMLTextAreaElement
> = {
  created(el, { modifiers: { lazy, trim, number } }, vnode) {
    // 省略部分代码
    addEventListener(el, lazy ? 'change' : 'input', e => {
      if ((e.target as any).composing) return
   		// ...
    })
  },
}

ХОРОШО,createdФункция ловушки анализируется здесь, давайте проанализируем ее дальшеmountedкрюк.

1.2 навесной крюк

// packages/runtime-dom/src/directives/vModel.ts
export const vModelText: ModelDirective<
  HTMLInputElement | HTMLTextAreaElement
> = {
  // set value on mounted so it's after min/max for type="range"
  mounted(el, { value }) {
    el.value = value == null ? '' : value
  },
}

mountedЛогика хука проста, еслиvalueзначениеnull, установите значение элемента в пустую строку, в противном случае используйте его напрямуюvalueценность .

1.3 ловушка перед обновлением

// packages/runtime-dom/src/directives/vModel.ts
export const vModelText: ModelDirective<
  HTMLInputElement | HTMLTextAreaElement
> = {
  beforeUpdate(el, { value, modifiers: { trim, number } }, vnode) {
    el._assign = getModelAssigner(vnode)
    // avoid clearing unresolved text. #2302
    if ((el as any).composing) return
    if (document.activeElement === el) {
      if (trim && el.value.trim() === value) {
        return
      }
      if ((number || el.type === 'number') && toNumber(el.value) === value) {
        return
      }
    }
    const newValue = value == null ? '' : value
    if (el.value !== newValue) { // 新旧值不相等时,执行更新操作
      el.value = newValue
    }
  }
}

Я считаю, что друзья, которые использовали Vue, знают, чтоv-modelДирективы могут применяться не только вinputа такжеtextareaВ элементах его также можно использовать в Checkbox, Radio и Select.v-modelинструкция. Однако следует отметить, что хотя эти элементы используются вv-modelКоманда, но на самом деле для флажков, коробок и коробок для выбора, которая выполняется функцией, соответствующей разным инструкциям. Здесь мы берем одну коробку, например, посмотрите на приложениеv-modelПосле директивы результат компиляции шаблона:

<input type="radio" value="One" v-model="picked" />

const _Vue = Vue
return function render(_ctx, _cache, $props, $setup, $data, $options) {
  with (_ctx) {
    const { vModelRadio: _vModelRadio, createVNode: _createVNode, 
      withDirectives: _withDirectives, openBlock: _openBlock, createBlock: _createBlock } = _Vue

    return _withDirectives((_openBlock(), _createBlock("input", {
      type: "radio",
      value: "One",
      "onUpdate:modelValue": $event => (picked = $event)
    }, null, 8 /* PROPS */, ["onUpdate:modelValue"])), [
      [_vModelRadio, picked]
    ])
  }
}

Как видно из приведенного выше кода, в приложении радиокнопкиv-modelПосле инструкции функция двухсторонней привязки будет переданаvModelRadioинструкции по реализации. КромеvModelRadioКроме того, естьvModelSelectа такжеvModelCheckboxДирективы, они определены вruntime-dom/src/directives/vModel.tsФайл, младший партнер заинтересован может посмотреть на себя.

фактическиv-modelПо сути, синтаксический сахар. Он отвечает за прослушивание событий пользовательского ввода для обновления данных и выполнения некоторой специальной обработки в определенных сценариях. должен быть в курсеv-modelбудет игнорировать все элементы формыvalue,checked,selectedНачальное значение атрибута всегда использует данные текущего активного экземпляра в качестве источника данных. Вы должны передать компонентdataНачальное значение объявлено в варианте.

также,v-modelВнутренне используйте разные свойства и генерируйте разные события для разных элементов ввода:

  • использование элементов text и textareavalueимущество иinputмероприятие;
  • использование флажков и радиоэлементовcheckимущество иchangeмероприятие;
  • выбранный элемент будетvalueкак опора и воляchangeкак событие.

Здесь вы уже знаете, что можете использоватьv-modelИнструкции в форме<input>,<textarea>а также<select>Создайте двустороннюю привязку данных к элементу. Но если вы хотите использовать на сборкеv-modelинструкция по созданию двусторонней привязки данных, как ее реализовать?

2. Используйте v-модель для компонентов

Предположим, вы хотите определитьcustom-inputкомпонент и использовать на этом компонентеv-modelинструкция для достижения двусторонней привязки, перед реализацией этой функции мы сначала используемVue 3 Template ExplorerОнлайн-инструменты, посмотрите на результаты компиляции шаблона:

<custom-input v-model="searchText"></custom-input>

const _Vue = Vue
return function render(_ctx, _cache, $props, $setup, $data, $options) {
  with (_ctx) {
    const { resolveComponent: _resolveComponent, createVNode: _createVNode, 
      openBlock: _openBlock, createBlock: _createBlock } = _Vue

    const _component_custom_input = _resolveComponent("custom-input")
    return (_openBlock(), _createBlock(_component_custom_input, {
      modelValue: searchText,
      "onUpdate:modelValue": $event => (searchText = $event)
    }, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue"]))
  }
}

Наблюдая за приведенной выше функцией рендеринга, мы можем видеть, что вcustom-inputпримененный компонентv-modelИнструкция после компиляции компилятором сгенерирует файл с именемmodelValueвходное свойство иupdate:modelValueпроизвольное название события. Если вам все еще неясно внутреннее устройство пользовательских событий, вы можете прочитатьРасширенное исследование пользовательских событий Vue 3.0Эта статья. Зная это, мы можем приступить к реализацииcustom-inputКомпоненты:

<div id="app">
   <custom-input v-model="searchText"></custom-input>
   <p>搜索的内容:{{searchText}}</p>
</div>
<script>
   const { createApp } = Vue
   const app = createApp({
     data() {
       return {
         searchText: "阿宝哥"
       }
     }
    })
   app.component('custom-input', {
     props: ['modelValue'],
     emits: ['update:modelValue'],
     template: `
       <input type="text" 
          :value="modelValue"
          @input="$emit('update:modelValue', $event.target.value)"
       >`
   })
   app.mount('#app')
</script>

Для реализации функции двусторонней привязки в пользовательских компонентах, помимо использования пользовательских событий, также можно использовать функцию вычисляемых свойств для определенияgetterа такжеsetter. Брат Абао не будет представлять его здесь, и заинтересованные друзья могут прочитать его.Официальный сайт Vue 3 — Основы компонентовсвязанный контент.

Следуйте «Дороге полного совершенствования», чтобы прочитать 4 оригинальных бесплатных электронных книги (загружено более 30 000) и9 Учебные пособия по Vue 3 Advanced Series.

3. Брату А Бао есть что сказать

3.1 Как изменить имя реквизита по умолчанию и имя события v-model?

По умолчанию в компонентеv-modelиспользоватьmodelValueкак опора иupdate:modelValueкак событие. мы можем пройтиv-modelДиректива передает аргументы для изменения этих имен:

<custom-input v-model:name="searchText"></custom-input>

Приведенный выше шаблон, скомпилированный компилятором, в результате выглядит следующим образом:

const _Vue = Vue
return function render(_ctx, _cache, $props, $setup, $data, $options) {
  with (_ctx) {
    const { resolveComponent: _resolveComponent, createVNode: _createVNode, 
      openBlock: _openBlock, createBlock: _createBlock } = _Vue

    const _component_custom_input = _resolveComponent("custom-input")
    return (_openBlock(), _createBlock(_component_custom_input, {
      name: searchText,
      "onUpdate:name": $event => (searchText = $event)
    }, null, 8 /* PROPS */, ["name", "onUpdate:name"]))
  }
}

Наблюдая за сгенерированной функцией рендеринга, мы знаем, что пользовательскаяcustom-inputкомпонент получаетnameВведите атрибут и включитеupdate:nameпользовательское событие:

app.component('custom-input', {
  props: {
    name: String
  },
  emits: ['update:name'],
  template: `
    <input type="text"
      :value="name"
      @input="$emit('update:name', $event.target.value)">
  `
})

Какое название пользовательского события?"onUpdate:name"В этой форме вы можете получитьРасширенное исследование пользовательских событий Vue 3.0описано в этой статьеemitНайдите соответствующий ответ в функции.

3.2 Могу ли я использовать несколько директив v-model для компонента?

В некоторых сценариях мы хотим использовать несколькоv-modelИнструкции, каждая инструкция привязана к разным данным. напримерuser-nameкомпонент, который позволяет пользователю вводитьfirstNameа такжеlastName. Ожидаемое использование этого компонента выглядит следующим образом:

<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>

Так же мы используемVue 3 Template ExplorerОнлайн-инструменты, сначала посмотрите на результаты компиляции приведенного выше шаблона:

const _Vue = Vue
return function render(_ctx, _cache, $props, $setup, $data, $options) {
  with (_ctx) {
    const { resolveComponent: _resolveComponent, createVNode: _createVNode, 
      openBlock: _openBlock, createBlock: _createBlock } = _Vue

    const _component_user_name = _resolveComponent("user-name")
    return (_openBlock(), _createBlock(_component_user_name, {
      "first-name": firstName,
      "onUpdate:first-name": $event => (firstName = $event),
      "last-name": lastName,
      "onUpdate:last-name": $event => (lastName = $event)
    }, null, 8 /* PROPS */, ["first-name", "onUpdate:first-name", "last-name", "onUpdate:last-name"]))
  }
}

Наблюдая за приведенными выше результатами вывода, мы можем видеть, чтоv-model:first-nameа такжеv-model:last-nameБудут сгенерированы соответствующие свойства реквизита и пользовательские события. Имена атрибутов в HTML нечувствительны к регистру, поэтому браузеры интерпретируют все символы верхнего регистра как строчные. Это означает, что когда вы используете шаблоны из DOM, имена пропсов в верблюжьем регистре (верблюжий регистр) должны быть названы с использованием их эквивалентов в кебаб-кейсе (имена, разделенные тире). Например:

<!-- kebab-case in HTML -->
<blog-post post-title="hello!"></blog-post>

app.component('blog-post', {
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})

Наоборот, дляfirst-nameа такжеlast-nameДля имен атрибутов в определенииuser-nameкомпонент, мы будем использоватьfirstNameа такжеlastNameИменование CamelCase.

 <div id="app">
    <user-name
       v-model:first-name="firstName"
       v-model:last-name="lastName">
    </user-name>
    Your name: {{firstName}} {{lastName}}
</div>
<script>
   const { createApp } = Vue
   const app = createApp({
     data() {
       return {
         firstName: "",
         lastName: ""
       }
     }
   })
   app.component('user-name', {
     props: {
       firstName: String,
       lastName: String
     },
     emits: ['update:firstName', 'update:lastName'],
     template: `
       <input
          type="text"
          :value="firstName"
          @input="$emit('update:firstName', $event.target.value)">
       <input
          type="text"
          :value="lastName"
          @input="$emit('update:lastName', $event.target.value)">
      `
   })
   app.mount('#app')
</script>

В приведенном выше кодеuser-nameИмена пользовательских свойств и событий, используемые компонентом, написаны верблюжьим регистром. Очевидно, что это несовместимо с форматом именования, сгенерированным после компиляции шаблона, поэтому приведенное вышеuser-nameКомпонент работает корректно? Ответ положительный, потому что для пользовательских событий вemitВнутри функция пройдетhyphenateфункцию, измените имя события сcamelCase(CamelCase) преобразуется вkebab-case(имена, разделенные тире), т.е.hyphenate(event):

// packages/runtime-core/src/componentEmits.ts
export function emit(
  instance: ComponentInternalInstance,
  event: string,
  ...rawArgs: any[]
) {
  // 省略部分代码
  // for v-model update:xxx events, also trigger kebab-case equivalent
  // for props passed via kebab-case
  if (!handler && isModelListener) {
    handlerName = toHandlerKey(hyphenate(event))
    handler = props[handlerName]
  }

  if (handler) {
    callWithAsyncErrorHandling(
      handler,
      instance,
      ErrorCodes.COMPONENT_EVENT_HANDLER,
      args
    )
  }
}

а такжеhyphenateРеализация функции также очень проста, а именно:

// packages/shared/src/index.ts
const hyphenateRE = /\B([A-Z])/g

// cacheStringFunction 函数提供了缓存功能
export const hyphenate = cacheStringFunction((str: string) =>
  str.replace(hyphenateRE, '-$1').toLowerCase()
)

3.3 Как добавить пользовательские модификаторы в v-модель?

Брат А Бао уже представил его раньше.v-modelвстроенные модификаторы:.trim,.numberа также.lazy. Но в некоторых сценариях вы можете захотеть настроить модификатор. Прежде чем рассказать, как настраивать модификаторы, мы снова используемVue 3 Template Explorerонлайн-инструменты, посмотритеv-modelРезультат компиляции шаблона после использования встроенных модификаторов:

<custom-input v-model.lazy.number="searchText"></custom-input>

const _Vue = Vue
return function render(_ctx, _cache, $props, $setup, $data, $options) {
  with (_ctx) {
    const { resolveComponent: _resolveComponent, createVNode: _createVNode, 
      openBlock: _openBlock, createBlock: _createBlock } = _Vue

    const _component_custom_input = _resolveComponent("custom-input")
    return (_openBlock(), _createBlock(_component_custom_input, {
      modelValue: searchText,
      "onUpdate:modelValue": $event => (searchText = $event),
      modelModifiers: { lazy: true, number: true }
    }, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue"]))
  }
}

Глядя на сгенерированную функцию рендеринга, мы можем увидетьv-modelдобавлено.lazyа также.numberмодификатор, скомпилированный вmodelModifiersреквизитное имущество. Предположим, мы хотим настроитьcapitalizeмодификатор, который действует какv-modelПреобразуйте первую букву строки привязки в верхний регистр:

<custom-input v-model.capitalize="searchText"></custom-input>

const _Vue = Vue
return function render(_ctx, _cache, $props, $setup, $data, $options) {
  with (_ctx) {
    const { resolveComponent: _resolveComponent, createVNode: _createVNode, 
      openBlock: _openBlock, createBlock: _createBlock } = _Vue

    const _component_custom_input = _resolveComponent("custom-input")
    return (_openBlock(), _createBlock(_component_custom_input, {
      modelValue: searchText,
      "onUpdate:modelValue": $event => (searchText = $event),
      modelModifiers: { capitalize: true }
    }, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue"]))
  }
}

Это понятноv-modelВверх.capitalizeмодификатор, также скомпилированный вmodelModifiersреквизитное имущество. Зная это, мы можем реализовать вышеуказанные модификаторы следующим образом:

<div id="app">
   <custom-input v-model.capitalize="searchText"></custom-input>
   <p>搜索的内容:{{searchText}}</p>
</div>
<script>
   const { createApp } = Vue
   const app = createApp({
     data() {
       return {
         searchText: ""
       }
     }
   })
   app.component('custom-input', {
     props: {
       modelValue: String,
       modelModifiers: {
         default: () => ({})
       }
     },
     emits: ['update:modelValue'],
     methods: {
       emitValue(e) {
         let value = e.target.value
         if (this.modelModifiers.capitalize) {
           value = value.charAt(0).toUpperCase() + value.slice(1)
         }
         this.$emit('update:modelValue', value)
       }
     },
     template: `<input
       type="text"
       :value="modelValue"
       @input="emitValue">`
   })
  app.mount('#app')
</script>

В этой статье в основном представлена ​​концепция двусторонней привязки и принцип двусторонней привязки в Vue 3. Для того, чтобы все лучше понималиv-modelсвязанные знания, брат Абао проанализировал с точки зрения исходного кодаvModelTextВнутренняя реализация директивы. Кроме того, Brother Abao также представил, как использовать несколько компонентов в компонентах.v-modelИнструкции и какv-modelДобавьте пользовательские модификаторы.

Статьи в расширенной серии Vue 3.0 все еще обновляются и были обновлены до девятой статьи.Друзья, которые хотят изучать Vue 3.0 вместе, могут добавить Abaoge WeChat - semlinker.

4. Справочные ресурсы