Vue2源码解析实现
Vue响应式实现
- Vue响应式原理
vue 实现数据响应式 ,是通过数据劫持侦测数据变化,发布订阅模式进行依赖收集与视图更新,Observe实现数据劫持,递归给对象属性,绑定setter和getter函数 - 实现机制
Vue的双向绑定实现机制核心:- 依赖于Object.defineProperty()实现数据劫持
- 订阅模式
- Vue2源码实现 —— 对data中对象的属性劫持
1. 创建一个index.js作为入口文件,创建一个Vue函数,并且抽离初始化文件进行模块化
import { initMixin } from "./init"; // 初始化 抽离模块化
function Vue(options) {
// new Vue 时是进行初始化
this._init(options);
}
initMixin(Vue);
export default Vue;
2. 创建初始化文件init.js,在Vue函数的实例上创建一个_init方法进行初始化状态
import { initStates } from "./initState";
export function initMixin(Vue) {
Vue.prototype._init = function (options) {
let vm = this;
vm.$options = options;
// 初始化状态
initStates(vm);
};
}
3. 创建初始化状态文件initState.js,在这里面写所有的初始化方法(data,props,watch,methods…)
import { observer } from "./observer/index";
export function initStates(vm) {
let opts = vm.$options;
if (opts.data) {
initData(vm);
}
if (opts.props) {
initProps(vm);
}
if (opts.watch) {
initWatch(vm);
}
if (opts.computed) {
initComputed(vm);
}
if (opts.methods) {
initMethods(vm);
}
}
//! Vue2 对data进行初始化
function initData(vm) {
//> 会有两种初始化方式 1. 对象 2. 函数
let data = vm.$options.data;
data = vm._data = typeof data === "function" ? data.call(vm) : data;
//> 对数据data进行劫持
observer(data);
}
function initProps() {}
function initWatch() {}
function initComputed() {}
function initMethods() {}
4. 创建一个文件夹observer里面的index.js,在这里面进行数据的劫持和发布订阅模式
export function observer(data) {
//> 判断data是否是对象或者空
if (typeof data != "object" || data == null) {
return data;
}
//> 1. 对象通过一个类
return new Observer(data);
}
//> vue2 进行劫持是通过Object.defineProperty() //! 缺点:只能是对象中的一个属性进行劫持 懒劫持
class Observer {
constructor(value) {
//> 进行遍历 让每一个属性都劫持到
this.walk(value);
}
walk(data) {
//> keys 是对象所有的属性
let keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
//> 对我们每一个属性进行劫持
const key = keys[i];
const value = data[key];
// console.log(value);
defineReactive(data, key, value);
}
}
}
//> 对对象属性进行劫持
function defineReactive(data, key, value) {
observer(value); //! 进行递归 ,深层次的劫持
Object.defineProperty(data, key, {
get() {
return value;
},
set(newValue) {
if (newValue == value) return;
observer(newValue); // 如果用户设置的值是对象,也要进行劫持
value = newValue;
},
});
}
-
总结
1. 需要多data数据进行判断,是对象还是函数,如果是函数就要改变this执行为vm
2. Object.defineProperty 有缺点,只能对对象中的一个属性进行劫持 所以进行遍历
3. 递归 将深层次的数据进行get set劫持 -
运行
为了方便打印查看是否进行数据劫持 在vm实例上添加_data属性存放data数据