MVVM框架最显著的特点就是虚拟dom和响应式的数据、我们以Vue为例,分别实现data、computed、created、methods以及虚拟dom。
这一章我们先实现简单的响应式,修改数据之后在控制台打印。
我们将该框架命名为MiniVue。
首先我们需要创建MiniVue的类(src/core/index.js)
创建完类之后我们需要创建一个js(src/core/init.js)给这个类的原型添加初始化事件,用来初始化data、computed等内容
然后在类中混入_init 方法并在构造方法中调用_init方法
接着我们来实现一个数据代理的一部分
当前的代理我们使用Object.defineProperty来实现,这个类方法有一个很大的缺点,就是在定义数组时,通过下标修改数据和新增删除数据时不会触发,vue2中的解决方法就是重写这些方法,这里我们也按照vue2的方式来实现。
我们在src中添加proxy.js,并添加代理方法(src/core/proxy.js)
/*** 代理数据* @param {*} vm * @param {*} data * @param {*} namespace 主要控制当前data是哪一个 name 还是 object.name 访问跟下面的属性就是 '' */
export function constructProxy(vm, data, namespace) {let proxyObj = {}// 我们需要判断data的类型if(data instanceof Array) { // 需要先判断Array类型} else if (data instanceof Object) { // 判断Object类型proxyObj = proxyObject(vm, data)} else {throw new Error('data must be an object or array')}return proxyObj
}
在响应式系统里面我们只代理Object和Array类型,如果不是就抛出错误
接下来我们来实现Object类型的代理 proxyObject 方法 (src/core/proxy.js)
/*** 代理Object数据* @param {*} vm * @param {*} data */
function proxyObject(vm, data) {// 创建代理对象let proxyObj = {}for(let key in data) {Object.defineProperty(proxyObj, key, {get() {return data[key]},set(newValue) {console.log(`正在修改:${key} 值为:${data[key]}`)data[key] = newValue}})// 为MiniVue的根实例添加属性Object.defineProperty(vm, key, {get() {return data[key]},set(newValue) {console.log(`正在修改根:${key} 值为:${data[key]}`)data[key] = newValue}})}return proxyObj
}
然后我们在初始化方法中去代理data (src/core/init.js)
/*** 实现init方法* @param {Object} options */
function init(options) {console.log(`options: ${options}`)// 初始化dataif(options && options.data) {this._data = constructProxy(this, options.data, "")}// 初始化computed// 初始化methods// 初始化created// 挂载dom
}
这样一个简单的代理就完成了,我们创建一个入口文件并实例化这个MiniVue来看效果
在控制台查看MiniVue实例的属性 然后修改_data 中的数据
我们通过图片内容可以看到:
1.我们可以看到实例中最外层有name 和 description ,这说明我们已经为MiniVue实例的根实例添加了属性
2.我们可以看到_data 中也添加了属性
3.修改根节点的name 后 _data.name 也会发生了变化,并且监控到了修改行为
章节总结:
1.创建MiniVue类,创建init.js 给原型添加_init方法
2.在方法中实现data、computed、created、methods以及虚拟dom
3.创建proxy.js, 入口方法中判断数据类型 如果不是Object 或者 Array 直接抛出异常
4.代理Object类型时, 创建代理对象,并且给实例的根属性也添加代理