Vue 2.x 中响应式原理的第二个阶段是如何实现的?

2026-05-05 18:161阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1315个文字,预计阅读时间需要6分钟。

Vue 2.x 中响应式原理的第二个阶段是如何实现的?

Vue 响应式原理模拟,模拟一个简易版 Vue 的整体分析,Vue 基本结构,打印 Vue,Vue 实例观察者,整体结构,Vue 要实现的功能,负责接收初始化参数(可选),负责把 data 中的属性注入到实例上。

Vue 响应式原理模拟

接上一步
模拟一个 简易版的 vue
整体分析

  • Vue 基本结构
  • 打印 Vue 实例观察
  • 整体结构

Vue 要实现的

  • 功能
    • 负责接收初始化的参数(选项)
    • 负责把 data 中的属性注入到 Vue 实例,转换成 getter/setter
    • 负责调用 observer 监听 data 中所有属性的变化
    • 负责调用 complier 解析指令/差值表达式
  • 结构 vue 类
    • $options
    • $el
    • $data
    • _proxyData() 私有
Vue 类.js

class Vue { constructor(options) { // 1. 通过属性保存选项的数据 this.$options = options || {} this.$data = options.data || {} this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el // 2. 把 data 中的成员转换成 getter 和 setter,注入到 vue 实例中 this._proxyData(this.$data) // 3. 调用 observer 对象,监听数据的变化 new Observer(this.$data) // 4. 调用 compiler 对象,解析指令和差值表达式 new Compiler(this) } _proxyData(data) { // 遍历 data 中的所有属性 Object.keys(data).forEach((key) => { // 把 data 中的属性注入到 vue 实例中 Object.defineProperty(this, key, { enumerable: true, configurable: true, get() { return data[key] }, set(newVal) { if (newVal === data[key]) return data[key] = newVal } }) }) } } Observer 类.js

// 负责数据劫持 // 把 $data 中的成员转换成 getter/setter class Observer { constructor(data) { this.walk(data) } // 遍历 data 对象的所有属性 walk(data) { // 1. 判断 data 是否是对象 if (!data || typeof data !== 'object') return // 2. 遍历 data 对象的所有属性 Object.keys(data).forEach((key) => { this.defineReactive(data, key, data[key]) }) } // 定义响应式成员 defineReactive(obj, key, val) { let that = this // 负责收集依赖,并发送通知 let dep = new Dep() // 如果 val 是对象,把 val 内部的属性转换成响应式数据 this.walk(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { // 收集依赖 Dep.target && dep.addSub(Dep.target) return val }, set(newVal) { if (newVal === val) return val = newVal that.walk(newVal) // 发送通知 dep.notify() } }) } } Compile 类.js

class Compiler { constructor(vm) { this.el = vm.$el this.vm = vm this.compile(this.el) } // 编译模板,处理文本节点和元素节点 compile(el) { let childNodes = el.childNodes Array.from(childNodes).forEach((node) => { if (this.isTextNode(node)) { // 处理文本节点 this.compileText(node) } else if (this.isElementNode(node)) { // 处理元素节点 this.compileElement(node) } // 判断 node 节点,是否有子节点,就递归 if (node.childNodes && node.childNodes.length) { this.compile(node) } }) } // 编译元素节点,处理指令 compileElement(node) { // 遍历所有的属性节点 Array.from(node.attributes).forEach((attr) => { let attrName = attr.name // 判断是否是指令 if (this.isDirective(attrName)) { // v-text --> text attrName = attrName.substr(2) let key = attr.value this.update(node, key, attrName) } }) } update(node, key, attrName) { let updateFn = this[attrName + 'Updater'] updateFn && updateFn.call(this, node, this.vm[key], key) } // 处理 v-text 指令 textUpdater(node, value, key) { node.textContent = value new Watcher(this.vm, key, (newVal) => { node.textContent = newVal }) } // v-model modelUpdater(node, value, key) { node.value = value new Watcher(this.vm, key, (newVal) => { node.value = newVal }) // 双向绑定 node.addEventListener('input', () => { this.vm[key] = node.value }) } // 编译文本节点,处理差值表达式 compileText(node) { let reg = /\{\{(.+?)\}\}/ let value = node.textContent if (reg.test(value)) { let key = RegExp.$1.trim() node.textContent = value.replace(reg, this.vm[key]) // 创建 watcher 对象,当数据改变更新视图 new Watcher(this.vm, key, (newVal) => { node.textContent = newVal }) } } // 判断元素属性是否是指令 isDirective(attrName) { return attrName.startsWith('v-') } // 判断节点是否是文本节点 isTextNode(node) { return node.nodeType === 3 } // 判断节点是否是元素节点 isElementNode(node) { return node.nodeType === 1 } } Watcher 类.js

class Watcher { constructor(vm, key, cb) { this.vm = vm // data 中的属性名称 this.key = key // 回调函数负责更新视图 this.cb = cb // 把 watcher 对象记录到 Dep 类的静态属性 target Dep.target = this // 触发 get 方法,在 get 方法中调用 addSub this.oldValue = vm[key] Dep.target = null } // 当数据发生变化的时候更新视图 update() { let newVal = this.vm[this.key] if (this.oldValue === newVal) return this.cb(newVal) } } Dep 类.js

class Dep { constructor() { // 存储所有的观察者 this.subs = [] } // 添加观察者 addSub(sub) { if (sub && sub.update) { this.subs.push(sub) } } // 发送通知 notify() { this.subs.forEach((sub) => { sub.update() }) } } 引入js

按相应的引入依赖,引入进 index.html 页面中,然后写相应的例子就行

<div id="app"> <h1>差值表达式</h1> <h3>{{msg}}</h3> <h4>{{count}}</h4> <h1>v-text</h1> <div v-text="msg"></div> <h1>v-model</h1> <input type="text" v-model="msg" /> <input type="text" v-model="count" /> </div> <script src="./js/dep.js"></script> <script src="./js/watcher.js"></script> <script src="./js/compiler.js"></script> <script src="./js/observer.js"></script> <script src="./js/vue.js"></script> <script> let vm = new Vue({ el: '#app', data: { msg: { info: '123' }, count: 100, person: { name: 'zhang' } } }) </script>

以上是一个超简单的响应式例子,还存在一些问题,不过理解一下对应的响应式原理流程就行

Vue 2.x 中响应式原理的第二个阶段是如何实现的?

本文共计1315个文字,预计阅读时间需要6分钟。

Vue 2.x 中响应式原理的第二个阶段是如何实现的?

Vue 响应式原理模拟,模拟一个简易版 Vue 的整体分析,Vue 基本结构,打印 Vue,Vue 实例观察者,整体结构,Vue 要实现的功能,负责接收初始化参数(可选),负责把 data 中的属性注入到实例上。

Vue 响应式原理模拟

接上一步
模拟一个 简易版的 vue
整体分析

  • Vue 基本结构
  • 打印 Vue 实例观察
  • 整体结构

Vue 要实现的

  • 功能
    • 负责接收初始化的参数(选项)
    • 负责把 data 中的属性注入到 Vue 实例,转换成 getter/setter
    • 负责调用 observer 监听 data 中所有属性的变化
    • 负责调用 complier 解析指令/差值表达式
  • 结构 vue 类
    • $options
    • $el
    • $data
    • _proxyData() 私有
Vue 类.js

class Vue { constructor(options) { // 1. 通过属性保存选项的数据 this.$options = options || {} this.$data = options.data || {} this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el // 2. 把 data 中的成员转换成 getter 和 setter,注入到 vue 实例中 this._proxyData(this.$data) // 3. 调用 observer 对象,监听数据的变化 new Observer(this.$data) // 4. 调用 compiler 对象,解析指令和差值表达式 new Compiler(this) } _proxyData(data) { // 遍历 data 中的所有属性 Object.keys(data).forEach((key) => { // 把 data 中的属性注入到 vue 实例中 Object.defineProperty(this, key, { enumerable: true, configurable: true, get() { return data[key] }, set(newVal) { if (newVal === data[key]) return data[key] = newVal } }) }) } } Observer 类.js

// 负责数据劫持 // 把 $data 中的成员转换成 getter/setter class Observer { constructor(data) { this.walk(data) } // 遍历 data 对象的所有属性 walk(data) { // 1. 判断 data 是否是对象 if (!data || typeof data !== 'object') return // 2. 遍历 data 对象的所有属性 Object.keys(data).forEach((key) => { this.defineReactive(data, key, data[key]) }) } // 定义响应式成员 defineReactive(obj, key, val) { let that = this // 负责收集依赖,并发送通知 let dep = new Dep() // 如果 val 是对象,把 val 内部的属性转换成响应式数据 this.walk(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { // 收集依赖 Dep.target && dep.addSub(Dep.target) return val }, set(newVal) { if (newVal === val) return val = newVal that.walk(newVal) // 发送通知 dep.notify() } }) } } Compile 类.js

class Compiler { constructor(vm) { this.el = vm.$el this.vm = vm this.compile(this.el) } // 编译模板,处理文本节点和元素节点 compile(el) { let childNodes = el.childNodes Array.from(childNodes).forEach((node) => { if (this.isTextNode(node)) { // 处理文本节点 this.compileText(node) } else if (this.isElementNode(node)) { // 处理元素节点 this.compileElement(node) } // 判断 node 节点,是否有子节点,就递归 if (node.childNodes && node.childNodes.length) { this.compile(node) } }) } // 编译元素节点,处理指令 compileElement(node) { // 遍历所有的属性节点 Array.from(node.attributes).forEach((attr) => { let attrName = attr.name // 判断是否是指令 if (this.isDirective(attrName)) { // v-text --> text attrName = attrName.substr(2) let key = attr.value this.update(node, key, attrName) } }) } update(node, key, attrName) { let updateFn = this[attrName + 'Updater'] updateFn && updateFn.call(this, node, this.vm[key], key) } // 处理 v-text 指令 textUpdater(node, value, key) { node.textContent = value new Watcher(this.vm, key, (newVal) => { node.textContent = newVal }) } // v-model modelUpdater(node, value, key) { node.value = value new Watcher(this.vm, key, (newVal) => { node.value = newVal }) // 双向绑定 node.addEventListener('input', () => { this.vm[key] = node.value }) } // 编译文本节点,处理差值表达式 compileText(node) { let reg = /\{\{(.+?)\}\}/ let value = node.textContent if (reg.test(value)) { let key = RegExp.$1.trim() node.textContent = value.replace(reg, this.vm[key]) // 创建 watcher 对象,当数据改变更新视图 new Watcher(this.vm, key, (newVal) => { node.textContent = newVal }) } } // 判断元素属性是否是指令 isDirective(attrName) { return attrName.startsWith('v-') } // 判断节点是否是文本节点 isTextNode(node) { return node.nodeType === 3 } // 判断节点是否是元素节点 isElementNode(node) { return node.nodeType === 1 } } Watcher 类.js

class Watcher { constructor(vm, key, cb) { this.vm = vm // data 中的属性名称 this.key = key // 回调函数负责更新视图 this.cb = cb // 把 watcher 对象记录到 Dep 类的静态属性 target Dep.target = this // 触发 get 方法,在 get 方法中调用 addSub this.oldValue = vm[key] Dep.target = null } // 当数据发生变化的时候更新视图 update() { let newVal = this.vm[this.key] if (this.oldValue === newVal) return this.cb(newVal) } } Dep 类.js

class Dep { constructor() { // 存储所有的观察者 this.subs = [] } // 添加观察者 addSub(sub) { if (sub && sub.update) { this.subs.push(sub) } } // 发送通知 notify() { this.subs.forEach((sub) => { sub.update() }) } } 引入js

按相应的引入依赖,引入进 index.html 页面中,然后写相应的例子就行

<div id="app"> <h1>差值表达式</h1> <h3>{{msg}}</h3> <h4>{{count}}</h4> <h1>v-text</h1> <div v-text="msg"></div> <h1>v-model</h1> <input type="text" v-model="msg" /> <input type="text" v-model="count" /> </div> <script src="./js/dep.js"></script> <script src="./js/watcher.js"></script> <script src="./js/compiler.js"></script> <script src="./js/observer.js"></script> <script src="./js/vue.js"></script> <script> let vm = new Vue({ el: '#app', data: { msg: { info: '123' }, count: 100, person: { name: 'zhang' } } }) </script>

以上是一个超简单的响应式例子,还存在一些问题,不过理解一下对应的响应式原理流程就行

Vue 2.x 中响应式原理的第二个阶段是如何实现的?