当前位置:   article > 正文

Vue响应式原理(源码)_vue数组响应式源码

vue数组响应式源码

前言:什么是vue响应式?官网解释:Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。
简而言之就是数据变页面变

实现原理:
Vue在组件和实例初始化的时候,会将data里的数据进行数据劫持(object.definepropty对数据做处理)。被解除过后的数据会有两个属性:一个叫getter,一个叫setter。
getter是使用数据的时候触发,setter是在修改数据的时候触发,修改数据的时候触发setter,同时也触发了底层的watcher监听,通知dom修改刷新(vue2.0)
Vue3.0是使用proxy()来实现对数据处理

vue响应式原理(源码)

1.导出vue构造函数

import {initMixin} from './init';

function Vue(options) {
    this._init(options);
}

initMixin(Vue); // 给原型上新增_init方法
export default Vue;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.init方法中初始化vue状态

import {initState} from './state';
export function initMixin(Vue){
    Vue.prototype._init = function (options) {
        const vm  = this;
        vm.$options = options
        // 初始化状态
        initState(vm)
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

根据不同属性进行初始化操作

export function initState(vm){
    const opts = vm.$options;
   // 判断里面有哪些数据,并进行初始化
    // 如 props, data, methods, computed, watch...
	if(opts.porps){
        initProps(vm);
    }  
    if(opts.methods){
        initMethod(vm);
    }
    if(opts.data){
        // 初始化data
        initData(vm);
    }
    if(opts.computed){
        initComputed(vm);
    }
    if(opts.watch){
        initWatch(vm);
    }
}
function initProps(){}
function initMethod(){}
function initData(vm){ // 这就就写data中的数据了
   // 数据响应式原理
    let data = vm.$options.data; // 用户传入的数据
    // vm._data 就是检测后的数据了
    data = vm._data = typeof data === 'function' ? data.call(vm) : data;
    // 观测数据
    observe(data); // 观测这个数据
}
function initComputed(){}
function initWatch(){}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

3.初始化数据

import {observe} from './observer/index.js'

function initData(vm){   //用户传递过来data中的数据
    let data = vm.$options.data;  //vm._data 就是监测后的数据
    data = vm._data = typeof data === 'function' ? data.call(vm) : data;
  // 观测数据
    observe(data);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
1.对象的监听

递归属性劫持

class Observer { // 观测值
    constructor(value){
        this.walk(value);
    }
    walk(data){ // 让对象上的所有属性依次进行观测
        let keys = Object.keys(data);
        for(let i = 0; i < keys.length; i++){
            let key = keys[i];
            let value = data[key];
            defineReactive(data,key,value);
        }
    }
}
function defineReactive(data,key,value){
    observe(value); // 判断是否是对象
    Object.defineProperty(data,key,{ // 添加get set方法
        get(){
            return value
        },
        set(newValue){
            if(newValue == value) return;
            observe(newValue);
            value = newValue
        }
    })
}
export function observe(data) {  // 判断data是不是对象,当null为空是也是返回object
	if(typeof data !== 'object' && data != null){
        return;
    }
    return new Observer(data);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

上面通过打印可以查看到data中的属性都添加了get和set属性(对象属性)

2.数组方法的劫持
import {arrayMethods} from './array';
class Observer { // 观测值
    constructor(value){

    // 对数组索引进行拦截 性能差而且直接更改索引的方式并不多
        Object.defineProperty(data,'__ob__',{ // __ob__ 是一个响应式饿表示 对象数组都有
            enumerable:false, // 不可枚举
            configurable:false,
            value:this
        })
        // data.__ob__ = this; // 相当于在数据上可以获取到__ob__这个属性 指代的是Observer的实例
        if(Array.isArray(data)){
            // vue如何对数组进行处理呢? 数组用的是重写数组的方法  函数劫持
            // 改变数组本身的方法我就可以监控到了
            data.__proto__ = arrayMethods; // 通过原型链 向上查找的方式
            // [{a:1}]    => arr[0].a = 100
            this.observeArray(data);
        }else{
            this.walk(data); // 可以对数据一步一步的处理
        }

    }
    observeArray(value){
        for(let i = 0 ; i < value.length ;i ++){
            observe(value[i]);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

重写数组原型方法

let oldArrayProtoMethods = Array.prototype;
export let arrayMethods = Object.create(oldArrayProtoMethods);
let methods = [ // 这七种方法会改变数组
    'push',
    'pop',
    'shift',
    'unshift',
    'reverse',
    'sort',
    'splice'
];

methods.forEach(method => {
    arrayMethods[method] = function (...args) {
        const result = oldArrayProtoMethods[method].apply(this, args);
        const ob = this.__ob__;
        let inserted;
        switch (method) {
            case 'push':
            case 'unshift':
                inserted = args;
                break;
            case 'splice':
                inserted = args.slice(2)
            default:
                break;
        }
        if (inserted) ob.observeArray(inserted); // 对新增的每一项进行观测
        return result
    }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

增加__obo__属性

class Observer { 
    constructor(value){
        Object.defineProperty(value,'__ob__',{
            enumerable:false,
            configurable:false,
            value:this
        });
        // ...
    }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

给所有响应式数据增加标识,并且可以在响应式上获取Observer实例上的方法;
最后附上代码:码云link;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/283841
推荐阅读
相关标签
  

闽ICP备14008679号