zoukankan      html  css  js  c++  java
  • Vue响应式原理(手写源码)

    前言:

    本文依照上一篇文章为基础进行书写,可先看上一篇开发环境的搭建后再来看本文章。链接:博客园

    什么是响应式?

      简单的来说就是更改数据的时候,视图也会更新。

      vue是利用了Object.defineProperty的方法里面的setter 与getter方法的观察者模式来实现。

    1.vue响应式原理

      1.导出vue构造函数

    import {initMixin} from './init';
    
    function Vue(options) {
        this._init(options);
    }
    
    initMixin(Vue); // 给原型上新增_init方法
    
    export default Vue;

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

    import {initState} from './state';
    export
    function initMixin(Vue){
    Vue.prototype._init
    = function (options) { const vm = this; vm.$options = options // 初始化状态 initState(vm) } }

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

    export function initState(vm){
        const opts = vm.$options;
       // 判断里面有哪些数据,并进行初始化
        // 如 props, data, methods, computed, watch...   if(opts.props){
            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){
       // 数据响应式原理
        let data = vm.$options.data; // 用户传入的数据
        // vm._data 就是检测后的数据了
        data = vm._data = typeof data === 'function' ? data.call(vm) : data;
        // 观测数据
        observe(data); // 观测这个数据
    }
    function initComputed(){}
    function initWatch(){}

      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);
    }

      4.递归属性劫持

    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(){
                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);
    }

      上面通过打印可以查看到data中的属性都添加了get和set属性,实现更新

      5.数组方法的劫持

    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]);
            }
        }
    }

      重写数组原型方法

    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
        }
    })

       增加__obo__属性

    class Observer { 
        constructor(value){
            Object.defineProperty(value,'__ob__',{
                enumerable:false,
                configurable:false,
                value:this
            });
            // ...
        }
     }

      给所有响应式数据增加标识,并且可以在响应式上获取Observer实例上的方法;

      最后附上码云地址:链接

      每天学习一点,记录一点...

  • 相关阅读:
    测光
    闪光灯
    快门
    光圈
    白加黑减
    曝光补偿
    取景雷区
    着眼点
    Web中的无状态含义
    图计算模型[转]
  • 原文地址:https://www.cnblogs.com/0314dxj/p/13153043.html
Copyright © 2011-2022 走看看