VUE-MVVM-part 10-Computed

8398 ワード

この文章を読む前に、前の文章を読んでいなかったら、文章の最後に引いて前の文章を調べます.
回顧する
まずしごきをします.前に実現したVue類は主に以下の機能があります.
  • 属性および方法のプロキシproxy
  • 傍受属性watcher
  • 事件
  • 現在のVueにおけるデータ処理と比べて、まだ実装されていないものがあります.Computedpropsprovied/inject.
    後の2つは親のコンポーネントと関係があるので、先に置いておいて、Computedを実現します.
    Computed
    公式文書には次のような言葉があります.
    属性の計算結果は、依存する応答属性が変化しない限り再計算されます.
    これは属性性能の計算が使用方法に比べて良い理由でもある.
    okこれを実現します.まず属性を計算する形式を決めます.
    {
        get: Function,
        set: Function
    }
    公式は私達に二種類の形式でComputedを書きにきました.ソースコードを見て、最終的にはこのような形に処理されていることが分かりました.だから、私達はまずこの形式を直接使って、後で統一化処理をします.
    慣例はテストコードを通して、私達はどんな機能を実現したいですか?
    let test = new Vue({
        data() {
            return {
                firstName: 'aco',
                lastName: 'Yang'
            }
        },
        computed: {
            computedValue: {
                get() {
                    console.log('    ')
                    return this.firstName + ' ' + this.lastName
                }
            },
            computedSet: {
                get() {
                    return this.firstName + ' ' + this.lastName
                },
                set(value) {
                    let names = value.split(' ')
                    this.firstName = names[0]
                    this.lastName = names[1]
                }
            }
        }
    })
    
    console.log(test.computedValue)
    //     
    // aco Yang
    console.log(test.computedValue)
    // acoYang (    ,      get   )
    test.computedSet = 'accco Yang'
    console.log(test.computedValue)
    //      (   set          )
    // accco Yang
    発見できます.
  • による属性計算は、Vueの例にエージェントされた1つの属性
  • である.
  • 第1回起動時にgetメソッドを呼び出しましたが、第2回目は
  • を出力しませんでした.
  • 依存性が変化すると、再度get方法
  • を呼び出した.
    解決
    第一のポイントはよく解決します.Object.definePropertyを使って代理すればいいです.次に、第二の点と第三の点を見て、依存性が変化すると、値が変化します.これは私達が以前に実現したWatcherと似ています.属性の計算値はget関数の戻り値です.Watcherにおいても、傍受値が保存されています.この値は依存の変化によって変化します.(watcher.valueが実現した学生を見ていないなら、Watcherstep3を見に行きます.)だから、属性を計算するstep4getWatcherです.getterWatcherは何ですか?ここではcallbackは必要ありません.属性の計算は依存が変化したときに保存された値だけが変化する必要があります.
    了解したら、私達はそれを実現します.同じように分かりやすいように、一つの種類を書きました.
    function noop() {
    }
    
    let uid = 0
    
    export default class Computed {
        constructor(key, option, ctx) {
            //     ctx     Vue    
            this.uid = uid++
            this.key = key
            this.option = option
            this.ctx = ctx
            this._init()
        }
    
        _init() {
            let watcher = new Watcher(
                this.ctx,
                this.option.get || noop,
                noop
            )
    
            //        Vue    
            Object.defineProperty(this.ctx, this.key, {
                enumerable: true,
                configurable: true,
                set: this.option.set || noop,
                get() {
                    return watcher.value
                }
            })
        }
    }
    
    // Vue      
    export class Vue extends Event {
        constructor(options) {
            super()
            this.uid = uid++
            this._init(options)
        }
    
        _init(options) {
            let vm = this
            ...
            for (let key in options.computed) {
                new Computed(vm, key, options.computed[key])
            }
    
        }
    }
    代理属性callbackと計算属性の値を更新し、依存性が変化していない場合にも、Object.definePropertyの更新をトリガすることなく、上記のWatcherの問題を解決した.
    しかし、属性を計算するには本当にリアルタイムで対応する値を更新する必要がありますか?
    まず、依存性の属性が変化すると計算属性の変化が起こることを知っています.言い換えれば、計算属性が変化すると、3の下の属性の一部が変化しています.dataの下の属性が変化すると、ビューの変化が生じるので、計算属性が変化してビューの変化をトリガする必要はありません.
    第二に、私たちは計算属性が必ず使われることを保証できません.
    第1の点に基づいて、属性を計算することは、ビューの変化をトリガする必要はないので、属性を計算することは、取得時に対応する値を更新すればよい.
    Watchの汚れ検査メカニズム
    上記の分析によれば、dataComputedの実現形態であるので、非リアルタイムアップデートWatcherを実現したい.Watcherにおいて、私たちが達成した値の更新は、以下のコードによって行われる.
    update() {
        const value = this.getter.call(this.obj)
        const oldValue = this.value
        this.value = value
        this.cb.call(this.obj, value, oldValue)
    }
    更新に依存すると、この関数をトリガします.この関数はWatcher例に保存されているWatcherを変更しました.ですから、ここで変更する必要があります.まず、疑似コードを見てください.
    update() {
        if(/*      Watcher          */){
            // doSomething
            //    update
            return
        }
        const value = this.getter.call(this.obj)
        const oldValue = this.value
        this.value = value
        this.cb.call(this.obj, value, oldValue)
    }
    ここでの判断は最初からvalueに教えてあげる必要がありますので、同じようにWatcherの構造関数を変更したいです.
    constructor(object, getter, callback, options) {
        ···
        if (options) {
            this.lazy = !!options.lazy
        } else {
            this.lazy = false
        }
        this.dirty = this.lazy
    }
    Watcherに複数の構成情報を伝達します.ここでは、リアルタイムで更新する必要がないWatcheroptionsと呼びます.また、このWatcherが更新する必要があるかどうかを示すためのフラグを設定します.専門点の名前を変えるには、汚い検査が必要ですか?
    okこれから上の疑似コードを実現します.
    update() {
        //     lazy Watcher
        if (this.lazy) {
            //        
            this.dirty = true
            return
        }
        const value = this.getter.call(this.obj)
        const oldValue = this.value
        this.value = value
        this.cb.call(this.obj, value, oldValue)
    }
    コードがlazy Watcherに到達した場合、このdirtyの依存性が変化したと説明しているが、これはWatcherであり、このupdateは汚損検査を行う必要がある.
    しかし、上記のコードはWatcherとしてマークされていますが、lazy Watcherには変化が発生していません.関数を書いて変化をトリガする必要があります.
    /**
     *              
     */
    evaluate() {
        this.value = this.getter.call(this.obj)
        //         ,   dirty
        this.dirty = false
    }
    完全なWatchコードを確認します.
    ok Watcherの実装を修正します.
    class Computed {
        constructor(ctx, key, option,) {
            this.uid = uid++
            this.key = key
            this.option = option
            this.ctx = ctx
            this._init()
        }
    
        _init() {
            let watcher = new Watcher(
                this.ctx,
                this.option.get || noop,
                noop,
                //    Wather     lazy Watcher
                {lazy: true}
            )
    
            Object.defineProperty(this.ctx, this.key, {
                enumerable: true,
                configurable: true,
                set: this.option.set || noop,
                get() {
                    //     dirty watch          ,   
                    if (watcher.dirty) {
                        watcher.evaluate()
                    }
                    return watcher.value
                }
            })
        }
    }
    テストしてください
    let test = new Vue({
        data() {
            return {
                firstName: 'aco',
                lastName: 'Yang'
            }
        },
        computed: {
            computedValue: {
                get() {
                    console.log('    ')
                    return this.firstName + ' ' + this.lastName
                }
            },
            computedSet: {
                get() {
                    return this.firstName + ' ' + this.lastName
                },
                set(value) {
                    let names = value.split(' ')
                    this.firstName = names[0]
                    this.lastName = names[1]
                }
            }
        }
    })
    //      (    watcher        get       )
    console.log('-------------')
    console.log(test.computedValue)
    //     
    // aco Yang
    console.log(test.computedValue)
    // acoYang (    ,      get   )
    
    test.firstName = 'acco'
    console.log(test.computedValue)
    //      (        ,     get   )
    // acco Yang
    
    test.computedSet = 'accco Yang'
    console.log(test.computedValue)
    //      (   set          )
    // accco Yang
    これまでのところ、単一Watcherにおけるデータ関連の内容はほぼ同じであり、valueComputedメカニズムを実現する前に、私たちは先に親子のコンポーネントを実現する必要があります.これも次のステップの内容です.
    クリックして関連コードを調べます.
    シリーズの記事アドレス
  • VUE-MVVM-part 1-defineProperty
  • VUE-MVVM-part 2-Dep
  • VUE-MVVM-part 3-Watch
  • VUE-MVVM-part 4-最適化Watch
  • VUE-MVVM-part 5-Observe
  • VUE-MVVM-part 6-Aray
  • VUE-MVVM-part 7-Event
  • VUE-MVVM-part 8-Eventを最適化する
  • VUE-MVVM-part 9-Vue
  • VUE-MVVM-part 10-Computed
  • VUE-MVVM-part 11-Exted
  • VUE-MVVM-part 12-props
  • VUE-MVVM-part 13-inject&総括