欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue3中v-model的原理示例詳解

 更新時(shí)間:2025年04月29日 09:50:27   作者:flying robot  
原生 JavaScript 實(shí)現(xiàn),模擬 Vue 3 的 v-model 功能,包括表單元素和組件支持,我們將逐步構(gòu)建代碼,并提供使用示例,對(duì)vue v-model原理解析感興趣的朋友一起看看吧

模擬 Vue 3 的完整 v-model 功能,我們需要實(shí)現(xiàn)以下特性:**

  • 表單元素的雙向綁定:支持 、、 等表單元素,包括修飾符(如 .lazy、.number、.trim)。
  • 自定義組件的雙向綁定:支持 v-model 在組件上的使用,基于 modelValue 和 update:modelValue,并支持多重 v-model(如 v-model:name)。
  • 自定義修飾符:支持組件上的自定義修飾符(如 v-model.capitalize)。
  • 響應(yīng)式系統(tǒng):實(shí)現(xiàn)一個(gè)簡(jiǎn)化的響應(yīng)式系統(tǒng),模擬 Vue 3 的 ref 或 Proxy 行為,確保數(shù)據(jù)變化觸發(fā)視圖更新。

原生 JavaScript 實(shí)現(xiàn),模擬 Vue 3 的 v-model 功能,包括表單元素和組件支持。我們將逐步構(gòu)建代碼,并提供使用示例。**

實(shí)現(xiàn)步驟

1. 響應(yīng)式系統(tǒng)(模擬 ref)

我們需要一個(gè)簡(jiǎn)單的響應(yīng)式系統(tǒng),類似于 Vue 3 的 ref,用于創(chuàng)建響應(yīng)式數(shù)據(jù)并在數(shù)據(jù)變化時(shí)通知依賴。

javascript

// 定義 Dep 對(duì)象,用于依賴收集
let Dep = {
 target: null // 當(dāng)前依賴的回調(diào)函數(shù)
};
// 模擬 Vue 3 的 ref
function ref(initialValue) {
  const deps = new Set();
  let _value = initialValue;
  return {
    get value() {
      // 依賴收集
      if (typeof Dep.target === 'function') {
        deps.add(Dep.target);
      }
      return _value;
    },
    set value(newValue) {
      if (_value !== newValue) {
        _value = newValue;
        // 通知依賴
        deps.forEach((dep) => dep());
      }
    }
  };
}
// 依賴收集的全局變量
Dep.target = null;
// 模擬 watchEffect,用于依賴收集
function watchEffect(callback) {
  Dep.target = callback;
  callback();
  Dep.target = null;
}
  • ref 創(chuàng)建響應(yīng)式數(shù)據(jù),getter 收集依賴,setter 通知依賴更新。
  • watchEffect 模擬 Vue 的依賴收集機(jī)制,執(zhí)行回調(diào)并收集依賴。

2. 表單元素的 v-model

表單元素的 v-model:value@input(或 @change)的語(yǔ)法糖。我們需要:

  • 綁定響應(yīng)式數(shù)據(jù)到元素的 value(或 checked)。
  • 監(jiān)聽輸入事件,更新響應(yīng)式數(shù)據(jù)。
  • 支持修飾符(如 .lazy、.number**、.trim)。**

javascript

// 處理修飾符邏輯
function applyModifiers(value, modifiers) {
  let result = value;
  if (modifiers.number) {
    result = isNaN(Number(result)) ? result : Number(result);
  }
  if (modifiers.trim && typeof result === 'string') {
    result = result.trim();
  }
  return result;
}
// 表單元素的 v-model
function vModelForm(element, reactiveRef, modifiers = {}) {
  const tag = element.tagName.toLowerCase();
  const type = element.type;
  // 確定事件類型
  const eventName = modifiers.lazy
    ? 'change'
    : tag === 'select' || type === 'checkbox' || type === 'radio'
      ? 'change'
      : 'input';
  // 更新視圖
  const updateView = () => {
    if (type === 'checkbox') {
      element.checked = reactiveRef.value;
    } else if (type === 'radio') {
      element.checked = reactiveRef.value === element.value;
    } else {
      element.value = reactiveRef.value;
    }
  };
  // 初始綁定和依賴收集
  Dep.target = updateView;
  updateView();
  Dep.target = null;
  // 監(jiān)聽輸入事件
  element.addEventListener(eventName, (event) => {
    let newValue;
    if (type === 'checkbox') {
      newValue = event.target.checked;
    } else if (type === 'radio') {
      if (event.target.checked) {
        newValue = event.target.value;
      } else {
        return; // 未選中時(shí)不更新
      }
    } else {
      newValue = event.target.value;
    }
    // 應(yīng)用修飾符
    newValue = applyModifiers(newValue, modifiers);
    reactiveRef.value = newValue;
  });
  // 數(shù)據(jù)變化時(shí)更新視圖
  reactiveRef.deps = reactiveRef.deps || new Set();
  reactiveRef.deps.add(updateView);
}
  • 修飾符處理:applyModifiers 處理 .number 和 .trim 修飾符。
  • 事件選擇:根據(jù)元素類型(checkbox、radio、select)或修飾符(.lazy)選擇 input 或 change 事件。
  • 視圖更新:根據(jù)元素類型更新 value 或 checked。
  • 事件監(jiān)聽:根據(jù)元素類型獲取用戶輸入的值,并應(yīng)用修飾符后更新響應(yīng)式數(shù)據(jù)。

3. 自定義組件的 v-model

組件的 v-model 基于 modelValueupdate:modelValue,支持多重 v-model 和自定義修飾符。我們模擬組件為一個(gè)簡(jiǎn)單的 DOM 結(jié)構(gòu),包含子組件邏輯。

javascript

// 模擬組件的 v-model
function vModelComponent(element, reactiveRef, propName = 'modelValue', modifiers = {}) {
  // 模擬組件的 props 和 emit
  const props = { [propName]: reactiveRef.value };
  const emit = (eventName, value) => {
    if (eventName === `update:${propName}`) {
      // 應(yīng)用修飾符
      let newValue = value;
      if (modifiers.capitalize && typeof newValue === 'string') {
        newValue = newValue.charAt(0).toUpperCase() + newValue.slice(1);
      }
      reactiveRef.value = newValue;
    }
  };
  // 更新視圖
  const updateView = () => {
    element.value = reactiveRef.value; // 模擬組件內(nèi)部 input 的 value
  };
  // 初始綁定
  Dep.target = updateView;
  updateView();
  Dep.target = null;
  // 模擬組件內(nèi)部的 input 事件
  element.addEventListener('input', (event) => {
    emit(`update:${propName}`, event.target.value);
  });
  // 數(shù)據(jù)變化時(shí)更新視圖
  reactiveRef.deps = reactiveRef.deps || new Set();
  reactiveRef.deps.add(updateView);
}
  • props 和 emit:模擬組件的 props(如 modelValue)和 $emit(如 update:modelValue)。
  • 多重 v-model:通過(guò) propName 支持自定義屬性(如 name、age)。
  • 修飾符:支持自定義修飾符(如 .capitalize),在 emit 時(shí)處理。
  • 視圖更新:模擬組件內(nèi)部的 ,綁定 value 并監(jiān)聽 input 事件。

4. 統(tǒng)一的 v-model 接口

創(chuàng)建一個(gè)統(tǒng)一的 vModel 函數(shù),根據(jù)上下文(表單元素或組件)調(diào)用不同的實(shí)現(xiàn)。

javascript

function vModel(element, reactiveRef, options = {}) {
  const { modifiers = {}, propName } = options;
  const isComponent = propName || element.tagName.toLowerCase() === 'custom-component';
  if (isComponent) {
    vModelComponent(element, reactiveRef, propName || 'modelValue', modifiers);
  } else {
    vModelForm(element, reactiveRef, modifiers);
  }
}
  • 選項(xiàng)modifiers 指定修飾符,propName 指定組件的綁定屬性。
  • 上下文判斷:根據(jù) propName 或元素標(biāo)簽判斷是表單元素還是組件。

5. 完整代碼與使用示例

javascript

// 定義 Dep 對(duì)象,用于依賴收集
const Dep = {
  target: null, // 當(dāng)前依賴的回調(diào)函數(shù)
};
// 模擬 Vue 3 的 ref
function ref(initialValue) {
  const deps = new Set();
  let _value = initialValue;
  return {
    get value() {
      // 依賴收集
      if (typeof Dep.target === 'function') {
        deps.add(Dep.target);
      }
      return _value;
    },
    set value(newValue) {
      if (_value !== newValue) {
        _value = newValue;
        // 通知依賴
        deps.forEach((dep) => dep());
      }
    },
  };
}
// 模擬 watchEffect,用于依賴收集
function watchEffect(callback) {
  Dep.target = callback;
  callback();
  Dep.target = null;
}
// 處理修飾符邏輯
function applyModifiers(value, modifiers) {
  let result = value;
  if (modifiers.number) {
    result = isNaN(Number(result)) ? result : Number(result);
  }
  if (modifiers.trim && typeof result === 'string') {
    result = result.trim();
  }
  return result;
}
// 表單元素的 v-model
function vModelForm(element, reactiveRef, modifiers = {}) {
  const tag = element.tagName.toLowerCase();
  const type = element.type;
  const eventName = modifiers.lazy
    ? 'change'
    : tag === 'select' || type === 'checkbox' || type === 'radio'
      ? 'change'
      : 'input';
  const updateView = () => {
    if (type === 'checkbox') {
      element.checked = reactiveRef.value;
    } else if (type === 'radio') {
      element.checked = reactiveRef.value === element.value;
    } else {
      element.value = reactiveRef.value;
    }
  };
  Dep.target = updateView;
  updateView();
  Dep.target = null;
  element.addEventListener(eventName, (event) => {
    let newValue;
    if (type === 'checkbox') {
      newValue = event.target.checked;
    } else if (type === 'radio') {
      if (event.target.checked) {
        newValue = event.target.value;
      } else {
        return;
      }
    } else {
      newValue = event.target.value;
    }
    newValue = applyModifiers(newValue, modifiers);
    reactiveRef.value = newValue;
  });
  reactiveRef.deps = reactiveRef.deps || new Set();
  reactiveRef.deps.add(updateView);
}
// 組件的 v-model
function vModelComponent(
  element,
  reactiveRef,
  propName = 'modelValue',
  modifiers = {},
) {
  const props = { [propName]: reactiveRef.value };
  const emit = (eventName, value) => {
    if (eventName === `update:${propName}`) {
      let newValue = value;
      if (modifiers.capitalize && typeof newValue === 'string') {
        newValue = newValue.charAt(0).toUpperCase() + newValue.slice(1);
      }
      reactiveRef.value = newValue;
    }
  };
  const updateView = () => {
    element.value = reactiveRef.value;
  };
  Dep.target = updateView;
  updateView();
  Dep.target = null;
  element.addEventListener('input', (event) => {
    emit(`update:${propName}`, event.target.value);
  });
  reactiveRef.deps = reactiveRef.deps || new Set();
  reactiveRef.deps.add(updateView);
}
// 統(tǒng)一 v-model 接口
function vModel(element, reactiveRef, options = {}) {
  const { modifiers = {}, propName } = options;
  const isComponent =
    propName || element.tagName.toLowerCase() === 'custom-component';
  if (isComponent) {
    vModelComponent(element, reactiveRef, propName || 'modelValue', modifiers);
  } else {
    vModelForm(element, reactiveRef, modifiers);
  }
}
// 使用示例
// 創(chuàng)建 DOM 元素
const inputText = document.createElement('input');
inputText.placeholder = '輸入文本';
const inputNumber = document.createElement('input');
inputNumber.type = 'number';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
const select = document.createElement('select');
select.innerHTML = `
  <option value="option1">選項(xiàng) 1</option>
  <option value="option2">選項(xiàng) 2</option>
 `;
const customComponent = document.createElement('input'); // 模擬組件
customComponent.placeholder = '自定義組件輸入';
// 添加到頁(yè)面
document.body.appendChild(inputText);
document.body.appendChild(document.createElement('br'));
document.body.appendChild(inputNumber);
document.body.appendChild(document.createElement('br'));
document.body.appendChild(checkbox);
document.body.appendChild(document.createElement('br'));
document.body.appendChild(select);
document.body.appendChild(document.createElement('br'));
document.body.appendChild(customComponent);
// 創(chuàng)建響應(yīng)式數(shù)據(jù)
const message = ref('Hello');
const number = ref(42);
const checked = ref(false);
const selected = ref('option1');
const componentName = ref('alice');
const componentAge = ref(25);
// 綁定 v-model
vModel(inputText, message, { modifiers: { trim: true } });
vModel(inputNumber, number, { modifiers: { number: true } });
vModel(checkbox, checked);
vModel(select, selected);
vModel(customComponent, componentName, {
  propName: 'name',
  modifiers: { capitalize: true },
});
// 模擬多重 v-model
const componentAgeInput = document.createElement('input');
componentAgeInput.type = 'number';
document.body.appendChild(document.createElement('br'));
document.body.appendChild(componentAgeInput);
vModel(componentAgeInput, componentAge, {
  propName: 'age',
  modifiers: { number: true },
});
// 打印數(shù)據(jù)變化
watchEffect(() => {
  console.log('message:', message.value);
  console.log('number:', number.value);
  console.log('checked:', checked.value);
  console.log('selected:', selected.value);
  console.log('componentName:', componentName.value);
  console.log('componentAge:', componentAge.value);
});
// 程序化更新數(shù)據(jù)
setTimeout(() => {
  message.value = 'Updated Text';
  componentName.value = 'bob';
}, 2000);

執(zhí)行流程圖

初始化:
  定義 Dep, ref, vModel 等
  創(chuàng)建 DOM 元素 -> 添加到頁(yè)面
  創(chuàng)建 ref 數(shù)據(jù) (message, number, ...)
  vModel 綁定:
    -> vModelForm 或 vModelComponent
       -> 確定事件 (input/change)
       -> 定義 updateView
       -> 初始更新 DOM (觸發(fā) getter, 收集 updateView)
       -> 綁定事件監(jiān)聽
       -> 添加 updateView 到 deps
  watchEffect:
    -> 打印初始值 (觸發(fā) getter, 收集 watchEffect)
  設(shè)置定時(shí)器
用戶交互 (例如輸入 " Hello Vue "):
  input 事件 -> 獲取值 " Hello Vue "
  applyModifiers (.trim) -> "Hello Vue"
  message.value = "Hello Vue" -> setter
    -> 更新 _value
    -> 通知 deps:
       -> updateView: inputText.value = "Hello Vue"
       -> watchEffect: 打印所有數(shù)據(jù)
程序化更新 (2秒后):
  message.value = 'Updated Text' -> setter
    -> 更新 _value
    -> 通知 deps:
       -> updateView: inputText.value = 'Updated Text'
       -> watchEffect: 打印數(shù)據(jù)
  componentName.value = 'bob' -> setter
    -> 更新 _value
    -> 通知 deps:
       -> updateView: customComponent.value = 'bob'
       -> watchEffect: 打印數(shù)據(jù)

到此這篇關(guān)于vue3中v-model的原理示例的文章就介紹到這了,更多相關(guān)vue v-model原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解vue-cli快速構(gòu)建項(xiàng)目以及引入bootstrap、jq

    詳解vue-cli快速構(gòu)建項(xiàng)目以及引入bootstrap、jq

    本篇文章主要介紹了vue-cli快速構(gòu)建項(xiàng)目以及引入bootstrap、jq,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • 關(guān)于element-ui中el-form自定義驗(yàn)證(調(diào)用后端接口)

    關(guān)于element-ui中el-form自定義驗(yàn)證(調(diào)用后端接口)

    這篇文章主要介紹了關(guān)于element-ui中el-form自定義驗(yàn)證(調(diào)用后端接口),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Vuex中actions優(yōu)雅處理接口請(qǐng)求的方法

    Vuex中actions優(yōu)雅處理接口請(qǐng)求的方法

    在項(xiàng)目開發(fā)中,如果使用到了 vuex,通常我會(huì)將所有的接口請(qǐng)求單獨(dú)用一個(gè)文件管理,這篇文章主要介紹了Vuex中actions如何優(yōu)雅處理接口請(qǐng)求,業(yè)務(wù)邏輯寫在 actions 中,本文給大家分享完整流程需要的朋友可以參考下
    2022-11-11
  • vue將時(shí)間戳轉(zhuǎn)換成自定義時(shí)間格式的方法

    vue將時(shí)間戳轉(zhuǎn)換成自定義時(shí)間格式的方法

    下面小編就為大家分享一篇vue將時(shí)間戳轉(zhuǎn)換成自定義時(shí)間格式的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • 手把手教你使用Vue實(shí)現(xiàn)彈窗效果

    手把手教你使用Vue實(shí)現(xiàn)彈窗效果

    在vue中彈窗是常用的組件之一,可以用來(lái)展示警告、成功提示和錯(cuò)誤信息等內(nèi)容,這篇文章主要給大家介紹了關(guān)于如何使用Vue實(shí)現(xiàn)彈窗效果的相關(guān)資料,需要的朋友可以參考下
    2024-02-02
  • vue element-ui之怎么封裝一個(gè)自己的組件的詳解

    vue element-ui之怎么封裝一個(gè)自己的組件的詳解

    這篇文章主要介紹了vue element-ui之怎么封裝一個(gè)自己的組件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • 在Vue+Ts+Vite項(xiàng)目中配置別名指向不同的目錄并引用的案例詳解

    在Vue+Ts+Vite項(xiàng)目中配置別名指向不同的目錄并引用的案例詳解

    這篇文章主要介紹了在Vue+Ts+Vite項(xiàng)目中配置別名指向不同的目錄并引用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • Vue 組件組織結(jié)構(gòu)及組件注冊(cè)詳情

    Vue 組件組織結(jié)構(gòu)及組件注冊(cè)詳情

    這篇文章主要介紹的是Vue 組件組織結(jié)構(gòu)及組件注冊(cè),為了能在模板中使用,這些組件必須先注冊(cè)以便 Vue 能夠識(shí)別。這里有兩種組件的注冊(cè)類型:全局注冊(cè)和局部注冊(cè)。至此,我們的組件都只是通過(guò) Vue.component 全局注冊(cè)的,文章學(xué)詳細(xì)內(nèi)容,需要的朋友可以參考一下
    2021-10-10
  • vue中如何去掉input前后的空格

    vue中如何去掉input前后的空格

    這篇文章主要介紹了vue中如何去掉input前后的空格問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Vue2和Vue3中常用組件通信用法分享

    Vue2和Vue3中常用組件通信用法分享

    這篇文章主要為大家整理了Vue3的8種和Vue2的12種組件通信的使用方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Vue有一定的幫助,值得收藏
    2023-04-04

最新評(píng)論