Vue响应式原理模拟实现原理探究
目录
- 前置知识
- 数据驱动
- 数据响应式的核心原理
- Vue 2.x
- Vue 3.x
- 发布订阅和观察者模式
- 发布/订阅模式
- 观察者模式
- Vue响应式原理模拟实现
- Vue
- Observer对data中的属性进行监听
- Compiler
- Watcher
- Dep
- 测试代码
前置知识
数据驱动
数据响应式——Vue 最标志性的功能就是其低侵入性的响应式系统。组件状态都是由响应式的 JavaScript 对象组成的。当更改它们时,视图会随即自动更新。
双向绑定——数据改变,视图改变;视图改变,数据也随之改变
数据驱动是Vue最独特的特性之一 —— 开发者只需关注数据本身,而无需关心数据如何渲染到视图
数据响应式的核心原理
Vue 2.x
Document - 锐客网 hello
当data中有多个对象时,需要对其进行遍历,此时需要对上述代码进行一些改造。
let data = https://www.it610.com/article/{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 =https://www.it610.com/article/== data[key]) returndata[key] = newValuedocument.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 = https://www.it610.com/article/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 =https://www.it610.com/article/== data[key]) returndata[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 =https://www.it610.com/article/== val) {return}val = newValuethat.walk(newValue)// 发送通知dep.notify()}})}}
Compiler
class Compiler {constructor(vm) {this.el = vm.$elthis.vm = vmthis.compile(this.el)}// 编译模板,处理文本节点和元素节点compile(el) {let childNodes = el.childNodesArray.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.nameif (this.isDirective(attrName)) {// v-text => textattrName = attrName.substr(2)let key = attr.valuethis.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 = valuenew Watcher(this.vm, key, (newValue) => {node.textContent = newValue})}// v-modelmodelUpdater(node, value, key) {node.value = https://www.it610.com/article/valuenew Watcher(this.vm, key, (newValue) => {node.value = https://www.it610.com/article/newValue})// 双向绑定node.addEventListener('input', () => {this.vm[key] = node.value})}// 编译文本节点,处理插值表达式compileText(node) {// console.dir(node); let reg = /\{\{(.+?)\}\}/let value = https://www.it610.com/article/node.textContentif (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类的静态属性targetDep.target = this// 触发get方法,在get方法中会调用addSubthis.oldValue = https://www.it610.com/article/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-textv-model
【Vue响应式原理模拟实现原理探究】到此这篇关于 Vue响应式原理模拟实现原理探究的文章就介绍到这了,更多相关 Vue响应式原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- vue清空form对象的方法
- 文档型数据库服务|火力全开!华为云发布三大新品,提供一站式数据库智能服务
- 不想又穷又丑注孤生,你该背诵这条价值两万亿日元的人生公式
- 函数式非凡的抽象能力
- 后端|35 设计优秀的分布式锁
- 嵌入式|2017年电信业值得关注的7大颠覆性技术
- 深入了解Java设计模式之职责链模式
- 如何结合整洁架构和MVP模式提升前端开发体验(三) - 项目工程化配置、规范篇
- SpringFramework的事件机制
- 使用ELK构建分布式日志分析系统