手写Vue 对象劫持
Posted guangzhou11
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写Vue 对象劫持相关的知识,希望对你有一定的参考价值。
1.引入我们手写的Vue 拿到配置数据
import Vue from ‘../source/src/index‘; let vm = new Vue({ el: ‘#app‘, data() { return { msg: ‘hello‘, school: { name: ‘zf‘, age: 10 }, arr: [1, 2, 3] } }, computed: { }, watch: { } }) console.log(vm) // setTimeout(() => { // vm.arr[0].push(100) // console.log(vm) // }, 1000)
2. 新建文件 source/src/index
在这个文件中 我们利用 ——init 这个方法 对 用户传入的数据进行初始化 传入配置信息
function Vue(options) { // console.log(options) // 初始化vue this._init(options) }
3. 在_init 方法中 我们可以先将 options 挂载到实例 再进行初始化数据操作
Vue.prototype._init = function (options) { // vue 的初始化 let vm = this; // 将 options 挂载到实例 vm.$options = options; //需要数据重新初始化 initState(vm) }
5. 新建 文件 observe/index 编写 initState 方法并且导出
export function initState(vm) {
console.log(vm)
}
在这里我们可以拿到 vue 的整个实例方法
6. 在initState 方法中 我们进行初始化 比如初始化 数据 初始化 计算属性 初始化 watch
export function initState(vm) { // 拿到option 存储起来 let options = vm.$options; if (options.data) { // 初始化数据 initData(vm) } if (options.computed) { // 初始化计算属性 initComputed() } if (options.watch) { // 初始化watch initWatch() } }
7. 初始化数据 在 initData 方法中 我们通过 传入实例的option 拿到数据 再判断 数据 是 函数 还是 对象 如果是函数 调用call 方法 拿到函数 返回值 如何不是直接返回数据 或者 空数据
let data = vm.$options.data // 判断是否是函数 取返回值 data = vm._data = typeof data === ‘function‘ ? data.call(vm) : data || {}
8. 拿到数据后 我们要对数据进行 监听 编写 observe 方法 监听数据 、
function initData(vm) { let data = vm.$options.data // 判断是否是函数 取返回值 data = vm._data = typeof data === ‘function‘ ? data.call(vm) : data || {} observe(vm._data) }
10 在 observe 方法中 要进行判断 看看数据是不是对象或者为空 如果不是 直接返回 如果是 返回一个 Observe 对象
export function observe(data) { // 不是对象或者是null if (typeof data !== ‘object‘ || data === null) { return } return new Observe(data) }
11. 编写这个 Observe 对象 新建文件 Observe.js
这个文件里面最主要的是 Object.defineProperty 方法 里面传入 data , 还有key key 代表属性 所以 我们需要遍历数据 拿到 所有的 key value 传入
class Observe { constructor(data) { // 数组 重写push 方法 if (Array.isArray()) { } else { // 对象 // data 就是我们 定义的 vm._data 的数据 this.walk(data) } } //将对象的数据使用 defineProperty 重新定义 walk(data) { let keys = Object.keys(data); for (let i = 0; i < keys.length; i++) { let key = keys[i]; // key let value = data[keys[i]]; // value defineReactive(data, key, value) } } } export function defineReactive(data, key, value) { Object.defineProperty(data, key, { get() { // 有值 return value; }, set(newValue) { if (newValue !== value) return; value = newValue } }) } export default Observe;
11. 因为 数据里面 的对象 可能嵌套 一个对象 所以我们应该在 数据里面的对象再进行 深度监听
export function defineReactive(data, key, value) { observe(value); Object.defineProperty(data, key, { get() { // 有值 return value; }, set(newValue) { if (newValue !== value) return; value = newValue } }) }
12. 测试代码
import Vue from ‘../source/src/index‘; let vm = new Vue({ el: ‘#app‘, data() { return { msg: ‘hello‘, school: { name: ‘zf‘, age: 10 }, arr: [1, 2, 3] } }, computed: { }, watch: { } }) console.log(vm._data.msg) // hello
打印出100
13. 这里每次取值都需要 挂在 _data 上 很 不方便 比如
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) } observe(vm._data) }
14, 编写 proxy 方法
function proxy(vm, source, key) { Object.defineProperty(vm, key, { get() { return vm[source][key] }, set(newValue) { vm[source][key] = newValue } }) }
15。测试成功
import Vue from ‘../source/src/index‘; let vm = new Vue({ el: ‘#app‘, data() { return { msg: ‘hello‘, school: { name: ‘zf‘, age: 10 }, arr: [1, 2, 3] } }, computed: { }, watch: { } }) console.log(vm.msg) // hello
以上是关于手写Vue 对象劫持的主要内容,如果未能解决你的问题,请参考以下文章