Vue2.X响应式原理

一.defineProperty 的应用

Vue2.X响应式中使用到了 defineProperty 进行数据劫持,所以我们对它必须有一定的了解,那么我们先来了解它的使用方法把, 这里我们来使用 defineProperty来模拟Vue 中的data

<body><div id="app"></div><script>// 模拟 Vue的datalet data = {msg: '',}// 模拟 Vue 实例let vm = {}// 对 vm 的 msg 进行数据劫持Object.defineProperty(vm, 'msg', {// 获取数据get() {return data.msg},// 设置 msgset(newValue) {// 如果传入的值相等就不用修改if (newValue === data.msg) return// 修改数据data.msg = newValuedocument.querySelector('#app').textContent = data.msg},})// 这样子就调用了 defineProperty vm.msg 的 setvm.msg = '1234'</script>
</body>

可以看见 上面 vm.msg 数据是响应式的

二.defineProperty修改多个参数为响应式

看了上面的方法只能修改一个属性,实际上我们 data 中数据不可能只有一个,我们何不定义一个方法把data中的数据进行遍历都修改成响应式呢

<body><div id="app"></div><script>// 模拟 Vue的datalet data = {msg: '哈哈',age: '18',}// 模拟 Vue 实例let vm = {}// 把多个属性转化 响应式function proxyData() {// 把data 中每一项都[msg,age] 拿出来操作Object.keys(data).forEach((key) => {// 对 vm 的 属性 进行数据劫持Object.defineProperty(vm, key, {// 可枚举enumerable: true,// 可配置configurable: true,// 获取数据get() {return data[key]},// 设置 属性值set(newValue) {// 如果传入的值相等就不用修改if (newValue === data[key]) return// 修改数据data[key] = newValuedocument.querySelector('#app').textContent = data[key]},})})}// 调用方法proxyData(data)</script>
</body>

三.Proxy

在Vue3 中使用 Proxy 来设置响应式的属性

先来了解下Proxy的两个参数
new Proxy(target,handler)

  • target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
  • handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

其实 和 Vue2.X实现的逻辑差不多,不过实现的方法不一样

<body><div id="app"></div><script>// 模拟 Vue datalet data = {msg: '',age: '',}// 模拟 Vue 的一个实例// Proxy 第一个let vm = new Proxy(data, {// get() 获取值// target 表示需要代理的对象这里指的就是 data// key 就是对象的 键get(target, key) {return target[key]},// 设置值// newValue 是设置的值set(target, key, newValue) {// 也先判断下是否和之前的值一样 节省性能if (target[key] === newValue) return// 进行设置值target[key] = newValuedocument.querySelector('#app').textContent = target[key]},})</script>
</body>

触发setget的方法

// 触发了set方法
vm.msg = 'haha'
// 触发了get方法
console.log(vm.msg)

四.发布订阅模式

在Vue 响应式中应用到了 发布订阅模式 我们先来了解下

首先来说简单介绍下 一共有三个角色
发布者、 订阅者、 信号中心 举个现实中例子 作者(发布者)写一篇文章 发到了掘金(信号中心) ,掘金可以处理文章然后推送到了首页,然后各自大佬(订阅者)就可以订阅文章
Vue中的例子 就是EventBus $on $emit

那么我们就简单模仿一下 Vue 的事件总线吧

<body><div id="app"></div><script>class Vue {constructor() {// 用来存储事件// 存储的 例子 this.subs = { 'myclick': [fn1, fn2, fn3] ,'inputchange': [fn1, fn2] }this.subs = {}}// 实现 $on 方法 type是任务队列的类型 ,fn是方法$on(type, fn) {// 判断在 subs是否有当前类型的 方法队列存在if (!this.subs[type]) {// 没有就新增一个 默认为空数组this.subs[type] = []}// 把方法加到该类型中this.subs[type].push(fn)}// 实现 $emit 方法$emit(type) {// 首先得判断该方法是否存在if (this.subs[type]) {// 获取到参数const args = Array.prototype.slice.call(arguments, 1)// 循环队列调用 fnthis.subs[type].forEach((fn) => fn(...args))}}}// 使用const eventHub = new Vue()// 使用 $on 添加一个 sum 类型的 方法到 subs['sum']中eventHub.$on('sum', function () {let count = [...arguments].reduce((x, y) => x + y)console.log(count)})// 触发 sum 方法eventHub.$emit('sum', 1, 2, 4, 5, 6, 7, 8, 9, 10)</script>
</body>

五.观察者模式

与发布订阅 的差异
与发布订阅者不同 观察者中 发布者和订阅者(观察者)是相互依赖的 必须要求观察者订阅内容改变事件 ,而发布订阅者是由调度中心进行调度,那么看看观察者模式 是如何相互依赖,下面就举个简单例子

<body><div id="app"></div><script>// 目标class Subject {constructor() {this.observerLists = []}// 添加观察者addObs(obs) {// 判断观察者是否有 和 存在更新订阅的方法if (obs && obs.update) {// 添加到观察者列表中this.observerLists.push(obs)}}// 通知观察者notify() {this.observerLists.forEach((obs) => {// 每个观察者收到通知后 会更新事件obs.update()})}// 清空观察者empty() {this.subs = []}}class Observer {// 定义观察者内容更新事件update() {// 在更新事件要处理的逻辑console.log('目标更新了')}}// 使用// 创建目标let sub = new Subject()// 创建观察者let obs1 = new Observer()let obs2 = new Observer()// 把观察者添加到列表中sub.addObs(obs1)sub.addObs(obs2)// 目标开启了通知 每个观察者者都会自己触发 update 更新事件sub.notify()</script>
</body>

六.模拟Vue的响应式原理

这里来实现一个小型简单的 Vue 主要实现以下的功能

  • 接收初始化的参数,这里只举几个简单的例子 el data options
  • 通过私有方法 _proxyData 把data 注册到 Vue 中 转成getter setter
  • 使用 observer 把 data 中的属性转为 响应式 添加到 自身身上
  • 使用 observer 方法监听 data 的所有属性变化来 通过观察者模式 更新视图
  • 使用 compiler 编译元素节点上面指令 和 文本节点差值表达式

1.vue.js

在这里获取到 el data
通过_proxyDatadata的属性 注册到Vue 并转成 getter setter

/* vue.js */
class Vue {constructor(options) {// 获取到传入的对象 没有默认为空对象this.$options = options || {}// 获取 elthis.$el =typeof options.el === 'string'? document.querySelector(options.el): options.el// 获取 datathis.$data = options.data || {}// 调用 _proxyData 处理 data中的属性this._proxyData(this.$data)}// 把data 中的属性注册到 Vue_proxyData(data) {Object.keys(data).forEach((key) => {// 进行数据劫持// 把每个data的属性 到添加到 Vue 转化为 getter setter方法Object.defineProperty(this, key, {// 设置可以枚举enumerable: true,// 设置可以配置configurable: true,// 获取数据get() {return data[key]},// 设置数据set(newValue) {// 判断新值和旧值是否相等if (newValue === data[key]) return// 设置新值data[key] = newValue},})})}
}

2.observer.js

在这里把 data 中的 属性变为响应式加在自身的身上,还有一个主要功能就是 观察者模式在 第 4.dep.js 会有详细的使用

/* observer.js */class Observer {constructor(data) {// 用来遍历 datathis.walk(data)}// 遍历 data 转为响应式walk(data) {// 判断 data是否为空 和 对象if (!data || typeof data !== 'object') return// 遍历 dataObject.keys(data).forEach((key) => {// 转为响应式this.defineReactive(data, key, data[key])})}// 转为响应式// 要注意的 和vue.js 写的不同的是// vue.js中是将 属性给了 Vue 转为 getter setter// 这里是 将data中的属性转为getter setterdefineReactive(obj, key, value) {// 如果是对象类型的 也调用walk 变成响应式,不是对象类型的直接在walk会被returnthis.walk(value)// 保存一下 thisconst self = thisObject.defineProperty(obj, key, {// 设置可枚举enumerable: true,// 设置可配置configurable: true,// 获取值get() {return value},// 设置值set(newValue) {// 判断旧值和新值是否相等if (newValue === value) return// 设置新值value = newValue// 赋值的话如果是newValue是对象,对象里面的属性也应该设置为响应式的self.walk(newValue)},})}
}

在html中引入的话注意顺序

<script src="./js/observer.js"></script>
<script src="./js/vue.js"></script>

然后在vue.js 中使用Observer

/* vue.js */class Vue {constructor(options) {...// 使用 Obsever 把data中的数据转为响应式new Observer(this.$data)}// 把data 中的属性注册到 Vue_proxyData(data) {...}
}

看到这里为什么做了两个重复性的操作呢?重复性两次把 data的属性转为响应式
obsever.js 中是把 data 的所有属性 加到 data 自身 变为响应式 转成 getter setter方式
vue.js 中 也把 data的 的所有属性 加到 Vue 上,是为了以后方面操作可以用 Vue 的实例直接访问到 或者在Vue 中使用 this 访问

使用例子

<body><div id="app"></div><script src="./js/observer.js"></script><script src="./js/vue.js"></script><script>let vm = new Vue({el: '#app',data: {msg: '123',age: 21,},})</script></body>


这样在Vue$data 中都存在了 所有的data属性了 并且是响应式的

3.compiler.js

comilper.js在这个文件里实现对文本节点 和 元素节点指令编译 主要是为了举例子 当然这个写的很简单 指令主要实现v-text v-model

/* compiler.js */class Compiler {// vm 指 Vue 实例constructor(vm) {// 拿到 vmthis.vm = vm// 拿到 elthis.el = vm.$el// 编译模板this.compile(this.el)}// 编译模板compile(el) {// 获取子节点 如果使用 forEach遍历就把伪数组转为真的数组let childNodes = [...el.childNodes]childNodes.forEach((node) => {// 根据不同的节点类型进行编译// 文本类型的节点if (this.isTextNode(node)) {// 编译文本节点this.compileText(node)} else if (this.isElementNode(node)) {//元素节点this.compileElement(node)}// 判断是否还存在子节点考虑递归if (node.childNodes && node.childNodes.length) {// 继续递归编译模板this.compile(node)}})}// 编译文本节点(简单的实现)compileText(node) {// 核心思想利用把正则表达式把{{}}去掉找到里面的变量// 再去Vue找这个变量赋值给node.textContentlet reg = /\{\{(.+?)\}\}/// 获取节点的文本内容let val = node.textContent// 判断是否有 {{}}if (reg.test(val)) {// 获取分组一  也就是 {{}} 里面的内容 去除前后空格let key = RegExp.$1.trim()// 进行替换再赋值给nodenode.textContent = val.replace(reg, this.vm[key])}}// 编译元素节点这里只处理指令compileElement(node) {// 获取到元素节点上面的所有属性进行遍历![...node.attributes].forEach((attr) => {// 获取属性名let attrName = attr.name// 判断是否是 v- 开头的指令if (this.isDirective(attrName)) {// 除去 v- 方便操作attrName = attrName.substr(2)// 获取 指令的值就是  v-text = "msg"  中msg// msg 作为 key 去Vue 找这个变量let key = attr.value// 指令操作 执行指令方法// vue指令很多为了避免大量个 if判断这里就写个 uapdate 方法this.update(node, key, attrName)}})}// 添加指令方法 并且执行update(node, key, attrName) {// 比如添加 textUpdater 就是用来处理 v-text 方法// 我们应该就内置一个 textUpdater 方法进行调用// 加个后缀加什么无所谓但是要定义相应的方法let updateFn = this[attrName + 'Updater']// 如果存在这个内置方法 就可以调用了updateFn && updateFn(node, key, this.vm[key])}// 提前写好 相应的指定方法比如这个 v-text// 使用的时候 和 Vue 的一样textUpdater(node, key, value) {node.textContent = value}// v-modelmodelUpdater(node, key, value) {node.value = value}// 判断元素的属性是否是 vue 指令isDirective(attr) {return attr.startsWith('v-')}// 判断是否是元素节点isElementNode(node) {return node.nodeType === 1}// 判断是否是 文本 节点isTextNode(node) {return node.nodeType === 3}
}

4.dep.js

写一个Dep类 它相当于 观察者中的发布者 每个响应式属性都会创建这么一个Dep 对象 ,负责收集该依赖属性的Watcher对象 (是在使用响应式数据的时候做的操作)
当我们对响应式属性在 setter中进行更新的时候,会调用Depnotify 方法发送更新通知
然后去调用 Watcher 中的update实现视图的更新操作(是当数据发生变化的时候去通知观察者调用观察者的update更新视图)
总的来说 在Dep(这里指发布者) 中负责收集依赖 添加观察者(这里指Wathcer),然后在 setter 数据更新的时候通知观察者
说的这么多重复的话,大家应该知道是在哪个阶段 收集依赖 哪个阶段 通知观察者了吧,下面就来实现一下吧

先写Dep

/* dep.js */class Dep {constructor() {// 存储观察者this.subs = []}// 添加观察者addSub(sub) {// 判断观察者是否存在 和 是否拥有update方法if (sub && sub.update) {this.subs.push(sub)}}// 通知方法notify() {// 触发每个观察者的更新方法this.subs.forEach((sub) => {sub.update()})}
}

obsever.js 中使用Dep
get 中添加 Dep.target (观察者)
set 中 触发notify (通知)

/* observer.js */class Observer {...}// 遍历 data 转为响应式walk(data) {...}// 这里是 将data中的属性转为getter setterdefineReactive(obj, key, value) {...// 创建 Dep 对象let dep = new Dep()Object.defineProperty(obj, key, {...// 获取值get() {// 在这里添加观察者对象 Dep.target 表示观察者Dep.target && dep.addSub(Dep.target)return value},// 设置值set(newValue) {if (newValue === value) returnvalue = newValueself.walk(newValue)// 触发通知 更新视图dep.notify()},})}
}

5.watcher.js

watcher的作用 数据更新后 收到通知之后 调用 update进行更新

/* watcher.js */class Watcher {constructor(vm, key, cb) {// vm 是 Vue 实例this.vm = vm// key 是 data 中的属性this.key = key// cb 回调函数 更新视图的具体方法this.cb = cb// 把观察者的存放在 Dep.targetDep.target = this// 旧数据 更新视图的时候要进行比较// 还有一点就是 vm[key] 这个时候就触发了 get 方法// 之前在 get 把 观察者 通过dep.addSub(Dep.target) 添加到了 dep.subs中this.oldValue = vm[key]// Dep.target 就不用存在了 因为上面的操作已经存好了Dep.target = null}// 观察者中的必备方法 用来更新视图update() {// 获取新值let newValue = this.vm[this.key]// 比较旧值和新值if (newValue === this.oldValue) return// 调用具体的更新方法this.cb(newValue)}
}

那么去哪里创建Watcher呢?还记得在compiler.js中 对文本节点的编译操作吗
在编译完文本节点后 在这里添加一个Watcher
还有 v-text v-model指令 当编译的是元素节点 就添加一个 Watcher

/* compiler.js */class Compiler {// vm 指 Vue 实例constructor(vm) {// 拿到 vmthis.vm = vm// 拿到 elthis.el = vm.$el// 编译模板this.compile(this.el)}// 编译模板compile(el) {let childNodes = [...el.childNodes]childNodes.forEach((node) => {if (this.isTextNode(node)) {// 编译文本节点this.compileText(node)} ...}// 编译文本节点(简单的实现)compileText(node) {let reg = /\{\{(.+)\}\}/let val = node.textContentif (reg.test(val)) {let key = RegExp.$1.trim()node.textContent = val.replace(reg, this.vm[key])// 创建观察者new Watcher(this.vm, key, newValue => {node.textContent = newValue})}}...// v-text textUpdater(node, key, value) {node.textContent = value// 创建观察者2new Watcher(this.vm, key, (newValue) => {node.textContent = newValue})}// v-modelmodelUpdater(node, key, value) {node.value = value// 创建观察者new Watcher(this.vm, key, (newValue) => {node.value = newValue})// 这里实现双向绑定 监听input 事件修改 data中的属性node.addEventListener('input', () => {this.vm[key] = node.value})}
}

当 我们改变 响应式属性的时候 触发了 set()方法 ,然后里面 发布者 dep.notify 方法启动了,拿到了 所有的 观察者watcher实例去执行 update方法调用了回调函数 cb(newValue)方法并把 新值传递到了 cb()当中cb方法是的具体更新视图的方法 去更新视图

比如上面的例子里的第三个参数cb方法

new Watcher(this.vm, key, newValue => {node.textContent = newValue
})

7.测试

到了目前为止 响应式 和 双向绑定 都基本实现了 那么来写个例子测试下

<body><div id="app">{{msg}} <br />{{age}} <br /><div v-text="msg"></div><input v-model="msg" type="text" /></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: '123',age: 21,},})</script>
</body>


OK 基本实现了 通过 观察者模式 来 实现 响应式原理

8.五个文件代码

vue.js
/* vue.js */class Vue {constructor(options) {// 获取到传入的对象 没有默认为空对象this.$options = options || {}// 获取 elthis.$el =typeof options.el === 'string'? document.querySelector(options.el): options.el// 获取 datathis.$data = options.data || {}// 调用 _proxyData 处理 data中的属性this._proxyData(this.$data)// 使用 Obsever 把data中的数据转为响应式new Observer(this.$data)// 编译模板new Compiler(this)}// 把data 中的属性注册到 Vue_proxyData(data) {Object.keys(data).forEach((key) => {// 进行数据劫持// 把每个data的属性 到添加到 Vue 转化为 getter setter方法Object.defineProperty(this, key, {// 设置可以枚举enumerable: true,// 设置可以配置configurable: true,// 获取数据get() {return data[key]},// 设置数据set(newValue) {// 判断新值和旧值是否相等if (newValue === data[key]) return// 设置新值data[key] = newValue},})})}
}
obsever.js
/* observer.js */class Observer {constructor(data) {// 用来遍历 datathis.walk(data)}// 遍历 data 转为响应式walk(data) {// 判断 data是否为空 和 对象if (!data || typeof data !== 'object') return// 遍历 dataObject.keys(data).forEach((key) => {// 转为响应式this.defineReactive(data, key, data[key])})}// 转为响应式// 要注意的 和vue.js 写的不同的是// vue.js中是将 属性给了 Vue 转为 getter setter// 这里是 将data中的属性转为getter setterdefineReactive(obj, key, value) {// 如果是对象类型的 也调用walk 变成响应式,不是对象类型的直接在walk会被returnthis.walk(value)// 保存一下 thisconst self = this// 创建 Dep 对象let dep = new Dep()Object.defineProperty(obj, key, {// 设置可枚举enumerable: true,// 设置可配置configurable: true,// 获取值get() {// 在这里添加观察者对象 Dep.target 表示观察者Dep.target && dep.addSub(Dep.target)return value},// 设置值set(newValue) {// 判断旧值和新值是否相等if (newValue === value) return// 设置新值value = newValue// 赋值的话如果是newValue是对象,对象里面的属性也应该设置为响应式的self.walk(newValue)// 触发通知 更新视图dep.notify()},})}
}
compiler.js
/* compiler.js */class Compiler {// vm 指 Vue 实例constructor(vm) {// 拿到 vmthis.vm = vm// 拿到 elthis.el = vm.$el// 编译模板this.compile(this.el)}// 编译模板compile(el) {// 获取子节点 如果使用 forEach遍历就把伪数组转为真的数组let childNodes = [...el.childNodes]childNodes.forEach((node) => {// 根据不同的节点类型进行编译// 文本类型的节点if (this.isTextNode(node)) {// 编译文本节点this.compileText(node)} else if (this.isElementNode(node)) {//元素节点this.compileElement(node)}// 判断是否还存在子节点考虑递归if (node.childNodes && node.childNodes.length) {// 继续递归编译模板this.compile(node)}})}// 编译文本节点(简单的实现)compileText(node) {// 核心思想利用把正则表达式把{{}}去掉找到里面的变量// 再去Vue找这个变量赋值给node.textContentlet reg = /\{\{(.+?)\}\}/// 获取节点的文本内容let val = node.textContent// 判断是否有 {{}}if (reg.test(val)) {// 获取分组一  也就是 {{}} 里面的内容 去除前后空格let key = RegExp.$1.trim()// 进行替换再赋值给nodenode.textContent = val.replace(reg, this.vm[key])// 创建观察者new Watcher(this.vm, key, (newValue) => {node.textContent = newValue})}}// 编译元素节点这里只处理指令compileElement(node) {// 获取到元素节点上面的所有属性进行遍历![...node.attributes].forEach((attr) => {// 获取属性名let attrName = attr.name// 判断是否是 v- 开头的指令if (this.isDirective(attrName)) {// 除去 v- 方便操作attrName = attrName.substr(2)// 获取 指令的值就是  v-text = "msg"  中msg// msg 作为 key 去Vue 找这个变量let key = attr.value// 指令操作 执行指令方法// vue指令很多为了避免大量个 if判断这里就写个 uapdate 方法this.update(node, key, attrName)}})}// 添加指令方法 并且执行update(node, key, attrName) {// 比如添加 textUpdater 就是用来处理 v-text 方法// 我们应该就内置一个 textUpdater 方法进行调用// 加个后缀加什么无所谓但是要定义相应的方法let updateFn = this[attrName + 'Updater']// 如果存在这个内置方法 就可以调用了updateFn && updateFn.call(this, node, key, this.vm[key])}// 提前写好 相应的指定方法比如这个 v-text// 使用的时候 和 Vue 的一样textUpdater(node, key, value) {node.textContent = value// 创建观察者new Watcher(this.vm, key, (newValue) => {node.textContent = newValue})}// v-modelmodelUpdater(node, key, value) {node.value = value// 创建观察者new Watcher(this.vm, key, (newValue) => {node.value = newValue})// 这里实现双向绑定node.addEventListener('input', () => {this.vm[key] = node.value})}// 判断元素的属性是否是 vue 指令isDirective(attr) {return attr.startsWith('v-')}// 判断是否是元素节点isElementNode(node) {return node.nodeType === 1}// 判断是否是 文本 节点isTextNode(node) {return node.nodeType === 3}
}
dep.js
/* dep.js */class Dep {constructor() {// 存储观察者this.subs = []}// 添加观察者addSub(sub) {// 判断观察者是否存在 和 是否拥有update方法if (sub && sub.update) {this.subs.push(sub)}}// 通知方法notify() {// 触发每个观察者的更新方法this.subs.forEach((sub) => {sub.update()})}
}
watcher.js
/* watcher.js */class Watcher {constructor(vm, key, cb) {// vm 是 Vue 实例this.vm = vm// key 是 data 中的属性this.key = key// cb 回调函数 更新视图的具体方法this.cb = cb// 把观察者的存放在 Dep.targetDep.target = this// 旧数据 更新视图的时候要进行比较// 还有一点就是 vm[key] 这个时候就触发了 get 方法// 之前在 get 把 观察者 通过dep.addSub(Dep.target) 添加到了 dep.subs中this.oldValue = vm[key]// Dep.target 就不用存在了 因为上面的操作已经存好了Dep.target = null}// 观察者中的必备方法 用来更新视图update() {// 获取新值let newValue = this.vm[this.key]// 比较旧值和新值if (newValue === this.oldValue) return// 调用具体的更新方法this.cb(newValue)}
}

手写demo了解vue响应式原理,订阅模式,观察者模式相关推荐

  1. TS手写简陋版reactive响应式原理(依赖收集,依赖更新)

    最近博主看源码了解到了vue3的响应式原理 vue3的响应式实现是依赖收集与依赖更新,vue3已经从Object.property更换成Proxy,Proxy相比于前者可以直接监听对象数组,对于深层次 ...

  2. Vue源码--解读vue响应式原理

    原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...

  3. Vue响应式原理探究之“发布-订阅”模式

    前言 在面试题中经常会出现与"发布订阅"模式相关的题目,比如考察我们对Vue响应式的理解,也会有题目直接考验我们对"发布订阅"模式或者观察者模式的理解,甚至还会 ...

  4. 【2019 前端进阶之路】深入 Vue 响应式原理,活捉一个 MVVM

    作者:江三疯,专注前端开发.欢迎关注公众号前端发动机,第一时间获得作者文章推送,还有各类前端优质文章,致力于成为推动前端成长的引擎. 前言 作为 Vue 面试中的必考题之一,Vue 的响应式原理,想必 ...

  5. vue 数组删除 dome没更新_详解Vue响应式原理

    摘要: 搞懂Vue响应式原理! 作者:浪里行舟 原文:深入浅出Vue响应式原理 Fundebug经授权转载,版权归原作者所有. 前言 Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是 ...

  6. Vue响应式原理的简单模型

    1.前言 最近在梳理vue响应式的原理,有一些心得,值得写一篇博客出来看看. 其实之前也尝试过了解vue响应式的原理,毕竟现在面试看你用的是vue的话,基本上都会问你几句vue响应式的原理.以往学习这 ...

  7. Vue响应式原理(看这一篇就够了)

    你肯定听说过Object.denfineProperty或是Proxy\reflect,这的确是在VUE响应式原理中起重要作用的一部分代码,但这远远不能代表整个流程的精妙.上图: 不懂没关系,请往下看 ...

  8. 深入理解Vue响应式原理

    前言 Vue响应式原理是Vue最独特的特性之一,当数据模型进行修改时,视图就会进行更新,这使得状态管理简单直接,但是其底层的细节还是需要我们深入学习理解,这样遇到一些问题我们才能快速进行定位,并解决: ...

  9. Vue学习 — Vue响应式原理

    一. Object.defineProperty 在学习vue响应式原理之前,必须搞懂 Object.defineProperty. Object.defineProperty(obj, prop, ...

  10. 详解Vue响应式原理

    摘要: 搞懂Vue响应式原理! 作者:浪里行舟 原文:深入浅出Vue响应式原理 Fundebug经授权转载,版权归原作者所有. 前言 Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是 ...

最新文章

  1. LIVE 预告 | 旷视王剑锋:丢弃Transformer,FCN也可以实现端到端检测
  2. PyTorch教程(十):单层感知机以及梯度更新
  3. 2021牛客暑期多校训练营1 A.Alice and Bob 博弈 SG函数
  4. [芯片] 2、接口技术·实验二·定时/计数器8253
  5. redis aof命令缓冲区的写入源码
  6. mybatis mapper配置 bigint_Mybatis-Plus入门
  7. 三星Galaxy note I9220 系统廋身
  8. while在c语言中的作用,while的用法_C语言中while的用法
  9. [词根词缀]nomin/norm/not/nounce/nov及词源N的故事
  10. 工程项目成本费用明细表_项目工程成本不会测算?全套Excel自动计算表,可直接套定额...
  11. c语言建立并存储树,利用十字链表存储树结构(便于同时求出某一点的入度与出度)------C语言实现...
  12. 没有学历可以做程序员吗?
  13. 拆解SSK SCRM330 USB3.0读卡器 GL3233 固件 0819
  14. PCB布局布线的相关基本原理和设计技巧
  15. 高一计算机精英班班徽,高一8班班徽设计图案素材大全
  16. java 得到汉字的拼音,Java获取汉字的拼音
  17. JavaScript实现11位手机号码正则表达式
  18. 电源系统优化设计,低压差稳压器(LDO)如何选型?
  19. 【Kafka从成神到升仙系列 六】kafka 不能失去网络通信,就像西方不能失去耶路撒冷
  20. 基础知识redis详解--【Foam番茄】

热门文章

  1. 联想服务器怎么加硬盘分区,联想EDU硬盘保护OEM分区的创建以及安装方法
  2. 华为移动应用引擎 虚拟化异常解决办法
  3. 爱快iKuai 安装成功后运行提示程序运行中解决办法
  4. 请说一下你是如何理解互联网思维的?
  5. 山东境内的自驾游景点一览表
  6. windows10启动项修复||an operating system wasn't found解决办法
  7. True Liars (并查集压缩路径 + DP)
  8. [转]如何高效迅速地进入500强外企
  9. 知云文献翻的一些使用
  10. Android补间动画原理介绍