VUEのアクティブシステム(VUE 2)


reactとは異なり、VueはsetState()のような設定状態の関数を使用せず、データ(すなわち状態)を変更することによって自動的に状態を更新する.どうしてそんなことができるの?これが気になってこの文章を書いた
一般的なブログ記事はVuejsはgetters/settersを用いてこれが可能であることを示していますが、このように説明するだけでは、どのような方法で現れるのでしょうか.正式な書類を見ているだけでは理解できないので、イメージを参考にして、理解した内容に基づいて書きます.
その前にVueまずjs公式文書を見てみましょう.

変更を追跡します。


以下の画像は公式文書の内容を写している.
VueインスタンスでのJavaScriptオブジェクトの使用  data  オプションとして渡すと、Vueは  Object.definePropertyの使用  getter/settersに変換します.これがVueがIE 8以下のES 5をサポートしない理由です.
ユーザーにはgetter/setterは表示されませんが、プロパティにアクセスまたは変更すると、Vueは依存追跡および変更通知を実行できます.変換後のデータ・オブジェクトを記録すると、ブラウザは異なるgetter/setterフォーマットを処理し、熟知したインタフェースを使用することに注意してください.  vue-devtoolsのインストールを推奨します.
すべての構成部品インスタンスに適用 watcher コンポーネント依存レンダリング中に[修正](Modify)のすべてのアトリビュートが記録されるインスタンスがあります.依存setterがトリガーされると、watcherに通知され、構成部品が再レンダリングされます.

まず、VUEが反応性システムのためにgetter/setterを使用する理由を理解するためには、まず依存性(dependency)と監視プログラムの2つの概念を理解しなければならない.
このようなJavascriptコードがあります
	let price = 5
    let quantity = 2
    let total = price * quantity  // 10
    price = 20
    console.log(`total is ${total}`) // 10
一番下のコンソールのtotalは10です.priceは20になりましたが、もちろん結果は10です.このコードをVue更新状態に変換しましょう.

Problem

totalまたはtotalを変更するには、priceを再実行するには、quantityをどこかに保存します.最終的には、totalをどこかに保存することが重要です.

Solution

priceまたはquantity変数を更新する場合は、保存されたtotalを再実行する必要があります.total関数を作成して、record()を格納します.
	let price = 5
    let quantity = 2
    let total = 0
    let target = null
    
    target = function () { 
      total = price * quantity
    }

	let storage = [] // target function을 저장할 배열
		
	price = 20    

    function record () {
      storage.push(target) // record 함수는 stoarge 배열에 target 함수를 저장한다.
    }
    
    record() // total을 저장하기
    target()
storageアレイに格納されたtargetを取り出し、replay()関数を作成します.
    function replay (){
      storage.forEach(run => run())
    }
	let price = 5
    let quantity = 2
    let total = 0
    let target = null
    let storage = []
    
    function record () { 
      storage.push(target)
    }
    
    function replay () {
      storage.forEach(run => run())
    }
    
    target = () => { total = price * quantity }
    
    record()
    target()
    
    price = 20
    console.log(total) // => 10
    replay()
    console.log(total) // => 40
完全なコードを見てみましょう.record()によりtotalが格納され、保存されたtotalを取り出すためにreplay()関数が呼び出される.では、一番下のコンソールは変化した価格を撮影します.40.
しかし、上記のコードはグローバル変数を乱用し、カプセル化されたコードもない.上のコードをクラスに入れます.

上の論理をClassで置き換えましょう


以上のrecord(), replay(), storage等はいずれも状態に関する論理であるため,クラスに分類する.
	class Dep { // Stands for dependency
      constructor () {
        this.subscribers = [] //  storage 배열을 대체한다. 이게 흔히들 알고 있는 의존성 배열이다.
     	}
      depend() {  // record() 함수를 대체한다.
        if (target && !this.subscribers.includes(target)) {
          this.subscribers.push(target)
        } 
      }
      notify() {  // replay() 함수를 대체한다.
        this.subscribers.forEach(sub => sub())
      }
    }
では、今からコンソールでpriceを撮りましょう.subscribers配列を依存配列と呼ぶ.コンソールを確認して、出力が正常であることを確認します.
	const dep = new Dep()
    
    let price = 5
    let quantity = 2
    let total = 0
    let target = () => { total = price * quantity }
    dep.depend() // 의존성 배열에 현재 target 추가
    target()  // total을 얻기 위해 target 호출
    
    console.log(total) // => 10
    price = 20
    console.log(total) // => 10
    dep.notify()       // 배열 내부의 함수 실행
    console.log(total) // => 40 

Watcher


これまでのコードは、各変数にDepクラスを提供する.もちろん、現在のコードも正常に動作しますが、監視状態の更新を継続する論理dep.depend()target()が面倒です.
この状態の更新を監視する論理をカプセル化することが望ましい. このため、watcher関数を作成します.
	// target, dep.depend()를 대신해서
	target = () => { total = price * quantity }
    dep.depend() 
    target()
	// watcher를 함수(캡슐화를 위한)를 이렇게 만들어보고 싶다는 것이다. 
	watcher(() => {
      total = price * quantity
    })
では、このwatcher関数の内部はどのように構築されるのでしょうか.watcher関数の内部には、コンソール上での撮影時に上記の結果を得るために以下の内容が記述されている.
	function watcher(myFunc) {
	  target = myFunc    // 콜백 함수 myFunc
      dep.depend()       // 의존성 배열에 myFunc를 추가
      target()           // 받아온 함수 myFunc, 즉 target을 호출
      target = null      // Reset target
    }

	price = 20
    console.log(total)
    dep.notify()      
    console.log(total)
さらに、notify()も単独で呼ばないようにしたい.notify()も状態に関する論理なので、カプセル化できる案はありますか?

  • 各変数はDepクラスを有する
    解決策のために、dataオブジェクト内にprice属性およびquantity属性が付与される.
    let data = { price: 5, quantity: 2 }
    このように変更すると、上記のwatcher関数も以下のように変更されます.
    	watcher(() => {
          total = data.price * data.quantity
        })
    この場合、新しいwatcher関数が呼び出され、今回呼び出されたwatcherdata.quantityにぶつからず、data.priceにぶつかるだけです.
    	watcher(() => {
          salePrice = data.price * 0.9
        })
    現在の状況を図で要約すると以下のようになる.
  • 2data.priceを監視する2つのwatcher関数が見られる.
  • 次に、各Depの内部でどの属性(price, quantity)がアクセスされ、どのwatcherインスタンスが呼び出されるかを決定する必要がある.これを画像で以下のように表現します.

    また、属性が更新されるタイミングも決定される(price, quantity).これを確認すれば、dep.notify()クラスからDepをいつ呼び出すべきかがわかります.
    簡単に言えば、dep.notify()を呼び出す必要がなくなります.

    そのため、Vue.jsはObject.defineProperty()を使用します.ここにはgetter/setterの概念があります.Object.defineProperty()メソッドの3番目のパラメータdescriptorの内部の属性get, setを定義する.次のコードを見てください.
    	let data = { price: 5, quantity: 2 }
        
        Object.defineProperty(data, 'price', {  // price 속성만
        
            get() {  // get 메서드 생성 
              console.log(`I was accessed`)
            },
            
            set(newVal) {  // set 메서드 생성
              console.log(`I was changed`)
            }
        })
        data.price // This calls get()
        data.price = 20  // This calls set(20)
    このようにしてget, setのオーバーシュートを実現した.概念がわかった以上、実際に書いたコードに適用しましょう.
    	// 코드에 적용된 예시 (data.price만)
    	let data = { price: 5, quantity: 2 }
        
        let internalValue = data.price // data.price를 저장하기 위한 변수 internalValue 
        
        Object.defineProperty(data, 'price', { 
        
            get() {  // Create a get method
              console.log(`Getting price: ${internalValue}`)
              return internalValue
            },
            
            set(newVal) { 
              console.log(`Setting price to: ${newVal}` )
              internalValue = newVal // 현재 value를 set 해준다.(바꿔준다)
            }
        })
        total = data.price * data.quantity  // This calls get() 
        data.price = 20  // This calls set()
    では現在はdata.priceだけでなく、data.quantityにも当てはまるでしょう.
    	let data = { price: 5, quantity: 2 }
        
        Object.keys(data).forEach(key => { // data객체의 key를 순회하기 위해
          let internalValue = data[key]
          Object.defineProperty(data, key, {
            get() {
              console.log(`Getting ${key}: ${internalValue}`)
              return internalValue
            },
            set(newVal) {
              console.log(`Setting ${key} to: ${newVal}` )
              internalValue = newVal
            }
          })
        })
        total = data.price * data.quantity
        data.price = 20
    今さっき作成したwatcher()関数を結合するとVueこれはjsのデータ反応性システムとある程度似ている.

    最終コード

    	let data = { price: 5, quantity: 2 }
        let target = null
        
        // 아까 봤던 Dep class이다.
        class Dep {
          constructor () {
            this.subscribers = [] 
          }
          depend() {  
            if (target && !this.subscribers.includes(target)) {
              this.subscribers.push(target)
            } 
          }
          notify() {
            this.subscribers.forEach(sub => sub())
          }
        }
        
        // Go through each of our data properties
        Object.keys(data).forEach(key => {
          let internalValue = data[key]
          
          // Each property gets a dependency instance
          const dep = new Dep()
          
          Object.defineProperty(data, key, {
            get() {
              dep.depend() // <-- Remember the target we're running
              return internalValue
            },
            set(newVal) {
              internalValue = newVal
              dep.notify() // <-- Re-run stored functions
            }
          })
        })
        
        // 더 이상 watcher 함수는 dep.depend()를 호출할 필요가 없게 된다. 
    	// 위에서 get 메서드에서 호출 중이기 때문
        function watcher(myFunc) {
          target = myFunc
          target()
          target = null
        }
        
        watcher(() => {
          data.total = data.price * data.quantity
        })

    n/a.結論


    これはVueですこれによりjsで実行されるコードが大幅に簡略化される.しかし、全体のフレームワークは同じです.(Vue.jsスポンサーのビデオだからでしょ?)
    時間があればぜひBuild a Reactivity Systemという動画を見てください説明が上手で、無料です.
    また、VUEのReactivity Systemについて詳しく知りたい場合は、deep-dive-into-reactivity-in-depthを参照してください.

    コメントリンク


  • Build a Reactivity System

  • 反応型を深く理解する

  • deep-dive-into-reactivity-in-depth