vue2響應式原理+模擬實現v-model

效果

簡述原理

配置對象傳入vue實例

模板解析,遍歷出所有文本節點,利用正則替換插值表達式為真實數據

data數據代理給vue實例,以后通過this.xxx訪問

給每個dom節點增加觀察者實例,由觀察者群組管理,內部每一個鍵值含有多個對不同dom的觀察者

data數據劫持,給data的每個屬性增加get和set函數,當值改變時觸發觀察者的update方法,更新所有與當前屬性值相關的dom元素

劫持數據,說的挺好聽的,就是加工數據嘛,多了set變化觸發了模板重新渲染,該渲染方式使用觀察者模式,獲取觀察者收集的各個dom的所有屬性 div,觀察的屬性,div的屬性textContent,同時根據最新值渲染模板

div.textContent=vm[key]

html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><!-- <script src="./vue.js"></script> -->
</head><body><div id="app">{{ name }} {{age}}<h1>{{age}}</h1><button @click="cli">按鈕</button><input type="text" v-model="name"></div>
</body>
<script src="./vue.js">
</script>
<script>new Vue({el: '#app',data: {name: 'Zwwwww',age: 18,},methods: {cli() {console.log(this);console.log(this.age);}},})</script></html>

js代碼

class Vue {constructor(options) {// 獲取配置對象的節點,存放在vm$el身上this.$el = document.querySelector(options.el)// console.log(this.$el)// 將配置對象的data對象代理到$datathis.$data = options.data// 獲取配置對象的method值,// vue實例監聽,當觸發了方法執行對應函數this.$methods = options.methods// 代理數據,后續通過this調用data對象的值this.$allWatcher = {}this.proxyData()// 劫持數據,為其增加觀察者監視數據變化引起視圖渲染this.observe()// 收集所有觀察者,用對象的屬性存放this.compile(this.$el)}// 數據代理到vue實例身上,后續this調用方法和data值proxyData() {// 遍歷$data身上所有keyfor (let key in this.$data) {// 數據代理給vue實例,thisObject.defineProperty(this, key, {// 使用get和set后續觸發獲取值和設置值做額外操作get() {// 返回當前data對應的key屬性值return this.$data[key]},set(value) {// 設置新值給當前屬性this.$data[key] = value},})}}// js數據替換{{name}},模板解析compile(node) {// 遍歷根節點下的所有節點node.childNodes.forEach((item, index) => {//遞歸元素節點,//如果還沒到文本節點,也就是說元素節點內還有元素節點//則繼續遞歸,直到元素節點沒有子節點//第二種可能,如果為元素元素節點,判斷是否有@click屬性,并獲取值//該值為綁定的methods方法if (item.nodeType === 1) {if (item.childNodes.length > 0) {this.compile(item)}if (item.hasAttribute('@click')) {let domKey = item.getAttribute('@click')// console.log('我是dom標簽的key', domKey)// 設置監聽器,如果被點擊了,觸發配置對象中的method函數item.addEventListener('click', () => {// 通過模板獲取的屬性值方法命,調用函數// 由于$methods只是引用地址,this指向還是原來的methods// 我們這里使用call來綁定他的上下文this,也就是綁定他的調用者// 在html部分我們就可以使用this.$data.age來獲取vue實例上的數據// 如果我們想直接this.age 就需要將data代理到vue實例身上this.$methods[domKey.trim()].call(this)})}if (item.hasAttribute('v-model')) {let vmodelKey = item.getAttribute('v-model').trim()// console.log('我是v-model的key', vmodelKey)// 設置監聽器,如果被點擊了,觸發配置對象中的method函數// 先單向給input框設置值item.value = this.$data[vmodelKey]item.addEventListener('input', () => {console.log('用戶正在輸入')// 每次輸入時將輸入框的值重新賦給data對象屬性值,完成雙向綁定this.$data[vmodelKey] = item.valueconsole.log(this.$data[vmodelKey])// 數據更新的同時重新解析模板// 這里使用觀察者類觀察數據變化所作出的響應})}}// 判斷是否為文本節點,nodeType == 3// console.log(item.nodeType)// 如果是文本節點,進行數據替換// 如果不是文本節點,為元素節點則往里遞歸遍歷文本節點if (item.nodeType === 3) {// 定義正則,替換{{xxx}}形式的字串為data下的屬性值let reg = /\{\{(.*?)\}\}/g// 獲取原本標簽里的值,后續進行替換let text = item.textContent// console.log(text)item.textContent = text.replace(reg, (match, dataKey) => {// 先將dataKey去空格處理dataKey = dataKey.trim()// match為匹配到的整體,datakey為捕獲到的子內容(.*?)//我們這里只需獲取dataKey對應的值并塞入即可// console.log(match, dataKey)// 返回值作為替換內容 去除dataKey的前后空格// 增加觀察者,傳vue實例對象,data屬性,item標簽,標簽屬性// 相當于給每個文本節點都添加了一個觀察者// 將所有觀察者收集到vue實例上,在數據發生變化時調用觀察者的update方法let watcher = new Watcher(this, dataKey, item, 'textContent')// 先進行判斷觀察者群組里是否有該節點的觀察者// 如果有,就push添加,因為一個dataKey可能有多個模板使用// 舉個例子,name屬性可能在div1里使用也在div2里使用// 也就是將多個文本節點與同個datakey綁定if (this.$allWatcher[dataKey]) {this.$allWatcher[dataKey].push(watcher)}// 如果沒有該屬性的觀察者存在,則新建空數組,push該觀察者進入else {this.$allWatcher[dataKey] = []this.$allWatcher[dataKey].push(watcher)}return this.$data[dataKey]})}})}observe() {console.log('開始劫持')// 遍歷所有的key,對其data數據劫持,值增加響應式功能for (let key in this.$data) {// 先獲取value,否則數據重新定義后值會丟失// 此處的value變量不會隨著observe方法的結束而銷毀// 與內部匿名函數get和set作為閉包永遠綁定在一起// 同時value值是對$data的一個引用,修改value值會引起$data變化let value = this.$data[key]// 保存一份vue的引用_this=this,// 防止后續在組件外部,也就是input輸入框// 此時觸發的set為一個閉包環境,上下文變成由defineproper定義的this.$data數據對象// 此時找不到vue實例作為上下文,對key和其他數據的引用也會失效let _this = thisObject.defineProperty(this.$data, key, {get() {console.log('有人要獲取劫持數據值', value)// 返回上面存儲的value值// 由于是響應式的,只有當觀察到數據變化時所以才接觸數據// 其value值作用域也作用在劫持過程中return value},set(newValue) {console.log('劫持到數據,修改值為', newValue)console.log('劫持前的數據為', value)value = newValue// 更新值的同時進行模板更新// 由于觀察者隊列含有觀察者來觀察不同屬性管理的若干個模板// 調用該屬性值下所有模板觀察者即可,// 只要屬性值變化,該屬性值下的所有觀察者重新渲染模板console.log(_this.$allWatcher)console.log(_this.$allWatcher[key])_this.$allWatcher[key].forEach((watcher, index) => {watcher.update()})},})}console.log('劫持成功')}
}class Watcher {constructor(vm, key, node, attr) {this.vm = vmthis.key = keythis.node = nodethis.attr = attr}//  item.textContent = this.$data[dataKey.trim()]update() {console.log('開始渲染')// 將原始dom標簽內容值替換為 data里的屬性值this.node[this.attr] = this.vm[this.key]}
}

代碼參考

VUE雙向綁定原理分析~實現視圖和數據的雙向綁定~_嗶哩嗶哩_bilibili

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/41354.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/41354.shtml
英文地址,請注明出處:http://en.pswp.cn/web/41354.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

sqlite 數據庫 介紹

文章目錄 前言一、什么是 SQLite &#xff1f;二、語法三、SQLite 場景四、磁盤文件 前言 下載 目前已經出到了&#xff0c; Version 3.46.0 SQLite&#xff0c;是一款輕型的數據庫&#xff0c;是遵守ACID的關系型數據庫管理系統&#xff0c;它包含在一個相對小的C庫中。它是…

VMware虛擬機配置橋接網絡

轉載&#xff1a;虛擬機橋接網絡配置 一、VMware三種網絡連接方式 VMware提供了三種網絡連接方式&#xff0c;VMnet0, VMnet1, Vmnet8&#xff0c;分別代表橋接&#xff0c;Host-only及NAT模式。在VMware的編輯-虛擬網絡編輯器可看到對應三種連接方式的設置&#xff08;如下圖…

美好生活的 100 條建議

簡介 一些簡潔明了的人生建議&#xff0c;易于理解&#xff0c;并且能夠為日常生活中的各個方面提供實用的指導。 財富 (Possessions) 1. If you want to find out about people’s opinions on a product, google \reddit. You’ll get real people arguing, as compared t…

SpringBoot2.2.6使用spring-boot-validation讀取不到自定義配置文件中的屬性

SpringBoot2.2.6沒有做message.properties文件中屬性的自動讀取配置。解決方法有兩種&#xff1a; 1. 升級springboot版本到2.6.x以上 2. 在現有springboot版本的基礎上添加以下自定義配置&#xff1a; Configuration public class RequestParamValidationConfig implements…

學習筆記——交通安全分析12

目錄 前言 當天學習筆記整理 4信控交叉口交通安全分析 結束語 前言 #隨著上一輪SPSS學習完成之后&#xff0c;本人又開始了新教材《交通安全分析》的學習 #整理過程不易&#xff0c;喜歡UP就點個免費的關注趴 #本期內容接上一期11筆記 當天學習筆記整理 4信控交叉口交…

ORA-03115 ORA-06594--空間不足 rman 磁帶壓縮備份 控制文件恢復后備份信息丟失

---用舊的控制文件恢復后 這個控制文件本身的備份信息不在此還原出的控制文件中。所以這個備份的控制文件就刪不掉&#xff0c;看不到。 但是只要設置正確的dbid&#xff0c;還是可以用恢復這個控制文件的 正常未恢復過controlfile的話&#xff0c;這個控制文件備份信息在現有…

Square Root SAM論文原理

文章目錄 Square Root SAM論文原理核心原理SLAM問題的3種表示貝葉斯網絡因子圖&#xff08;Factor graph&#xff09;馬爾科夫隨機場(Markov Random Field, MRF) SLAM最小二乘問題&線性化因式分解 factorization矩陣與圖(Matrices ? Graphs)因式分解&變量消元(Factori…

Kafka系列之Kafka知識超強總結

一、Kafka簡介 Kafka是什么 Kafka是一種高吞吐量的分布式發布訂閱消息系統&#xff08;消息引擎系統&#xff09;&#xff0c;它可以處理消費者在網站中的所有動作流數據。 這種動作&#xff08;網頁瀏覽&#xff0c; 搜索和其他用戶的行動&#xff09;是在現代網絡上的許多社…

14-22 劍和遠方2 - 深度神經網絡中的學習機制

概論 在第一部分中&#xff0c;我們深入探討了人工智能的興衰簡史以及推動人工智能發展的努力。我們研究了一個簡單的感知器&#xff0c;以了解其組件以及簡單的 ANN 如何處理數據和權重層。在簡單的 ANN 中&#xff0c;不會對數據執行特定操作。ANN 中的激活函數是一個線性函…

Surface splatting (2D Gaussian splatting)代碼分析

源碼地址 colab.research.google.com/drive/1qoclD7HJ3-o0O1R8cvV3PxLhoDCMsH8W 核心代碼 surface_splatting def surface_splatting(means3D, scales, quats, colors, opacities, intrins, viewmat, projmat):# Rasterization setupprojmat torch.zeros(4,4).cuda()projm…

flask使用定時任務flask_apscheduler(APScheduler)

Flask-APScheduler描述: Flask-APScheduler 是一個 Flask 擴展&#xff0c;增加了對 APScheduler 的支持。 APScheduler 有三個內置的調度系統可供您使用&#xff1a; Cron 式調度&#xff08;可選開始/結束時間&#xff09; 基于間隔的執行&#xff08;以偶數間隔運行作業…

c#中的超時終止

在C#中&#xff0c;可以使用CancellationToken和Task的超時機制來實現調用方法時的超時終止。 一 用Task.Delay(int)模擬耗時操作 static async Task Main(string[] args){using (var cts new CancellationTokenSource(1 * 1000)){await doSomething(cts.Token);}Console.Wr…

移動校園(7)ii:uniapp響應攔截器處理token,以及微信小程序報錯當前頁面正在處于跳轉狀態,請稍后再進行跳轉....

依據昨天的寫完&#xff0c;在token過期之后&#xff0c;再次調用接口&#xff0c;會觸發后端攔截&#xff0c;扔進全局錯誤處理中間件 前端說明提示都沒有&#xff0c;只有一個這個&#xff0c;現在優化一下&#xff0c;再寫一個類似全局后置守衛&#xff0c;當狀態碼是401的時…

RAID 冗余磁盤陣列

RAID也是Linux操作系統中管理磁盤的一種方式。 只有Linux操作系統才支持LVM的磁盤管理方式。 而RAID是一種通用的管理磁盤的技術&#xff0c;使用于多種操作系統。 優勢&#xff1a;提升數據的讀寫速度&#xff0c;提升數據的可靠性。具體實現哪什么功能&#xff0c;要看你所…

RGB樹-美團2023筆試(codefun2000)

題目鏈接 RGB樹-美團2023筆試(codefun2000) 題目內容 塔子哥是一位著名的冒險家&#xff0c;他經常在各種森林里探險。今天&#xff0c;他來到了道成林&#xff0c;這是一片美麗而神秘的森林。在探險途中&#xff0c;他遇到了一棵 n 個節點的樹&#xff0c;樹上每個節點都被涂…

LVGL移植與VS模擬器使用

一、移植文件介紹 二、移植部分 第一步&#xff1a;創建LVGL文件夾 第二步&#xff1a; 構造LVGL文件夾&#xff1a;LVGL - GUI - lvgl - 第三步&#xff1a;添加文件 3.1 從examples中添加2個.c文件 3.2 從src中添加文件 draw文件 extra文件 第四步&#xff1a; 三、Ke…

Linux系統安裝軟件包的方法rpm和yum詳解

起因&#xff1a; 本篇文章是記錄學習Centos7的歷程 關于rpm 常見命令 1&#xff09;查看已經安裝的軟件包 rpm -q 軟件包名 2&#xff09;查看文件的相關信息 rpm -qi 軟件包名 3&#xff09;查看軟件包的依賴關系 就是說要想安裝這個軟件包&#xff0c;就必須把一些前…

三級_網絡技術_04_中小型網絡系統總體規劃與設計

1.下列關于路由器技術特征的描述中&#xff0c;正確的是()。 吞吐量是指路由器的路由表容量 背板能力決定了路由器的吞吐量 語音、視頻業務對延時抖動要求較低 突發處理能力是以最小幀間隔值來衡量的 2.下列關于路由器技術特征的描述中&#xff0c;正確的是()。 路由器的…

springboot公寓租賃系統-計算機畢業設計源碼03822

摘要 1 緒論 1.1 研究背景與意義 1.2選題背景 1.3論文結構與章節安排 2 公寓租賃系統系統分析 2.1 可行性分析 2.1.1 技術可行性分析 2.1.2 經濟可行性分析 2.1.3 法律可行性分析 2.2 系統功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系統用例分析 2.4 系…

韋東山嵌入式linux系列-第一個實驗

1 前言 筆者使用的是韋東山STM32MP157 Pro的板子&#xff0c;環境搭建部分按照說明文檔配置完成。配置橋接網卡實現板子、windows、ubuntu的通信&#xff0c;也在開發板掛載 Ubuntu 的NFS目錄 &#xff0c;這里就不再贅述了。 板子: 192.168.5.9 windows: 192.168.5.10 ubunt…