手写实现vue的MVVM响应式原理

MVVM响应式实现原理:

1.模板编译

2.数据劫持

3.watcher

文中应用到的数据名词:

MVVM   ------------------        视图-----模型----视图模型                三者与 Vue 的对应:view 对应 templatevm 对应 new Vue({…})model 对应 data

nodeType       判断节点是否是元素节点

querySelector    创建一个元素节点

createDocumentFragment     文档碎片

attributes      获取元素属性集合

textContent   获取文本内容

reduce( prev,next,currentIndex){}               一个可以用上一个元素和当前元素做处理的方法

defineProperty(obj,key,value){}                数据拦截的主要方法

首先建立一个vue的实例,建立mvvm.js ,构建 mvvm类。  获取el的节点 和 data   放入实例中,在将Observer.js(数据劫持)和Compile.js(模板编译) 放入mvvm的js   ,全部在index页面运行.

第一步:模板编译

我首先制作Compile.js ,也就是模板编译  。

首先需要获取el 这个属性的值   用nodeType === 1判断是不是元素节点. 如果不是则用 queryselector() 生成一个节点 。  这样做的目的是,有些人el:#app  有些人是document.getElementById(‘app‘)。 不管俩者如何,我们都要生成一个节点来供后续使用。

随后判断el节点是否存在,如果存在。则进行编译 ,  这里编译最好不要在dom里进行遍历编译,非常耗性能 。 我推荐的是用 createDocumentFragment() 方法. 建立一个虚拟节点对象, 在这个虚拟节点对象里进行遍历以及对应的操作。

那么说到虚拟节点, 我们需要将我们获取的el节点整个放入进去 ,进行遍历,将app里的每一个子节点都搬到fragement 变量中。

然后进行节点的编译。这里的节点又分为元素节点和文本节点。 还是用刚刚的nodeType判断区分吗,然后做对应的操作。

接下来我们先编译元素节点  首先我们需要知道,获取元素节点要做什么,为什么获取元素节点。 我是希望通过获取元素节点上的关于vue的指令,比如:v-model,v-html,v-for 。等等...   那么这些指令是放在元素节点上的属性里,所以我们用 attributes 获取元素节点的属性名的集合 ,也就是我们说的v-model 。通过遍历这个attr属性名的集合,获取每个属性名。通过isDirective函数判断attrName包含 v-  的属性,这里我做给假设,好方便理解。 这里通过上面的过滤,可以得出attrName 是一个指令名。那我假设这个指令名为v-model。 我首先获取v-model的值,也就是expr。然后做一个解耦对象CompileUtil ,方便后面制作其他的指令。所以这里调用的是CompileUtil[model]{node,this.v,,expr};

 

调用model的指令后,在model这个函数里做相对应的处理。这里的watcher构造函数先不用管,后面的事情。 这里的uptate[‘modelUptate‘]和model一样放在CompileUtil 中,方便管理。 如果updateFn存在的话,则执行updateFn(),将v-model的值赋予input节点的value.下图中的getVal 是防止 v-model=’messge.a‘ 这种嵌套对象的。这个函数里,首先利用split将messge.a 拆分成[messge,a] 数组。然后利用reduce方法   放回 上一个元素[当前元素],而最下面的vm.$data 是reduce方法遍历的初始值。也就是 data 。

因为data:{ messge:{ a:‘hello.world‘  }   }.这样的编译,元素节点就可以编译出来了,可以将data的值编译到元素节点上了。

  

接下来编译文本节点,那文本节点,我们首先获取文本节点里的值,然后利用正则的test找{{ a }} , 和之前的元素节点一样,执行对应的函数。,执行对应的行数。这里第86-90 可以先不管,不过这里的textVal和上面的getVal 函数不一样,首先是需要将符合条件的元素里的变量取出来  也就是 {{   a  }}里的a ,argments[1] 就是a变量  。 在考虑到对象嵌套,就执行上面的getVal。然后就可以将data里的值替换到文本里了。

这样元素节点和文本节点都编译完成了。然后将整个虚拟节点丢回dom树里去 。MVVM的编译就结束了

第二步:数据劫持,函数很少。但比较绕.这里执行observe,利用递归遍历,将data里的键值对全部拿出来处理,执行defineReactive函数,这里18行可以先不看。 看下面的最重点的Object.defineProperty()。这里要传入劫持的对象,劫持的键,以及回调函数。这里回调函数里俩个参数在下图。

然后,get函数是取值是做对应的操作,set函数是设置值做对应的操作。至此数据劫持就完成了

第三步:watcher 监察者 ,一旦变化执行对应的操作。也就是将模板编译和数据劫持俩个函数联系在一起。有衔接。

这里创建watcher类,将需要的参数获取。 vm是实例,expr是值,cb是回调函数callback。watcher实例里的value = get方法的返回值,value执行一次嵌套处理返回。这里监察者作用主要是 一 更新值,二是执行callback回调函数cb。三将自己的实例,放入dep的target里。那么watcher监察者就制作好了。

最后的连接部分,首先data里的每个属性值都被加上了set和get

获取值

在最开始编译的时候,编译节点的文本节点处理和元素节点处理的时候执行watcher函数,在watcher函数里的get函数中将 watcher函数自己放入dep的target中。然后也在访问值的时候,则会执行get函数,将 每个watcher放入dep数组中 。

修改值

在修改值的时候,会触发Observer.js 的defineProperty的set函数,set函数里比较新的值和旧的值,value是编译时候的值,newValue是set函数的第一个参数,也就是修改后的新值 。   将俩者比较,如果不同,就执行Dep构造函数的notify函数。notify则会遍历全部存在的dep数组里的watcher的update方法。在watcher的update方法中,比较值的不同,如果不同就则执行回调函数,将视图更新。这个回调函数是嵌套在处理文本节点和元素节点的方法里。

 

v-model的双向绑定

至于v-model的双向绑定,其实是绑定输入框的输入事件。将输入事件新的值赋值给input节点的value值,然后值的改变,执行set函数,将视图改变。视图的改变,会执行wacther的回调函数,文本节点也会重新赋值。

这就是mvvm响应式原理的实现,如果有残缺讲不清楚的地方,欢迎指出。谢谢。

原文地址:https://www.cnblogs.com/At867604340/p/12366176.html

时间: 2024-11-01 11:14:17

手写实现vue的MVVM响应式原理的相关文章

Vue中的响应式原理

Vue最独特的特性之一,是其非侵入性的响应式系统. 响应式原理:数据变,页面变 Vue如何追踪变化 当把一个普通的JS对象传入Vue实例作为data选项时,Vue将遍历此对象的所有属性,并使用Object.defineProperty把这些属性全部转为getter/setter.Object.defineProperty是ES5中一个无法shim的特性,这也是Vue不支持IE8以及更低版本浏览器的原因. Object.defineProperty(obj, prop, descriptor)//

Vue.js响应式原理

本文和大家分享的主要是Vue.js 响应式原理相关内容,一起来看看吧,希望对大家 学习Vue.js有所帮助. 关于Vue.js Vue.js 是一款 MVVM 框架,上手快速简单易用,通过响应式在修改数据的时候更新视图. Vue.js 的响应式原理依赖于 Object.defineProperty  ,尤大大在Vue.js 文档中就已经提到过,这也是 Vue.js 不支持 E8 以及更低版本浏览器的原因. Vue 通过设定对象属性的  setter/getter  方法来监听数据的变化,通过 g

Vue 数据响应式原理

Vue 数据响应式原理 Vue.js 的核心包括一套"响应式系统"."响应式",是指当数据改变后,Vue 会通知到使用该数据的代码.例如,视图渲染中使用了数据,数据改变后,视图也会自动更新. 举个简单的例子,对于模板: {{ name }} 创建一个 Vue 组件: var vm = new Vue({ el: '#root', data: { name: 'luobo' } }) 代码执行后,页面上对应位置会显示:luobo. 如果想改变显示的名字,只需要执行:

Vue源码之响应式原理(个人向)

浅谈响应式原理 关于响应式原理,其实对于vue这样一个大项目来说肯定有很多细节和优化的地方,在下水平精力有限,不能一一尝试探索,本文仅以将响应式的大致流程个人向的梳理完毕为目的. 对于响应式主要分为三大部分来分析,1.响应式对象:2.依赖收集:3.派发更新. 最后将是个人的分析. 1.响应式对象 (Object.defineProperty) 我们先从初始化数据开始,再介绍几个比较核心的方法. 1.1.initState 文件位置:src/core/instance/state.js 在Vue的

深度解析 Vue 响应式原理

深度解析 Vue 响应式原理 该文章内容节选自团队的开源项目 InterviewMap.项目目前内容包含了 JS.网络.浏览器相关.性能优化.安全.框架.Git.数据结构.算法等内容,无论是基础还是进阶,亦或是源码解读,你都能在本图谱中得到满意的答案,希望这个面试图谱能够帮助到大家更好的准备面试. Vue 初始化 在 Vue 的初始化中,会先对 props 和 data 进行初始化 Vue.prototype._init = function(options?: Object) { // ...

Vue.js学习 Item12 – 内部响应式原理探究

深入响应式原理 大部分的基础内容我们已经讲到了,现在讲点底层内容.Vue.js 最显著的一个功能是响应系统 —— 模型只是普通对象,修改它则更新视图.这让状态管理非常简单且直观,不过理解它的原理也很重要,可以避免一些常见问题.下面我们开始深挖 Vue.js 响应系统的底层细节. 如何追踪变化 把一个普通对象传给 Vue 实例作为它的 data 选项,Vue.js 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter.这是 ES5 特性,不能打补丁

深入Vue响应式原理

深入Vue.js响应式原理 一.创建一个Vue应用 new Vue({ data() { return { name: 'yjh', }; }, router, store, render: h => h(App), }).$mount('#app'); 二.实例化一个Vue应用到底发生了什么? this._init() callHook(vm, 'beforeCreate') observe(vm._data) vm._data = vm.$options.data() proxy(vm, _

vue响应式原理解析

# Vue响应式原理解析 首先定义了四个核心的js文件 - 1. observer.js 观察者函数,用来设置data的get和set函数,并且把watcher存放在dep中 - 2. watcher.js 监听者函数,用来设置dep.target开启依赖收集的条件,和触发视图的更新函数 - 3. compile.js 编译者函数,用来编译模版和实例化 watcher 函数 - 4. index.js 入口文件 注意dep函数就是一个壳子,用来存放watcher和触发watcher更新的 首先从

vue数据响应式原理

vue2.0数据响应式原理 对象 Obect.defineproperty 定义对象的属性mjm defineproperty 其实不是核心的为一个对象做数据双向绑定,而是去给对象做属性标签,设置一系列操作权限,只不过属性里的get和set实现了响应式 var ob = { a: 1, b: 2 } //1-对象 2-属性 3-对于属性的一系列配置 Object.defineProperty(ob, 'a' , { //a对象则是ob的绝对私有属性,,默认都是true writable: fal