vue2的响应式原理

in分为对象和数组两种

对象的话用defineReactive方法,内部采用Object.defineproperty给对象里面的每个属性都设置set和get方法,用递归的方式进行劫持对象里面的属性。

数组的话就会对数组里面的7个方法进行重写,通过引用数组原型对象身上的方法,进行重写的。

项目目录:

public ->index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    你好
    <div id="app">wa</div>
    <script src="../dist/umd/vue.js"></script>
    <script>
        // console.log(Vue);
        const instance = new Vue({
            el: '#app',
            data: {
                a: 100,
                b: { m: 10 },
                c: 30,
                d: [8, 9, 10, { x: 99 }]
            }
        })
        // instance._data.c = { x: 100 }
        //console.log(instance.$options)
    </script>
</body>

</html>

 

state.js

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

export default function initState(vm) {
    //console.log(vm)
    const ops = vm.$options //{el:'#app',data:{}}
    if (ops.props) {
        initProps(vm)
    }

    if (ops.methods) {
        initMethods(vm)
    }

    if (ops.data) {
        initData(vm)
    }

    if (ops.computed) {
        initComputed(vm)
    }

    if (ops.watch) {
        initWatch(vm)
    }
}

function initProps(vm) {

}
function initMethods(vm) {

}
function initData(vm) {
    //console.log(vm.$options.data) //{a:100}  //data.call调用data同时改变this指向
    let data = vm.$options.data
    vm.data = data = typeof data === 'function' ? data.call(vm) : data //方便获取数据
    //观测数据
    observe(data)
}
function initComputed(vm) {

}

function initWatch(vm) {

}

 init.js

import initState from "./state"
export default function initMixin(Vue) {
    Vue.prototype._init = function (options) {
        const vm = this
        vm.$options = options

        //初始化状态
        initState(vm)
    }


}

index.js

import initMixin from "./init"

function Vue(options) {
    //初始化
    this._init(options)

}
//在原型上扩展_init方法 (加下划线表示私有的)

//调用initMixin
initMixin(Vue)
export default Vue

observer-> index.js

import arrayMethods from "./array";

class Observer {
    constructor(value) {
        //vue不推荐我们操作数组用索引方式,推荐用数组方法操作 push shift pop……
        //
        Object.defineProperty(value, '__ob__', {
            enumerable: false, //不可枚举
            configurable: false,
            value: this
        })
        if (Array.isArray(value)) {
            //给value添加一个属性__ob__ 指向Observe实例
            //value是数组?
            value.__proto__ = arrayMethods //value._proto ={_proto_:Array.prototype}
            this.observeArray(value)
        } else {
            this.walk(value)
        }

    }

    observeArray(value) {
        value.forEach(item => observe(item));
        //数组的每个元素也要观测
    }



    walk(value) {
        const keys = Object.keys(value) //获取对象所有的属性
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i]
            let v = value[key]

            defineReactive(value, key, v)
        }
    }
}
function defineReactive(data, key, value) {
    //value 也有可能是对象,需要观测一下
    observe(value)
    console.log(data, key, value);
    Object.defineProperty(data, key, {
        get() {
            console.log('获取值');
            return value
        },
        set(newV) {

            if (newV === value) return
            observe(newV) //属性重新赋值也可能赋值一个对象
            console.log('设置值'); //更新视图逻辑
            value = newV

        }
    })

}


export function observe(data) {

    if (typeof data !== 'object' || data === null) {
        return;
    }
    return new Observer(data)

}

array.js

//Object.create() 返回值是{_proto_:Array.prototype}
let arrayMethods = Object.create(Array.prototype);


//重写数组方法 push pop shift unshift sort splice reverse 变异方法
// arrayMethods['push'] = function () {
//         console.log('数据变了,更新视图')
// }

['push', 'pop', 'shift', 'unshift', 'sort', 'splice', 'reverse']
    .forEach((method) => {
        arrayMethods[method] = function (...args) {
            console.log('数据变了,更新视图');
            //对传入的args参数进行响应式处理
            let inserted;
            switch (method) {
                case 'push':
                case 'unshift':
                    inserted = args;
                    break;
                case 'splice': inserted = args.slice(2)
            }
            if (inserted) {
                //对inserted每个元素进行观测
                this.__ob__.observeArray(inserted)
            }
            return Array.prototype[method].call(this, ...args)
        }

    });


export default arrayMethods //{_proto_:Array.prototype,push:function……}

版本信息:

 package.json

{
  "name": "vue-advance",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "rollup -c -w"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.22.9",
    "@babel/preset-env": "^7.22.9",
    "rollup": "^2.79.1",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-serve": "^2.0.2"
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值