最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue3實現(xiàn)v-model原理詳解

 更新時間:2019年10月09日 09:45:38   作者:愛學(xué)習(xí)的老A@羅學(xué)  
這篇文章主要介紹了vue3實現(xiàn)v-model原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

vue3 源碼正式放出來了,想必大家也都開始爭先恐后的學(xué)習(xí) vue3 的知識了。由于 vue3 已經(jīng)不再支持 v-model 了,而使用 .sync 來代替,但是為了這篇文章可以幫助大家快速了解 vue 的雙向綁定實現(xiàn)原理,部分使用了 vue2.x v-model 的實現(xiàn)原理

proxy 的基礎(chǔ)知識,相信大家已經(jīng)都很了解了,讓我們一起來回顧一下吧

proxy 是對一個對象的代理,并返回一個已代理的對象,已代理的對象如果發(fā)生任何 set 跟 get 的方法都可以被捕獲到,我們寫一個簡單的 :chestnut:

const target = {
 a: 1
}
const handers = {
 get() {
  // 當(dāng)對 observed.a 進行取值時會觸發(fā)
 },
 set() {
  // 當(dāng)對 observed.a 進行賦值時會觸發(fā)
 },
 // 還有一些額外的參數(shù)如 has 等,這里用不到,就不多說了
 ....
}
const observed = new Proxy(target, handers)

這樣我們就可以對 target 對象設(shè)置了一層代理,當(dāng)我們對 target 進行取賦值操作的時候就可以接可以截獲到它的行為了,但是如果你以為就只有這么簡單你就錯了。

我們把 target 改寫成多層嵌套

const target = {
 a: {
  b: 1
 }
}

...

const observed = new Proxy(target, handers)

我們再獲取 observed.a.b = 2 的時候,get 方法取到的是 a 的值 { b: 1 }, 而 set 并不會觸發(fā)。這也說明了 proxy 只能代理一層對象,不能深層代理!

那么我們需要監(jiān)聽到嵌套的對象怎么辦?

其實這個也不難,就是在 get 的時候判斷一下得到的值是不是對象,如果是對象的話就 在對它代理一層,直到最后一層,全部代理完為止,這里就需要一個遞歸函數(shù)

const target = {
 a: {
  b: 1
 }
}

function reactive(data: any) {
 const handers = {
  get(target, key, receiver) {
   const res = Reflect.get(target, key, receiver);
   if (isObject(res)) {
    data[key] = reactive(res);
   }
   return target[key];
  }
 }
 const observed = new Proxy(target, handers)
}

這樣我們就可以對目標(biāo)函數(shù)內(nèi)部的所有屬性進行深層監(jiān)聽了,但是這樣還是不夠,因為我們每次取值的時候都會設(shè)置代理這樣會導(dǎo)致代碼無限循環(huán)->死循環(huán),所以我們需要做一層判斷,如果已經(jīng)設(shè)置了代理的或這已經(jīng)是代理的對象就不需要在此設(shè)置代理了。又因為我們要儲存對象的映射,所以需要使用map函數(shù)。下面是reactive完整的代碼。

const rawToReactive: WeakMap<any, any> = new WeakMap();
const reactiveToRaw: WeakMap<any, any> = new WeakMap();

function reactive(data: any) {
 // 已經(jīng)有代理
 let observed = rawToReactive.get(data);
 if (observed !== void 0) {
  return observed;
 }
 // 這個數(shù)據(jù)已經(jīng)是代理
 if (reactiveToRaw.has(data)) {
  return data;
 }
 const handler = {
  get: function(target: any, key: string, receiver: any) {
   const res = Reflect.get(target, key, receiver);
   if (isObject(res)) {
    data[key] = data[key] = reactive(res);
   }
   return target[key];
  },
  set: function(target: any, key: string, value: any) {
   // 將新值賦值
   target[key] = value;
   // 通知所有訂閱者觸發(fā)更新
   trigger(target);
   // 嚴(yán)格模式下需要設(shè)置返回值,否則會報錯
   return value;
  }
 };
 // 返回代理監(jiān)聽對象
 observed = new Proxy(data, handler as any);
 rawToReactive.set(data, observed);
 reactiveToRaw.set(observed, data);

 return observed;
}

定義watcher 用來作為 compile 跟 reactive 的橋梁, 跟 vue3 的實現(xiàn)可能不一樣

// 收集watcher依賴
const Dep: Dep = {
 deps: [],
 add(watcher: Watcher) {
  this.deps.push(watcher);
 }
};

// observer跟compile的橋梁,在編譯時添加watcher,在數(shù)據(jù)更新時觸發(fā)update更新視圖
function _watcher(node: any, attr: string, data: any, key: string): Watcher {
 return {
  node,
  attr,
  data,
  key,
  update() {
   // 逐層取值
   const mutationKeys = this.key.split('.');
   if (mutationKeys.length > 1) {
    let d: any = null;
    mutationKeys.forEach(key => (d = this.data[key] || (d && d[key])));
    this.node[this.attr] = d;
    return;
   }
   this.node[this.attr] = this.data[this.key];
  }
 };
}

接下來是編譯模板

這里只是模擬編譯,真正的編譯不是這樣的

獲取到模板上的 v-model 、 v-bind 屬性,獲取到綁定的屬性。當(dāng)數(shù)據(jù)發(fā)生變化時,更新視圖(這里會在trigger進行觸發(fā)),當(dāng)視圖改變數(shù)據(jù)時修改數(shù)據(jù)(為了簡單,通過eval函數(shù)實現(xiàn)),具體代碼如下

// 編譯模板
function _compile(nodes: any, $data: any) {
 [...nodes].forEach((e, index) => {
  const theNode = nodes[index];
  // 獲取到 input標(biāo)簽下的 v-model 屬性,并添加watcher
  if (theNode.tagName === 'INPUT' && theNode.hasAttribute('v-model')) {
   const key = theNode.getAttribute('v-model');
   Dep.add(_watcher(theNode, 'value', $data, key));
   // 監(jiān)聽input事件
   theNode.addEventListener('input', () => {
    const mutationKeys = key.split('.');
    if (mutationKeys.length > 1) {
     eval(`$data.${key}='${theNode.value}'`);
     return;
    }
    $data[key] = theNode.value;
   });
  }
  // 獲取 v-bind 屬性,并添加watcher
  if (theNode.hasAttribute('v-bind')) {
   const key = theNode.getAttribute('v-bind');
   Dep.add(_watcher(theNode, 'innerHTML', $data, key));
  }
 });
 trigger($data);
}

trigger 對依賴進行觸發(fā)

function trigger(target: any, key?: string | symbol) {
 Dep.deps.forEach((e: Watcher) => {
  e.update();
 });
}

使用效果

廢話不多說。直接上代碼!

假設(shè)我們有一個模板是這樣的,接下來我們在這個模板的 id="my-app" 元素內(nèi)實現(xiàn)雙向綁定

<div id="my-app">
 <h1 v-bind="a"></h1>
 <input v-model="a" type="text">
</div>

vue3 中 new Vue 已經(jīng)被 createApp 所代替,reactive 是反應(yīng)原理,可以抽出來單獨使用,vue3 外漏了所有內(nèi)部的 api,都可以在外部使用

const { createApp, reactive } = require('./vue.ts').default;
const App = {
 setup() {
  const react = reactive({
   a: {
    b: {
     c: {
      d: {
       e: 111
      }
     }
    }
   }
  });
  // 測試異步反應(yīng)
  setTimeout(() => {
   react.a.b.c.d.e = 222;
  }, 100);
  return react;
 }
};
createApp().mount(App, '#my-app');

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue實現(xiàn)項目部署到非根目錄及解決刷新頁面時找不到資源

    Vue實現(xiàn)項目部署到非根目錄及解決刷新頁面時找不到資源

    這篇文章主要介紹了Vue實現(xiàn)項目部署到非根目錄及解決刷新頁面時找不到資源問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue+vuecli+webpack中使用mockjs模擬后端數(shù)據(jù)的示例

    vue+vuecli+webpack中使用mockjs模擬后端數(shù)據(jù)的示例

    本篇文章主要介紹了vue+vuecli+webpack中使用mockjs模擬后端數(shù)據(jù)的示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-10-10
  • vue3中Teleport和Suspense的具體使用

    vue3中Teleport和Suspense的具體使用

    本文主要介紹了vue3中Teleport和Suspense的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • vue:內(nèi)存泄露詳解

    vue:內(nèi)存泄露詳解

    這篇文章主要介紹了一個Vue的內(nèi)存泄露詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-10-10
  • vue3中的hooks總結(jié)

    vue3中的hooks總結(jié)

    這篇文章主要介紹了vue3中的hooks總結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Vue解決ajax跨域的問題

    Vue解決ajax跨域的問題

    這篇文章主要介紹了Vue解決ajax跨域的問題,跨域請求:是指協(xié)議名、主機名、端口號三者有任何一個不一樣,而且跨域請求是請求發(fā)出去了,服務(wù)器接收并返回了結(jié)果,只是瀏覽器沒有接收響應(yīng)結(jié)果。感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • vue3.0?router路由跳轉(zhuǎn)傳參問題(router.push)

    vue3.0?router路由跳轉(zhuǎn)傳參問題(router.push)

    這篇文章主要介紹了vue3.0?router路由跳轉(zhuǎn)傳參問題(router.push),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • vue項目初始化到登錄login頁面的示例

    vue項目初始化到登錄login頁面的示例

    今天小編就為大家分享一篇vue項目初始化到登錄login頁面的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-10-10
  • vue中格式化時間過濾器代碼實例

    vue中格式化時間過濾器代碼實例

    這篇文章主要介紹了vue格式化時間過濾器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • vue element Cascader級聯(lián)選擇器解決最后一級顯示空白問題

    vue element Cascader級聯(lián)選擇器解決最后一級顯示空白問題

    這篇文章主要介紹了vue element Cascader級聯(lián)選擇器解決最后一級顯示空白問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10

最新評論

吉林市| 罗江县| 西充县| 顺昌县| 大荔县| 于都县| 通城县| 柏乡县| 文安县| 志丹县| 梁河县| 正宁县| 如皋市| 长海县| 柞水县| 永胜县| 桂平市| 沅江市| 晋城| 故城县| 黄石市| 泗洪县| 巧家县| 通江县| 灵寿县| 临泽县| 大安市| 吉木萨尔县| 佛冈县| 长治市| 洪湖市| 金塔县| 金平| 宜丰县| 皋兰县| 铁岭市| 内黄县| 东安县| 志丹县| 板桥市| 溧水县|