<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                >[success] # 響應式模型 1. 從代碼運行角度來看這個問題,如果代碼正常執行的順序來說,JavaScript是程序性的,不是反應性的 2. vue2 利用 `Object.defineProperty` 進行收集「**依賴收集**」的**響應劫持**使得程序成為了具備反應性功能 >[info] ## js 執行角度例子 1. 下面正常的js邏輯代碼是否能像vue一樣,當price 發生改變期待著total 也可以重新計算,但實際結果卻是打印出來的結果為10 而不是12 2. JavaScript是程序性的,**不是反應性**的,為了使total反應性,我們必須使用JavaScript使事物表現不同 ~~~ let price = 5 let quantity = 2 let total = price * quantity price++ console.log(`total is ${total}`) // 10 ~~~ >[danger] ##### 讓程序具備響應性 total 可以在每次price 發生變化后**接著調用**,就可以實現**響應式的效果** ~~~ let price = 5 let quantity = 2 let total = price * quantity price++ total = price * quantity console.log(`total is ${total}`) // 12 ~~~ 讓程序具備響應式,就是在指定位置去觸發效果的改變,先收集行為,在指定觸發即可 ~~~ /** * 設計思路,記錄操作步驟=》在某些觸發節點觸發記錄步驟=》數據改變 */ let price = 5 let quantity = 2 let total = 0 // 收集步驟的空間 const storage = [] // 收集步驟方法 function record(step) { storage.push(step) } // 觸發步驟方法 function replay() { storage.forEach((run) => run()) } // 定義一些需要進行步驟方法例如 價格*數量 - 固定金額 為了方便演示拆兩個方法 const all = () => { total = price * quantity } const subtraction = () => { total = total - 8 } // 第一次收集需要執行步驟形式 record(all) record(subtraction) // 收集完開始觸發 replay() console.log(total) // 2 // 再次觸發需要調用改變數據行為的方法 price++ // 改變價格 replay() console.log(total) // 4 ~~~ * 發布訂閱來優化代碼 ~~~ // 發布者 class Dep { // 初始化構造收集 constructor() { this.sub = [] } // 收集 addSub(target) { this.sub.push(target) } // 觸發 notify() { this.sub.forEach((fun) => { fun() }) } } // 訂閱(觀察者) function watcher(myFunc) { target = myFunc // 在接受到調用者指令時候執行的回調函數 dep.addSub(target) target() // 上來先調用一次 target = null } const dep = new Dep() let price = 5 let quantity = 2 let total = 0 watcher(() => { total = price * quantity }) console.log(`total is ${total}`) // 10 price++ dep.notify() console.log(`total is ${total}`) // 12 ~~~ >[info] ## 利用 defineProperty 解決 手動觸發 上面問題是需要每次自己找到觸發點去手動觸發,隨著邏輯越來越多這種穿插的行為會越來越多,需要找到一個方法可以幫助我們自動去觸發我們注冊想執行的邏輯,,vue2.0采用了 `Object.defineProperty`這是一個函數,它允許我們為屬性**定義getter和setter函數** ~~~ let person = { name: 'w', } Object.defineProperty(person, 'name', { get() { console.log('調用時候執行') }, set(val) { console.log('賦值時候執行') } }) person.name // 執行get person.name = 'y' // 這里執行set ~~~ 現在可以利用對**數據變化進行監控**,這樣就可以做到對應觸發**發布者** ~~~ type VueOpt = { data: Record<any, any> } function cb(v: any) { console.log('更新') } class Vue { _data: Record<any, any> constructor(opt: VueOpt) { this._data = opt.data this.observer(this._data) } // observer 觀察者 observer(data: Record<any, any>) { if (!data || typeof data !== 'object') return Object.keys(data).forEach((key) => { this.defineReactive(data, key, data[key]) }) } // 轉換賦值Object.defineProperty defineReactive(data: Record<any, any>, key: string, value: any) { Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { return value }, set(v) { if (v === data[key]) return value = v // 簡單相應攔截 cb(v) }, }) } } const vue = new Vue({ data: { test: '測試', }, }) vue._data.test = '123' console.log(vue._data.test) ~~~ >[danger] ##### 簡單的渲染案例 1. 這里做個說明Object.defineProperty 對屬性進行get 和 set 是時候,在沒有調用情況僅僅只是一個綁定。 2. 僅僅是綁定因此'**watcher**' 先觸發有了統一的**target ** 3. 這個案例就說明了抽離觀察這和訂閱者的好處 可以通過外部去控制 ~~~ <!DOCTYPE html> <html lang="en" style="height: 100%;"> <body style="height: 100%;"> <div id="app">hello</div> <div id="app1">hello</div> <button id="changeViwe">觸發視圖更新 </button> <button id="changeViwe1">觸發視圖更新1 </button> </body> <script> /** * 描述 響應代理 * @param {Object} vm 代理后的對象 * @param {Object} data 被代理的對象 * @returns {any} */ function proxy(vm,data){ Object.keys(data).forEach(key => { // 注冊觀察者 const dep = new Dep() Object.defineProperty(vm,key,{ enumerable:true, configurable:true, get(){ console.log(1234); // 在get 時候記錄這個訂閱 // Object.defineProperty 在第一次注冊的時候是不會執行get的 // 因此其實第一遍給每個屬性綁定上get 和set時候只是注冊了 dep 觀察者對象,并沒有執行內部get 和set 方法 // 所以也就并沒有往里面填入實際的觀察方法 dep.depend() return data[key]; }, set(val){ console.log(1234); if(val===data[key]) return; data[key] = val; // 原來簡易版本的執行是寫死的只能固定某個位置渲染 // document.getElementById('app').textContent = Object.values(data).join() // 現在利用觀察者模型在dep.depend() 去注冊了 外部希望執行的回調函數代碼,代碼靈活 dep.notify() // 在賦值時候觸發訂閱動作 }, }) }) } /** * 描述 發布者 -- 主要是用來收集,和統一執行被收集來的訂閱者 */ class Dep{ constructor(){ this.subs = [] } // 這里有點區別之前的做的觀察者模型,那時候是有個函數參數 // 這個函數參數通過調用depend 注冊 // 但是現在觸發他的方法被在Object.defineProperty get 提前聲明 // 你在最初的時候不知道數據會根據什么數據動態變化 // 因此需要更為動態的形式去傳遞 方法 depend(){ if(target && !this.subs.includes(target)){ this.subs.push(target) } } // 去調用 notify(msg){ const { length } = this.subs length && this.subs.forEach(fun=>fun()) } } /** * 描述 訂閱者 * @param {Function} fun 執行的訂閱回調函數 */ function watcher(fun){ target = fun // 最開始的進階案例我們在 wather 這個函數調用了 Dep 中depend 來進行觀察的注冊 // 現在 因為通過Object.defineProperty 做了響應模型,這樣get 時候就能夠自動 // 幫助我們注冊了 Dep 中depend 來進行觀察 // 因此只用當fun 這個回調方法中使用了Object.defineProperty 包裹的屬性才會觸發 // 其實就是為解決在get 方法中自動添加監聽回調觸發 fun() target = null } const data = {msg:"123",age:123} const vm = {} proxy(vm,data) /** * 描述 id為app 的dom 節點插入內容 */ function appendAppRoot(){ document.getElementById('app').textContent = vm.msg + vm.age } /** * 描述 id為app1 的dom 節點插入內容 */ function appendApp1Root(){ document.getElementById('app1').textContent = vm.msg + vm.age } watcher( // 更加動態的在外部執行訂閱的方法 // watcher 內部會自調用 回調函數,回調函數中 vm.msg + vm.age 這兩段就會觸發get // 形成訂閱 appendAppRoot ) document.getElementById('changeViwe').onclick = function(){ // 當賦值的時候觸發set ,set 會觸發在調用watcher 時候注冊進入觀者某型中subs里面的回調 vm.msg = "測試" } watcher( // 更加動態的在外部執行訂閱的方法 appendApp1Root ) document.getElementById('changeViwe1').onclick = function(){ vm.msg = "測試666" } </script> </html> ~~~ >[danger] ##### 參考 [在 Vue Mastery 觀看視頻講解](https://www.vuemastery.com/courses/advanced-components/build-a-reactivity-system "Vue Reactivity") [關于 Object.definePropert 可以看我另外一篇文章](http://www.hmoore.net/cyyspring/more/1246692)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看