1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【Vue.js】880- Vue 3.0 進階之雙向綁定探秘

        共 29583字,需瀏覽 60分鐘

         ·

        2021-02-28 08:56

        本文是 Vue 3.0 進階系列 的第三篇文章,在閱讀本文前,建議你先閱讀 Vue 3.0 進階之指令探秘Vue 3.0 進階之自定義事件探秘 這兩篇文章。在看具體示例前,阿寶哥先來簡單介紹一下雙向綁定,它由兩個單向綁定組成:

        • 模型 —> 視圖數(shù)據(jù)綁定;
        • 視圖 —> 模型事件綁定。

        在 Vue 中 :value 實現(xiàn)了 模型到視圖 的數(shù)據(jù)綁定,@event 實現(xiàn)了 視圖到模型 的事件綁定:

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

        而在表單中,通過使用內(nèi)置的 v-model 指令,我們可以輕松地實現(xiàn)雙向綁定,比如 <input v-model="searchText" />。介紹完上面的內(nèi)容,接下來阿寶哥將以一個簡單的示例為切入點,帶大家一起一步步揭開雙向綁定背后的秘密。

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

        在以上示例中,我們在 input 搜索輸入框中應用了 v-model 指令,當輸入框的內(nèi)容發(fā)生變化時,p 標簽中內(nèi)容會同步更新。

        要揭開 v-model 指令背后的秘密,我們可以利用 Vue 3 Template Explorer 在線工具,來看一下模板編譯后的結(jié)果:

        <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)
            }, null8 /* PROPS */, ["onUpdate:modelValue"])), 
            [ 
              [_vModelText, searchText] 
            ])
          }
        }

        <input v-model="searchText" /> 模板生成的渲染函數(shù)中,我們看到了 Vue 3.0 進階之指令探秘 文章中介紹的 withDirectives 函數(shù),該函數(shù)用于把指令信息添加到 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 時,觸發(fā)相同行為,而不關系其他的鉤子函數(shù)
            if (isFunction(dir)) { // 處理函數(shù)類型指令
              dir = {
                mounted: dir,
                updated: dir
              } as ObjectDirective
            }
            bindings.push({ // 把指令信息保存到vnode.dirs數(shù)組中
              dir, instance, value, 
              oldValue: void 0, arg, modifiers
            })
          }
          return vnode
        }

        除此之外,在模板生成的渲染函數(shù)中,我們看到了 vModelText 指令,通過它的名稱,我們猜測該指令與模型相關,所以我們先來分析 vModelText 指令。

        一、vModelText 指令

        vModelText 指令是 ObjectDirective 類型的指令,該指令中定義了 3 個鉤子函數(shù):

        • created:在綁定元素的屬性或事件監(jiān)聽器被應用之前調(diào)用。
        • mounted:在綁定元素的父組件被掛載后調(diào)用。
        • beforeUpdate:在更新包含組件的 VNode 之前調(diào)用。
        // 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) {
            // ..
          }
        }

        接下來,阿寶哥將逐一分析每個鉤子函數(shù),這里先從 created 鉤子函數(shù)開始。

        1.1 created 鉤子

        // 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' // 是否轉(zhuǎn)為數(shù)值類型
            // 若使用 lazy 修飾符,則在 change 事件觸發(fā)后將輸入框的值與數(shù)據(jù)進行同步
            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) { // 自動將用戶的輸入值轉(zhuǎn)為數(shù)值類型
                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 方法來說,它會通過解構(gòu)的方式獲取 v-model 指令上添加的修飾符,在 v-model 上可以添加 .lazy、.number.trim 修飾符。這里我們簡單介紹一下 3 種修飾符的作用:

        • .lazy 修飾符:在默認情況下,v-model 在每次 input 事件觸發(fā)后將輸入框的值與數(shù)據(jù)進行同步。你可以添加 lazy 修飾符,從而轉(zhuǎn)為在 change 事件之后進行同步。

          <!-- 在 change 時而非 input 時更新 -->
          <input v-model.lazy="msg" />
        • .number 修飾符:如果想自動將用戶的輸入值轉(zhuǎn)為數(shù)值類型,可以給 v-model 添加 number 修飾符。這通常很有用,因為即使在 type="number" 時,HTML 輸入元素的值也總會返回字符串。如果這個值無法被 parseFloat() 解析,則會返回原始的值。

          <input v-model.number="age" type="number" />
        • .trim 修飾符:如果要自動過濾用戶輸入的首尾空白字符,可以給 v-model 添加 trim 修飾符。

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

        而在 created 方法內(nèi)部,會通過 getModelAssigner 函數(shù)獲取 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 函數(shù)獲取的 ModelAssigner 對象是 $event => (searchText = $event) 函數(shù)。在獲取  ModelAssigner 對象之后,我們就可以更新模型的值了。created 方法中的其他代碼相對比較簡單,阿寶哥就不詳細介紹了。這里我們來介紹一下 compositionstartcompositionend 事件。

        中文、日文、韓文等需要借助輸入法組合輸入,即使是英文,也可以利用組合輸入進行選詞等操作。在一些實際場景中,我們希望等用戶組合輸入完的一段文字才進行對應操作,而不是每輸入一個字母,就執(zhí)行相關操作。

        比如,在關鍵字搜索場景中,等用戶完整輸入 阿寶哥 之后再執(zhí)行搜索操作,而不是輸入字母 a 之后就開始搜索。要實現(xiàn)這個功能,我們就需要借助 compositionstartcompositionend 事件。另外,需要注意的是,compositionstart 事件發(fā)生在 input 事件之前,因此利用它可以優(yōu)化中文輸入的體驗。

        了解完 compositionstart(組合輸入開始) 和 compositionend (組合輸入結(jié)束)事件,我們再來看一下 onCompositionStartonCompositionEnd 這兩個事件處理器:

        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')
          }
        }

        // 觸發(fā)元素上的指定事件
        function trigger(el: HTMLElement, typestring{
          const e = document.createEvent('HTMLEvents')
          e.initEvent(typetruetrue)
          el.dispatchEvent(e)
        }

        當組合輸入時,在 onCompositionStart 事件處理器中,會 e.target 對象上添加 composing 屬性并設置該屬性的值為 true。而在 change 事件或 input 事件回調(diào)函數(shù)中,如果發(fā)現(xiàn)  e.target 對象的 composing 屬性為 true 則會直接返回。當組合輸入完成后,在 onCompositionEnd 事件處理器中,會把 target.composing 的值設置為 false 并手動觸發(fā) 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 鉤子函數(shù)就分析到這里,接下來我們來分析 mounted 鉤子。

        1.2 mounted 鉤子

        // 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 beforeUpdate 鉤子

        // 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) { // 新舊值不相等時,執(zhí)行更新操作
              el.value = newValue
            }
          }
        }

        相信使用過 Vue 的小伙伴都知道,v-model 指令不僅可以應用在 inputtextarea 元素上,在復選框(Checkbox)、單選框(Radio)和選擇框(Select)上也可以使用 v-model 指令。不過需要注意的是,雖然這些元素上都是使用 v-model 指令,但實際上對于復選框、單選框和選擇框來說,它們是由不同的指令來完成對應的功能。這里我們以單選框為例,來看一下應用 v-model 指令后,模板編譯的結(jié)果:

        <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)
            }, null8 /* PROPS */, ["onUpdate:modelValue"])), [
              [_vModelRadio, picked]
            ])
          }
        }

        由以上代碼可知,在單選框應用 v-model 指令后,雙向綁定的功能會交給 vModelRadio 指令來實現(xiàn)。除了 vModelRadio 之外,還有 vModelSelectvModelCheckbox 指令,它們被定義在 runtime-dom/src/directives/vModel.ts 文件中,感興趣的小伙伴可以自行研究一下。

        其實 v-model 本質(zhì)上是語法糖。它負責監(jiān)聽用戶的輸入事件來更新數(shù)據(jù),并在某些場景下進行一些特殊處理。需要注意的是 v-model 會忽略所有表單元素的 value、checkedselected attribute 的初始值而總是將當前活動實例的數(shù)據(jù)作為數(shù)據(jù)來源。你應該通過在組件的 data 選項中聲明初始值。

        此外,v-model 在內(nèi)部為不同的輸入元素使用不同的 property 并拋出不同的事件:

        • text 和 textarea 元素使用 value property 和 input 事件;
        • checkbox 和 radio 元素使用 check property 和 change 事件;
        • select 元素將 value 作為 prop 并將 change 作為事件。

        這里你已經(jīng)知道,可以用 v-model 指令在表單 <input>、<textarea><select> 元素上創(chuàng)建雙向數(shù)據(jù)綁定。但如果你也想在組件上使用 v-model 指令來創(chuàng)建雙向數(shù)據(jù)綁定,那應該如何實現(xiàn)呢?

        二、在組件上使用 v-model

        假設你想定義一個 custom-input 組件并在該組件上使用 v-model 指令來實現(xiàn)雙向綁定,在實現(xiàn)該功能前,我們先利用 Vue 3 Template Explorer 在線工具,看一下模板編譯后的結(jié)果:

        <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"]))
          }
        }

        通過觀察以上的渲染函數(shù),我們可知在 custom-input 組件上應用了 v-model 指令,經(jīng)過編譯器編譯之后,會生成一個名為 modelValue 的輸入屬性和一個名為 update:modelValue 的自定義事件名。如果你對自定義事件內(nèi)部原理還不清楚的話,可以閱讀 Vue 3.0 進階之自定義事件探秘 這篇文章。了解完這些內(nèi)容之后,我們就可以開始實現(xiàn) custom-input 組件了:

        <div id="app">
           <custom-input v-model="searchText"></custom-input>
           <p>搜索的內(nèi)容:{{searchText}}</p>
        </div>
        <script>
           const { createApp } = Vue
           const app = Vue.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>

        在自定義組件中實現(xiàn)雙向綁定的功能,除了使用自定義事件之外,還可以使用計算屬性的功能來定義 gettersetter。這里阿寶哥就不展開介紹了,感興趣的小伙伴可以閱讀 Vue 3 官網(wǎng) - 組件基礎 的相關內(nèi)容。

        三、阿寶哥有話說

        3.1 如何修改 v-model 默認的 prop 名和事件名?

        默認情況下,組件上的 v-model 使用 modelValue 作為 prop 和 update:modelValue 作為事件。我們可以通過向 v-model 指令傳遞參數(shù)來修改這些名稱:

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

        以上的模板,經(jīng)過編譯器編譯后的結(jié)果如下:

        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)
            }, null8 /* PROPS */, ["name""onUpdate:name"]))
          }
        }

        通過觀察生成的渲染函數(shù),我們可知自定義 custom-input 組件接收一個 name 輸入屬性并含有一個名為 update:name 的自定義事件:

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

        })

        至于自定義的事件名為什么是 "onUpdate:name" 這種形式,你可以從 Vue 3.0 進階之自定義事件探秘 這篇文章中介紹的 emit 函數(shù)中找到對應的答案。

        3.2 能否在組件上使用多個 v-model 指令?

        在某些場景下,我們是希望在組件上使用多個 v-model 指令,每個指令與不同的數(shù)據(jù)做綁定。比如一個 user-name 組件,該組件允許用戶輸入 firstNamelastName。該組件期望的使用方式如下:

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

        同樣,我們使用 Vue 3 Template Explorer 在線工具,先來看一下以上模板編譯后的結(jié)果:

        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)
            }, null8 /* PROPS */, ["first-name""onUpdate:first-name""last-name""onUpdate:last-name"]))
          }
        }

        通過觀察以上的輸出結(jié)果,我們可知 v-model:first-namev-model:last-name 都會生成對應的 prop 屬性和自定義事件。HTML 中的 attribute 名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋為小寫字符。這意味著當你使用 DOM 中的模板時,camelCase (駝峰命名法)的 prop 名需要使用其等價的 kebab-case(短橫線分隔命名)命名。比如:

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

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

        反之,對于 first-namelast-name 屬性名來說,在定義 user-name 組件時,我們將使用 firstNamelastName 駝峰命名方式。

         <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 = Vue.createApp({
             data() {
               return {
                 firstName"",
                 lastName""
               }
             }
           })
           app.component('user-name', {
             props: {
               firstNameString,
               lastNameString
             },
             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 函數(shù)內(nèi)部會通過 hyphenate 函數(shù),把事件名從 camelCase(駝峰命名法)的形式轉(zhuǎn)換為 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 函數(shù)的實現(xiàn)也很簡單,具體如下所示:

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

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

        3.3 如何為 v-model 添加自定義修飾符?

        在前面阿寶哥已經(jīng)介紹了 v-model 的內(nèi)置修飾符:.trim.number.lazy。但在某些場景下,你可能希望自定義修飾符。在介紹如何自定義修飾符前,我們再次利用 Vue 3 Template Explorer 在線工具,看一下 v-model 使用內(nèi)置修飾符后,模板編譯的結(jié)果:

        <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"]))
          }
        }

        通過觀察生成的渲染函數(shù),我們可以看到 v-model 上添加的 .lazy.number 修飾符,被編譯到 modelModifiers prop 屬性中。假設我們要為自定義一個 capitalize 修飾符 ,該修飾符的作用是將 v-model 綁定字符串的第一個字母轉(zhuǎn)成大寫:

        <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 prop 屬性中。了解完這些,我們就可以實現(xiàn)上述的修飾符,具體如下所示:

        <div id="app">
           <custom-input v-model.capitalize="searchText"></custom-input>
           <p>搜索的內(nèi)容:{{searchText}}</p>
        </div>
        <script>
           const { createApp } = Vue
           const app = Vue.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 指令的內(nèi)部實現(xiàn)。

        此外,阿寶哥還介紹了在組件中如何使用多個 v-model 指令及如何為 v-model 添加自定義修飾符。Vue 3.0 進階系列的文章還在持續(xù)更新,感興趣的小伙伴請持續(xù)關注喲。

        四、參考資源

        • Vue 3 官網(wǎng) - 自定義指令
        • Vue 3 官網(wǎng) - 自定義事件
        聚焦全棧,專注分享 TypeScript、Web API、前端架構(gòu)等技術(shù)干貨。

        瀏覽 50
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            91黄色电影 | 无码首页 | 亚洲AV影院 | 天天操大香蕉 | 粗大黑人巨茎大战欧美成人 | 96av很很操天天日 | 久操电影 | 国产毛多水多女人A片 | 精品一区二区三区av | 大骚逼AV |