上一篇: 简单的准备工作
为了更加有代入感,我们直接就在 src/index.js 下按照平时项目工程的写法开始;
在 source/vue/index.js
import { initState } from './observer';
// 构造函数
function Vue(options) {
this._init(options);
};
Vue.prototype._init = function(options) {
let vm = this;
// $options 表示 new Vue的时候传入的参数
vm.$options = options;
initState(vm);
}
export default Vue;
接下来新建一个 observer 文件夹, 再写一个 initState 的函数来完成初始化;
mkdir observer
cd observer
new-item index.js
// observer/index.js
import Observer from './observer';
export function initState(vm) {
let opts = vm.$options;
if (opts.data) {
// 初始化 data 属性
initData(vm)
}
};
export function observer(data) {
if (typeof data !== 'object' || data == null) {
return;
}
return new Observer(data);
}
function initData(vm) {
let data = vm.$options.data;
// 有可能 是函数,有可能是对象
data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
observer(vm._data);
};
走到这一步,很接近核心了,接下来就是 基于 Object.defineProperty
实现响应式数据;
// Powershell
new-item observer.js
// observer/observer.js
import { observer } from './index';
export function defineReactive(data, key, value) {
/**
这里是闭包的实际应用, value 的引用在外部使用,
不会被收回,每次更新数据都从 defineReactive 的作用域 VO 里面获取value
*/
observer(value); // 有可能当前value 是一个对象, 所以再调用一次 observer函数
return Object.defineProperty(data, key, {
get () {
return value;
},
set (newValue) {
if (newValue === value) return;
value = newValue;
}
})
}
class Observer {
constructor(data) {
this.walk(data)
}
walk(data) {
// 遍历传入的 对象, 循环调用 defineReactive;
let keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
defineReactive(data, key, data[key]);
}
}
};
export default Observer;