您现在的位置是:网站首页> 编程资料编程资料

Vue响应式原理模拟实现原理探究_vue.js_

2023-05-24 376人已围观

简介 Vue响应式原理模拟实现原理探究_vue.js_

前置知识

数据驱动

数据响应式——Vue 最标志性的功能就是其低侵入性的响应式系统。组件状态都是由响应式的 JavaScript 对象组成的。当更改它们时,视图会随即自动更新。

双向绑定——数据改变,视图改变;视图改变,数据也随之改变

数据驱动是Vue最独特的特性之一 —— 开发者只需关注数据本身,而无需关心数据如何渲染到视图

数据响应式的核心原理

Vue 2.x

Document
hello

当data中有多个对象时,需要对其进行遍历,此时需要对上述代码进行一些改造。

let data = { msg: 'hello', count: 10 } let vm = {} proxyData(data) function proxyData(data) { Object.keys(data).forEach(key => { Object.defineProperty(vm, key, { enumerable: true, configurable: true, get() { return data[key] }, set(newValue) { if (newValue === data[key]) return data[key] = newValue document.querySelector('#app').textContent = data[key] } }) }) }

Vue 3.x

步入Vue3,尤小右使用Proxy对其进行了改造,不仅抛弃了如 $delete 之类的鸡肋API(因为Proxy可以监听删除属性),还提升了性能。

Document
hello

发布订阅和观察者模式

发布/订阅模式

注:为简便起见,代码实现并未加入对传参的考虑。

Document

观察者模式

注:为简便起见,代码实现并未加入对传参的考虑。

观察者模式

Vue响应式原理模拟实现

Vue

功能

  • 接收初始化参数
  • 将data中的数据注入实例并转换成getter/setter
  • 调用observer监听data中属性的变化
  • 调用compiler解析指令/插值表达式
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) { Object.keys(data).forEach(key => { Object.defineProperty(this, key, { enumerable: true, configurable: true, get() { return data[key] }, set(newValue) { if (newValue === data[key]) return data[key] = newValue } }) }) } }

Observer对data中的属性进行监听

class Observer { constructor(data) { this.walk(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是对象,会将其内部的对象也变成响应式数据 this.walk(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { // 收集依赖 Dep.target && dep.addSub(Dep.target) return val }, set(newValue) { if (newValue === val) { return } val = newValue that.walk(newValue) // 发送通知 dep.notify() } }) } }

Compiler

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.comipleElement(node) } // 判断node节点是否有子节点 if (node.childNodes && node.childNodes.length) { this.compile(node) } }) } // 编译元素节点,处理指令 comipleElement(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, (newValue) => { node.textContent = newValue }) } // v-model modelUpdater(node, value, key) { node.value = value new Watcher(this.vm, key, (newValue) => { node.value = newValue }) // 双向绑定 node.addEventListener('input', () => { this.vm[key] = node.value }) } // 编译文本节点,处理插值表达式 compileText(node) { // console.dir(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, (newValue) => { node.textContent = newValue }) } } // 判断元素属性是否是指令 isDirective(attrName) { return attrName.startsWith('v-') } // 判断节点是否是文本节点 isTextNode(node) { return node.nodeType === 3 } // 判断节点是否是元素节点 isElementNode(node) { return node.nodeType === 1 } }

Watcher

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 newValue = this.vm[this.key] if (this.oldValue === newValue) { return } this.cb(newValue) } }

Dep

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

测试代码

Mini Vue

差值表达式

{{ msg }}

{{ count }}

v-text

v-model

提示: 本文由整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!

-六神源码网