Vue源码 - 2.数据劫持

  1. 新建src/index.js
import { initMixin } from "./init"
function Vue(options) {
    // 运行 Vue 的初始化操作
    this._init(options)
}
// 通过引入文件的方式给 Vue 原型上添加方法
initMixin(Vue)
export default Vue
  1. 新建src/init.js
import { initState } from './state';
// 在原型上添加一个 init 的方法
export function initMixin(Vue) {
    // 初始化方法
    Vue.prototype._init = function(options) {
        // 数据的劫持
        const vm = this; // vue 中使用的 this.$options 指的就是用户传递的属性
        vm.$options = options
        // 初始化状态
        initState(vm)
    }
}
  1. 新建 src/state.js
import { observe } from "./observer/index"
export const initState = function(vm) {
    let opts = vm.$options
    console.log(opts);
    // vue 数据的来源 属性 、 方法 、 数据 、 计算属性 、 watch
    if(opts.props) {
        initProps(vm)
    }
    if(opts.methods) {
        initMethods(vm)
    }
    if(opts.data) {
        initData(vm)
    }
    if(opts.computed) {
        initComputed(vm)
    }
    if(opts.watch) {
        initWatch(vm)
    }
}
function initData(vm) {
    // 数据的初始化工作
    let data = vm.$options.data // 用户传递的 data
    /**
     * 1. data._data 是希望用户也能拿到 data 属性
     * 2. 如果 data 返回的是一个方法的话立即执行 data()
     */
    data = vm._data = typeof data == 'function' ? data.call(vm) : data
    // 对象劫持 用户改变了数据,我希望可以得到通知 - 刷新页面
    // MVVM 模式 数据改变可以驱动视图的变化
    // 核心方法 object.defineProperty() 给属性增加 get 、set 方法
    observe(data) // 响应式原理
}
  1. 新建 src/observer/index.js
/**
 * 1. 把 data 中的数据,都使用 Object.defineProperty 重新定义 es5 的方法
 * 2. Object.defineProperty 不能兼容 ie8 及其以下,所以 vue2 无法兼容 ie8 版本 
 */
import { isObject, def } from '../utils/index';
import { arrayMethods } from './array.js'
class Observer {
    constructor(value) {
        // vue 如果数据的层次过多,需要递归的去解析对象中的属性,依次增加 set 和 get 方法
        // 所以 vue3.0 中使用 proxy 方法,proxy不需要递归,也不需要给属性添加 get 和 set 方法,所以性能提升2-3倍
        def(value, '__ob__', this)
        if(Array.isArray(value)) {
            // 如果是数组的话并不会对索引进行观测,因为会导致性能问题
            // 前端开发很少去操作索引 push shift unshift
            // 如果数组里放的是对象我再监控
            value.__proto__ = arrayMethods // 重写数组的原型方法
            this.observerArray(value)
        } else {
            this.walk(value)
        }
    }
    observerArray(data) {
        data.forEach(item => {
            observe(item)
        })
    }
    walk(data) {
        let keys = Object.keys(data)
        keys.forEach(key => {
            defineReactive(data, key, data[key]) // 定义响应式数据
        });
    }
}

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) {
    let isObj = isObject(data)
    if(!isObj) {
        return
    }
    // 如果是对象的话就需要 观测数据
    return new Observer(data)
}
  1. 新建 src/utils/index.js
// 判断是否是对象
export function isObject(data) {
    return typeof data === 'object' && data !== null
}
// 不可枚举的响应式方法
export function def(data, key, value) {
    Object.defineProperty(data, key, {
        enumerable: false,
        configurable: false,
        value
    })
}

你可能感兴趣的:(Vue)