入口文件 index.js
import { initMixin } from './init'
function Vue(options) {
this.__init(options);
}
initMixin(Vue);
export default Vue
初始化文件 init.js
import { initState } from './initState.js';
export function initMixin(Vue) {
Vue.prototype.__init = function(options) {
let vm = this;
vm.$options = options;
initState(vm);
}
}
初始化状态 initState.js
import { observer } from './observer/index'
export function initState(vm) {
let ops = vm.$options;
if(ops.data) {
initData(vm);
}
}
function initData(vm) {
let data = vm.$options.data;
data = vm._data = typeof data === 'function' ? data.call(vm) : data;
for(let key in data) {
proxy(vm, '_data', key);
}
observer(data);
}
function proxy(vm, source, key) {
Object.defineProperty(vm, key, {
get() {
return vm[source][key];
},
set(newValue) {
vm[source][key] = newValue;
}
});
}
数据劫持 observer/index.js
import { ArrayMethods } from './arr';
export function observer(data) {
if(typeof data != 'object' || data == null ) {
return data;
}
return new Observer(data);
}
class Observer {
constructor(value) {
Object.defineProperty(value, '__obj__', {
enumerable: false,
value: this
});
if(Array.isArray(value)) {
value.__proto__ = ArrayMethods;
this.observerArr(value);
}else {
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);
}
}
observerArr(value) {
for(let i = 0; i < value.length; i++) {
observer(value[i]);
}
}
}
function defineReactive(data, key, value) {
observer(value);
Object.defineProperty(data, key, {
get() {
return value;
},
set(newValue) {
if(newValue === value) return value;
observer(value);
value = newValue;
}
})
}
数组的劫持
let oldArrayProtoMethods = Array.prototype;
export let ArrayMethods = Object.create(oldArrayProtoMethods);
let methods = [
'push',
'pop',
'unshift',
'shift',
'splice',
'reverse',
'sort'
]
methods.forEach(item => {
ArrayMethods[item] = function(...args) {
let result = oldArrayProtoMethods[item].apply(this, args);
let inserted;
switch(item) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.splice(2);
break;
}
let ob = this.__obj__;
if(inserted) {
ob.observerArr(inserted);
}
return result;
}
})
总结
-
- Vue原型上添加init方法
-
- vm赋值this(vue的实例)
-
- vm.$options = options方便以后去拿取数据
-
- initState(vm) =>初始化属性
-
- initData(vm) => 初始化数据
- vm.$options.data获取值
- 判断data是函数 or 对象
- for/in遍历代理到 vm(vue实例上去)
- observer(data) => 进行数据劫持
-
- 通过一个(Observer)类劫持
-
- 对象进行劫持
- 循环对象对每个属性,通过Object.defineProperty进行数据劫持
- 这里要考虑到对象的深度代理 get的时候 observer(value)
- 对象修改成另一个对象 set的时候 observer(value)
-
- 对数组进行劫持
- 这里需要重写数组的方法
- 获取原来的数组方法
- let oldArrayProtoMethods = Array.prototype;
- 继承
- export let ArrayMethods = Object.create(oldArrayProtoMethods);
- 需要劫持的数组方法名遍历重写数组方法
- 数组里面添加 对象的方式需要处理
- let ob = this.obj;//this -> value value上有observerArr方法
- ob.observerArr(inserted);//对添加的对象进行劫持
- 上一步的精髓: 在(ctrl+f) @9527处加一个不能枚举的属性,代理的值上存observerArr方法,value给的是 Observer的实例