使用copilot生成vue响应式原理
何时进行响应式处理
1. 在new Vue
的时候,会调用observe
函数,observe函数会调用Observer
类,Observer类会调用walk
函数,walk函数会调用defineReactive
函数,defineReactive函数会调用Object.defineProperty
函数,从而实现响应式处理。
2. 在属性被读取的时候触发get()方法,Dep.target
进行赋值调用dep.depend(Dep.target)进行依赖收集。
3. 在属性被赋值的时候触发set()方法,对newValue
进行observe处理,调用dep.notify()
通知依赖进行更新。
4. Dep是依赖收集器/发布者,Watcher是观察者/订阅者,是属性与界面的中介,当属性变化时通知中介去更新视图,当
相关代码如下
// 生成vue的响应式原理 function defineReactive(obj, key, val) { // 递归 observe(val); // 创建Dep实例 const dep = new Dep(); Object.defineProperty(obj, key, { get() { // 收集依赖 Dep.target && dep.addDep(Dep.target); return val; }, set(newVal) { if (newVal !== val) { // 递归 observe(newVal); val = newVal; // 发送通知 dep.notify(); } }, }); } // 生成vue响应式的observe函数 function observe(obj) { if (typeof obj !== "object" || obj === null) { return; } new Observer(obj); } // 生成vue响应式的Observer类 // Obeserver类对应的中文名称是什么 => Observer对应的中文名称是 观察者 // Obeserver和watcher的区别是什么 => Obeserver是一个类,用来实例化一个Observer对象,Observer对象中有一个value属性,value属性对应的值就是data中的数据; // watcher是一个类,用来实例化一个watcher对象,watcher对象中有一个getter属性,getter属性对应的值是一个函数,函数中调用了data中的属性,从而触发属性的getter函数 class Observer { constructor(value) { this.value = value; // 判断value的类型 if (Array.isArray(value)) { // todo } else { this.walk(value); } } // 遍历对象的每一个key,对每一个key调用defineReactive walk(obj) { Object.keys(obj).forEach((key) => { defineReactive(obj, key, obj[key]); }); } } // 生成vue响应式的Dep类,用来收集依赖和发送通知 // Dep中文名称是什么 => Dep对应的中文名称是 【依赖收集器】 // 定义一个全局的target属性,用来存储当前的watcher // Dep.target是一个全局的唯一的watcher,因此在使用的时候需要先保存之前的值,使用完之后再恢复 class Dep { static target = ''; constructor() { this.deps = []; } addDep(dep) { this.deps.push(dep); } notify() { // 何时调用notify => 在属性的setter函数中调用 this.deps.forEach((dep) => dep.update()); } } // Dep.target在何时赋值 => 在Watcher的get方法中赋值 // 生成vue响应式的Watcher类,用来收集依赖和发送通知 // watcher是什么 => watcher是一个类,用来实例化一个watcher对象 // watcher有什么作用 => watcher的作用是在数据发生变化的时候,执行对应的更新函数 // watcher有哪些属性 => watcher有getter、options两个属性 // watcher有哪些方法 => watcher有get、update两个方法 // watcher对于的中文名称是什么 => 【Watcher】对应的中文名称是 观察者 或者 【订阅者】 // 发布者是谁 => 【发布者是Dep】 class Watcher { constructor(getter, options) { // getter是一个函数,函数中调用了data中的属性,从而触发属性的getter函数 this.getter = getter; // options是一个对象,包含了watcher的一些配置,例如:immediate、deep、lazy等 this.options = options; this.get(); } get() { // 将Dep.target指向当前的Watcher实例 // @ts-ignore // this是当前的watcher实例 Dep.target = this; // 执行getter函数,触发属性的getter函数 this.getter(); // 将Dep.target置为null // @ts-ignore Dep.target = null; } update() { // if (this.options.sync) { // this.run(); // } else { // queueWatcher(this); // } } run() { // const value = this.get(); // if (value !== this.value || isObject(value) || this.deep) { // const oldValue = this.value; // this.value = value; // if (this.user) { // try { // this.cb.call(this.vm, value, oldValue); // } catch (e) { // console.error(e, this.vm, `callback for watcher "${this.expression}"`); // } // } else { // this.cb.call(this.vm, value, oldValue); // } // } } queueWatcher(watcher) { // const id = watcher.id; // if (has[id] == null) { // has[id] = true; // if (!flushing) { // queue.push(watcher); // } else { // // if already flushing, splice the watcher based on its id // // if already past its id, it will be run next immediately. // let i = queue.length - 1; // while (i > index && queue[i].id > watcher.id) { // i--; // } // queue.splice(i + 1, 0, watcher); // } // // queue the flush // if (!waiting) { // waiting = true; // nextTick(flushSchedulerQueue); // } // } } } // watcher在何时创建 => 在组件的mounted钩子函数中创建 // 在mounted中如何创建watcher => 通过调用render函数来创建watcher // render函数在何时调用 => 在组件的render函数中调用 => new Watcher(getter, options) // getter是一个函数,函数中调用了data中的属性,从而触发属性的getter函数 // getter具体长啥样:() => { this._update(this._render()) } // render函数示例 // render函数中何时用到了watcher // 在render函数中调用了h函数,h函数中调用了data中的属性,从而触发属性的getter函数 function render() { // 生成虚拟dom const vnode = h("div", { id: "app" }, [ h("p", null, "hello world"), h("p", null, "hello world"), h("p", null, "hello world"), ]); // 将虚拟dom转换为真实dom const container = document.getElementById("app"); patch(container, vnode); } // 生成vue的h函数,作用是生成虚拟dom function h(tag, props, children) { return { tag, props, children, }; } // 生成vue的patch函数,作用是将虚拟dom转换为真实dom function patch(container, vnode) { const el = createElm(vnode); container.appendChild(el); } // 生成vue的createElm函数,作用是将虚拟dom转换为真实dom function createElm(vnode) { const { tag, props, children } = vnode; // 创建元素 const el = document.createElement(tag); // 处理属性 if (props) { for (let key in props) { el.setAttribute(key, props[key]); } } // 处理子节点 if (children) { if (typeof children === "string") { el.textContent = children; } else { children.forEach((child) => { patch(el, child); }); } } return el; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律