日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

vue的响应式原理

發布時間:2025/3/21 vue 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vue的响应式原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Vue 的響應式是通過 Object.defineProperty 對數據進行劫持,并結合發布訂閱者模式實現。 Vue 利用 Object.defineProperty 創建一個 observe 來劫持監聽所有的屬性,把這些屬性全部轉為 getter 和 setter。Vue 中每個組件實例都會對應一個 watcher 實例,它會在組件渲染的過程中把使用過的數據屬性通過 getter 收集為依賴。之后當依賴項的 setter 觸發時,會通知 watcher,從而使它關聯的組件重新渲染。

一、reduce

1.數值的累加

作用:將****前一項**后一項****的值進行運算,返回累積的結果

格式:數組.reduce(function(prev,next){…})

其中,prev表示前一項,next表示后一項。

運算規則:

默認情況下,會把數組的第一個元素作為prev的初始值。

每循環一次,把累積的結果賦給prev,next就變為下一個數組元素

var arr3 = [10,22,23,25,50];const total = arr3.reduce(function(pre,next){console.log(pre+"----"+next);return pre+next;})console.log(total);

實際上,reduce方法還有第二個參數,****如果傳遞了第二個參數,就作為prev的初始值****。同時next就是數組的第一個元素。

<script>const total = arr3.reduce(function(pre,next){console.log(pre+"----"+next);return pre+next;},100)console.log(total);</script>

2.鏈式獲取對象的值

const person={name:"尼古拉斯趙四",info:{address:{location:"東北鐵嶺",work:"二人轉"}}}const arrs=["info","address","location"];const result=arrs.reduce((newobj,k)=>{console.log(newobj)return newobj[k]},person)console.log(result); //如果不是一個數組而是一個字符串呢 const str="info.address.location"; //console.log(str.split("."))const result2 = str.split(".").reduce((newobj,k)=>{return newobj[k]; },person) console.log(result2);

二、Object.defineProperty

Object.defineProperty()的作用就是直接在一個對象上定義一個新屬性,或者修改一個已經存在的屬性

Object.defineProperty(obj, prop, desc)
  • obj 需要定義屬性的當前對象
  • prop 當前需要定義的屬性名
  • desc 屬性描述符
  • 一般通過為對象的屬性賦值的情況下,對象的屬性可以修改也可以刪除,但是通過Object.defineProperty()定義屬性,通過描述符的設置可以進行更精準的控制對象屬性。

    屬性描述符

    通過Object.defineProperty()為對象定義屬性,有兩種形式,且不能混合使用,分別為數據描述符,存取描述符,下面分別描述下兩者的區別:

    數據描述符 --特有的兩個屬性(value,writable)
    let Person = {} Object.defineProperty(Person, 'name', {value: 'Lucky',writable: true // 是否可以改變 })
    存取描述符 --是由一對 getter、setter 函數功能來描述的屬性

    get:一個給屬性提供getter的方法,如果沒有getter則為undefined。該方法返回值被用作屬性值。默認為undefined。
    set:一個給屬性提供setter的方法,如果沒有setter則為undefined。該方法將接受唯一參數,并將該參數的新值分配給該屬性。默認值為undefined。

    let Person = {} let temp = null Object.defineProperty(Person, 'name', {get: function () {return temp},set: function (val) {temp = val} })
    數據描述符和存取描述均具有以下描述符
  • configrable 描述屬性是否配置,以及可否刪除
  • enumerable 描述屬性是否會出現在for in 或者 Object.keys()的遍歷中
  • **注意:**configurable: false 時,不能刪除當前屬性,且不能重新配置當前屬性的描述符(但是可以把writable的狀態由true改為false,但是無法由false改為true),但是在writable: true的情況下,可以改變value的值。

    其他屬性:Object.seal()、Object.freeze()不再說。

    configurable: true時,可以刪除當前屬性,可以配置當前屬性所有描述符。

    var fun={name:"kelly",age:'123'}var funage='哈哈';Object.defineProperty(fun,'address',{get(){return funage;},set(val){console.log('觸發fun')funage=val; }})// fun.name="張三";// console.log(fun);// fun.age=12;// console.log(fun.age)fun.address="中山西路"console.log(fun.address)

    三、發布-訂閱者模式

    1.vue響應原理:

    vue.js采用數據劫持結合發布-訂閱者模式,通過Object.defineProperty()來劫持data中各個屬性的setter、getter,在數據變動時,發布消息給訂閱者,觸發響應的監聽回調。

    (setter和getter是對象的存儲器屬性,是一個函數(屬性),用來獲取和設置值)

    2、發布-訂閱者模式的作用:

    處理一對多的場景,應用于不同情況下的不同函數調用

    優點:低耦合性,易于代碼維護;

    缺點:若訂閱的消息未發生,需消耗一定的時間和內存。

    發布訂閱者模式:

    其定義對象間一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都將得到通知

    舉個栗子

    這里面以微信公眾號為例:

    • 假如用戶大腳訂閱了 胡吃海喝這個公眾號,那么當公眾號胡吃海喝推送消息的時候,用戶大腳就會收到相關的推送,點開可以查看推送的消息內容。
    • 但是公眾號胡吃海喝并不關心訂閱的它的是男人、女人還是未成年,它只負責發布自己的主體,只要是訂閱公眾號的用戶均會收到該消息。
    • 作為用戶大腳,不需要時刻打開手機查看公眾號胡吃海喝是否有推動消息,因為在公眾號推送消息的那一刻,大腳就會收到相關推送。
    • 當然了,用戶大腳如果不想繼續關注公眾號胡吃海喝,那么可以取消關注,取關以后,公眾號胡吃海喝再推送消息,大腳就無法收到了。

    還有一個生活中的栗子,就是買房子的情景。

    vue的發布訂閱模式可以用下圖簡單描述:


    接下來用代碼實現簡單的發布訂閱者

    //收集依賴/收集訂閱class Dep{constructor(){//這個subs數組,用來存放所有訂閱者的信息this.subs=[]}//向subs數組中,添加訂閱者的信息addSubs(watcher){this.subs.push(watcher)}//發布通知的方法notify(){this.subs.forEach((watcher)=>watcher.update()) }}//訂閱者的類class Watcher{constructor(cb) {this.cb=cb;}//觸發回調的方法update(){this.cb();}}const w1=new Watcher(()=>{console.log("我是第一個訂閱者");})const w2=new Watcher(()=>{console.log("我是第二個訂閱者!")})const d1=new Dep();d1.addSubs(w1);d1.addSubs(w2);//只要我們為vue中的數據重新賦值了,這個賦值的操作,會被vue監聽到//然后vue把數據的變化,通知到每個訂閱者!!//接下來,訂閱者(DOM元素)要根據最新的數據,更新自己的內容//1.誰是訂閱者 2.為什么要訂閱d1.notify();

    四、簡單實現雙向數據綁定

    1.創建一個js文件,vue.js

    class Vue {constructor(options) {this.$data = options.data// 調用數據劫持的方法Observe(this.$data)// 屬性代理Object.keys(this.$data).forEach((key) => {Object.defineProperty(this, key, {enumerable: true,configurable: true,get() {return this.$data[key]},set(newValue) {this.$data[key] = newValue},})})// 調用模板編譯的函數Compile(options.el, this)} }// 定義一個數據劫持的方法 function Observe(obj) {// 遞歸終止條件if (!obj || typeof obj !== 'object') returnconst dep = new Dep()// 通過 Object.keys(obj) 獲取到當前 obj 上的每個屬性Object.keys(obj).forEach((key) => {// 當前被循環的 key 所對應的屬性值let value = obj[key]// 把 value 這個子節點,進行遞歸Observe(value)// 需要為當前的 key 所對應的屬性,添加 getter 和 setterObject.defineProperty(obj, key, {enumerable: true,configurable: true,get() {// 只要執行了下面這一行,那么剛才 new 的 Watcher 實例,// 就被放到了 dep.subs 這個數組中了Dep.target && dep.addSub(Dep.target)return value},set(newVal) {value = newValObserve(value)// 通知每一個訂閱者更新自己的文本dep.notify()},})}) }// 對 HTML 結構進行模板編譯的方法 function Compile(el, vm) {// 獲取 el 對應的 DOM 元素vm.$el = document.querySelector(el)// 創建文檔碎片,提高 DOM 操作的性能const fragment = document.createDocumentFragment()while ((childNode = vm.$el.firstChild)) {fragment.appendChild(childNode)}// 進行模板編譯replace(fragment)vm.$el.appendChild(fragment)// 負責對 DOM 模板進行編譯的方法function replace(node) {// 定義匹配插值表達式的正則const regMustache = /\{\{\s*(\S+)\s*\}\}/// 證明當前的 node 節點是一個文本子節點,需要進行正則的替換if (node.nodeType === 3) {// 注意:文本子節點,也是一個 DOM 對象,如果要獲取文本子節點的字符串內容,需要調用 textContent 屬性獲取const text = node.textContent// 進行字符串的正則匹配與提取const execResult = regMustache.exec(text)console.log(execResult)if (execResult) {const value = execResult[1].split('.').reduce((newObj, k) => newObj[k], vm)node.textContent = text.replace(regMustache, value)// 在這個時候,創建 Watcher 類的實例new Watcher(vm, execResult[1], (newValue) => {node.textContent = text.replace(regMustache, newValue)})}// 終止遞歸的條件return}// 判斷當前的 node 節點是否為 input 輸入框if (node.nodeType === 1 && node.tagName.toUpperCase() === 'INPUT') {// 得到當前元素的所有屬性節點const attrs = Array.from(node.attributes)const findResult = attrs.find((x) => x.name === 'v-model')if (findResult) {// 獲取到當前 v-model 屬性的值 const expStr = findResult.valueconst value = expStr.split('.').reduce((newObj, k) => newObj[k], vm)node.value = value// 創建 Watcher 的實例new Watcher(vm, expStr, (newValue) => {node.value = newValue})// 監聽文本框的 input 輸入事件,拿到文本框最新的值,把最新的值,更新到 vm 上即可node.addEventListener('input', (e) => {const keyArr = expStr.split('.')const obj = keyArr.slice(0, keyArr.length - 1).reduce((newObj, k) => newObj[k], vm)const leafKey = keyArr[keyArr.length - 1]obj[leafKey] = e.target.value})}}// 證明不是文本節點,可能是一個DOM元素,需要進行遞歸處理node.childNodes.forEach((child) => replace(child))} }// 依賴收集的類/收集 watcher 訂閱者的類 class Dep {constructor() {// 所有的 watcher 都要存到這個數組中this.subs = []}// 向 subs 數組中,添加 watcher 的方法addSub(watcher) {this.subs.push(watcher)}// 負責通知每個 watcher 的方法notify() {this.subs.forEach((watcher) => watcher.update())} }// 訂閱者的類 class Watcher {// cb 回調函數中,記錄著當前 Watcher 如何更新自己的文本內容// 但是,只知道如何更新自己還不行,還必須拿到最新的數據,// 需要在 new Watcher 期間,把 vm 也傳遞進來(因為 vm 中保存著最新的數據)// 必須在 new Watcher 期間,指定 watcher 對應的數據的名字constructor(vm, key, cb) {this.vm = vmthis.key = keythis.cb = cb// 把創建的 Watcher 實例存到 Dep 實例的 subs 數組中 Dep.target = thiskey.split('.').reduce((newObj, k) => newObj[k], vm)Dep.target = null}// watcher 的實例,需要有 update 函數,從而讓發布者能夠通知我們進行更新!update() {const value = this.key.split('.').reduce((newObj, k) => newObj[k], this.vm)this.cb(value)} }

    2.在頁面中引入vue.js 然后實例化一個實例

    <script src="./vue.js"></script></head><body><div id="app"><p>姓名:{{name}}</p><p>年齡:{{age}}</p><p>工作:{{info.work}}</p><p>住址:{{info.address}}</p><input type="text" v-model="phone" /><p>雙向綁定的iPhone:{{phone}}</p></div><script>const vm=new Vue({el:"#app",data:{name:"海綿寶寶",age:3,info:{work:"高級廚師",address:"太平洋比奇堡"},phone:'123'}})</script>

    總結

    以上是生活随笔為你收集整理的vue的响应式原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。