[Vue2 ソースコード] レスポンシブ原則

[Vue2 ソースコード] レスポンシブ原則


Vue.js は基本的に MVVM (Model–View–ViewModel) アーキテクチャ パターンに従い、データ モデルは単なる通常の JavaScript オブジェクトです。それらを変更すると、ビューが更新されます。この記事では、Vue 応答システムの基礎となる詳細について説明します。

Vue响应式核となる設計思想は、

Vueインスタンスを作成するとき、オプションのプロパティをvue走査し、プロパティの追加をハイジャックしてデータを読み取り (依存関係の収集に使用され、更新のディスパッチに使用されます)、内部で依存関係を追跡し、プロパティがアクセスおよび変更されたときに変更を通知します。dataObject.definePropertygettersettergettersetter

各コンポーネント インスタンスには対応するインスタンスがあり、コンポーネント (依存関係コレクションとインスタンス)watcherのレンダリング中に依存関係のすべてのデータ属性が記録され、依存関係が変更されると、メソッドは依存関係にあるインスタンスに通知します。再計算 (更新のディスパッチ) を行い、関連するコンポーネントを再レンダリングします。computed watcheruser watchersetterdatawatcher

全体的なプロセス

フロントエンドMVVMフレームワークとしてのVue基本的な考え方はと同じでAngular、その核となるのは、データが変更されるとページが自動的に更新されるため、煩雑な操作から解放され、ビジネス ロジックの処理に集中できるということです。ReactDOMDOM

これはVue双方向のデータ バインディング (応答原則とも呼ばれます) です。データの双方向バインディングは、Vue最もユニークな機能の 1 つです。ここでは、公式フローチャートを使用して、Vueレスポンシブ システムのプロセス全体を簡単に説明します。

ここに画像の説明を挿入

ではVue、各コンポーネント インスタンスには対応するwatcherインスタンス オブジェクトがあり、コンポーネントのレンダリング中にプロパティを依存関係として記録し、依存関係が呼び出されるときに再計算setterを通知することwatcherで、関連するコンポーネントが更新されます。

これは典型的な観察者のパターンです。

応答性における重要な役割

Vue データ双方向バインディングの実装ロジックには、3 つの重要な役割があります。

  • Observer: その機能は、依存関係の収集と更新のディスパッチのためにオブジェクトのプロパティにgetterと を追加することです。setter
  • Dep: 現在の応答オブジェクトの依存関係を収集するために使用されます. サブオブジェクトを含む各応答オブジェクトにはインスタンスがありますDep(内部にインスタンス配列subsがありますWatcher). データが変更されると、dep.notify()それぞれに通知されますwatcher
  • Watcher: オブザーバー オブジェクト、インスタンスは 、 、 の 3 つのタイプに分けられます渲染 watcher (render watcher)计算属性 watcher (computed watcher)侦听器 watcher(user watcher)

変更の検出に関する考慮事項

Object.definePropertyVue 2.0 では、実装に基づいた応答性の高いシステムです (このメソッドは ES5 ではシム化できない機能であるため、Vue は IE8 以前のバージョンのブラウザーをサポートしません) Proxy/Reflect

1. JavaScript の制限により、Object.defineProperty() API は配列の長さの変化を監視したり、配列やオブジェクトの新しい変更を検出したりできません。

2. Vue は、配列インデックスを介して配列項目を直接変更する操作を検出できません。これが Object.defineProperty() API の理由ではありませんが、Yuda 氏は、パフォーマンスの消費はそれがもたらすユーザー エクスペリエンスに直接比例しないと考えています。配列の項目が 1000 または 10000 個など大きい場合があるため、配列の応答検出ではパフォーマンスが大幅に消費されます。

応答原理

応答性の基本原則は、オプションのデータを Vue のコンストラクターで処理することです。つまり、Vue インスタンスを初期化するときに、データ、プロパティ、その他のオブジェクトの各属性が Object.defineProperty によって一度定義され、データが設定されると、対応するビューを変更するためのいくつかの操作が実行されます。

データ観察

Object.defineProperty に基づいて配列とオブジェクトのハイジャックを実現します。

\src\observe\index.js

import {
    
     newArrayProto } from "./array"

class Observe {
    
    
    constructor (data) {
    
    
        //Object.defineReactive只能劫持已经存在的属性(vue俩民回为此单独写一些api)
        //data.__ob__ = this  //这里的this指的是Observe,把这个实例附到了ob上,还可以用于检测是否被劫持过
        Object.defineProperty(data,'__ob__',{
    
    
            value:this,
            enumerable:false //将__ob__编程不可枚举,这样循环的时候就无法获取到,不会进入死循环
        })
        if(Array.isArray(data)) {
    
    
            data.__proto__ = newArrayProto
            this.observeArray(data)  //如果数组中放置的是对象,也可以被监控到
            //这里我们可以重写数组中的方法,7个变异方法,是可以修改到数组本身的
        }else {
    
    
            this.walk(data)
        }
        
    }
    walk (data) {
    
     //循环对象,对属性依次劫持
        //“重新定义”属性  性能差
        Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
    }
    observeArray(data) {
    
    
        data.forEach(item=> observe(item))
    }
}

export function defineReactive (target, key, value) {
    
      //闭包
    observe(value) //对所有的对象都进行属性劫持  深度劫持
    Object.defineProperty(target, key, {
    
    
        get () {
    
     //取值的时候会执行get
            console.log(key,"key");
            return value
        },
        set (newValue) {
    
      //修改的时候会执行set
            if (newValue === value) return
            observe(newValue)
            value = newValue
        }
    })
}


export function observe (data) {
    
    
    if (typeof data !== 'object' || data == null) {
    
    
        //只对对象进行劫持
        return
    }
    if(data.__ob__ instanceof Observe) {
    
     //如果存在data.__ob__就说明这个被代理过了
        return data.__ob__
    }
    //如果一个对象被劫持过了,那就不需要再被劫持了
    //要判断一个对象是否被劫持过,可以增添一个实例,用实例来判断是否被劫持过
    return new Observe(data)

}

配列 7 の変更メソッドを書き換える

7 つのメソッドとは、プッシュ、ポップ、シフト、シフト解除、ソート、リバース、スプライスを指します。(この 7 つで元の配列が変更されます) 実装アイデア: スライス指向プログラミング!

Array.prototype のメソッドを直接オーバーライドするのではなく、プロトタイプ チェーンの継承と関数ハイジャックによる移行です。

Object.create(Array.prototype) を使用して新しいオブジェクト newArrayProto を生成します。オブジェクトのプロトはArray.prototype を指し、次にオーバーライドされたメソッドで配列のプロトが新しいオブジェクト newArrayProto を指すようにします。これにより、newArrayProto と Array.prototype がすべて一致するようになります。配列のプロトタイプチェーン上。

ああ。プロト=== newArrayProto;newArrayProto。プロト=== 配列.プロトタイプ

次に、Array.prototype.push.call を使用して、オーバーライドされたメソッド内の元のメソッドを呼び出し、新しく追加されたデータをハイジャックします。
\src\observe\array.js


let oldArrayProto = Array.prototype  //获取数组的原型

export let newArrayProto = Object.create(oldArrayProto)

let methods = [ //通过遍历寻找到所有变异方法
    'push',
    'pop',
    'shift',
    'reverse',
    'sout',
    'splice'
] //concat slice不会改变原数组

methods.forEach(method => {
    
    
    newArrayProto[method] = function (...args) {
    
     //这里重写了数组的方法
        const result = oldArrayProto[method].call(this,...args)  //再内部调用原来的方法,函数的劫持,切片编程
        //我们需要对新增的数据进行劫持
        let inserted
        let ob = this.__ob__
        switch (method) {
    
    
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice' : //arr.splice(0,1,{a:1},{b:2})
                inserted = args
                break

        }
        console.log("xinzeng ");
        if(inserted) {
    
    
            //对新增的内容再次进行观测
            ob.observeArray(inserted)

        }
        
        
        return result
    }
})

__ob__ 属性を追加

これは厄介で賢いプロパティです。このインスタンスを Observer クラス内のリアクティブ データに追加します。これは、すべてのレスポンシブデータに識別子を追加することに相当し、オブザーバーインスタンス上のメソッドをレスポンシブデータ上で取得できます。

class Observe {
    
    
    constructor (data) {
    
    
        //Object.defineReactive只能劫持已经存在的属性(vue俩民回为此单独写一些api)
        //data.__ob__ = this  //这里的this指的是Observe,把这个实例附到了ob上,还可以用于检测是否被劫持过
        Object.defineProperty(data,'__ob__',{
    
    
            value:this,
            enumerable:false //将__ob__编程不可枚举,这样循环的时候就无法获取到,不会进入死循环
            
        })

        if(Array.isArray(data)) {
    
    
            data.__proto__ = newArrayProto

            this.observeArray(data)  //如果数组中放置的是对象,也可以被监控到
            //这里我们可以重写数组中的方法,7个变异方法,是可以修改到数组本身的
        }else {
    
    
            this.walk(data)
        }
        
    }
    walk (data) {
    
     //循环对象,对属性依次劫持
        //“重新定义”属性  性能差
        Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
    }
    observeArray(data) {
    
    
        data.forEach(item=> observe(item))
    }
}
__ob__ には 2 つの主な用途があります。

1. オブジェクトが乗っ取られた場合、再度乗っ取る必要はありません。オブジェクトが乗っ取られたかどうかを判断するには、obを使用して判断できます。


export function observe (data) {
    
    
    if (typeof data !== 'object' || data == null) {
    
    
        //只对对象进行劫持
        return
    }
    if (data.__ob__ instanceof Observe) {
    
     //如果存在data.__ob__就说明这个被代理过了
        return data.__ob__
    }
    //如果一个对象被劫持过了,那就不需要再被劫持了
    //要判断一个对象是否被劫持过,可以增添一个实例,用实例来判断是否被劫持过
    return new Observe(data)

}

2. 配列の 7 つの突然変異メソッドを書き直しました。そのうちのプッシュ、アンシフト、およびスプライス メソッドは、配列に新しいメンバーを追加します。この時点で、新しく追加されたメンバーを再度監視する必要があり、Observer インスタンスのobserveArray メソッドをobを通じて呼び出すことができます。

おすすめ

転載: blog.csdn.net/m0_63779088/article/details/130273962