文章目录

  • #1.vue的强大之处不必细说,vue的核心v-model的实现原理,网上都有很多。但是真正自己实现双向绑定,mvvm的源码却几乎没见过。
    • #1.2本人根据源码的解读,理解,以及借鉴网上的视频教程,手写一份mvvm,希望能帮助更多的vue学习者。
  • #2.先看成果
  • #3.实现原理
  • #3.1.数据劫持(observe)+模板编译(compile)+数据监听(watch)+发布订阅(Dep)
  • #4.源码
  • #4.1mvvm(架构)
  • #4.2模板编译(conpile)
  • #4.3数据劫持(observe)+发布订阅(dep)
  • #4.4mvvm架构
  • #5.使用方法
  • #6.开发不易,点个赞吧。希望一键三连,

#1.vue的强大之处不必细说,vue的核心v-model的实现原理,网上都有很多。但是真正自己实现双向绑定,mvvm的源码却几乎没见过。

#1.2本人根据源码的解读,理解,以及借鉴网上的视频教程,手写一份mvvm,希望能帮助更多的vue学习者。

#2.先看成果

#3.实现原理

#3.1.数据劫持(observe)+模板编译(compile)+数据监听(watch)+发布订阅(Dep)

#4.源码

#4.1mvvm(架构)

class MVVM{constructor(optins){//1.先将传入的参数进行挂载this.$el=optins.el;this.$data=optins.data;//2.进行模板编译if(this.$el){//先进行数据劫持new Observe(this.$data);new Compile(this.$el,this);}}
}

#4.2模板编译(conpile)

class Compile{constructor(el,vm) {             //el挂载名称,vm 是 WrM实例this.el=this.isElememntNode(el)?el:document.querySelector(el);    //进行判断,挂载节点this.vm=vm;if(this.el){                ////1.首先取出节点,获取到节点let fragment=this.node2fragment(this.el)//2.编译=>提取想要的元素节点 w-model和 文本节点 {{}}this.compile(fragment);//2.将节点放入到文档碎片中(内存)  fragment//3.将编译好的fragment放回到真实dom中this.el.appendChild(fragment);}}   /* 辅助方法*///1.判断是不是节点isElememntNode(node){return  node.nodeType===1;}//判断元素节点是否包含w-指令isDirective(attrName){return attrName.includes('w-');}/*核心方法 *///1.将dom节点移入内存中node2fragment(el){     //将el中的内容全部放到内存中let fragment=document.createDocumentFragment();//开辟内存存储空间let firstChild;      //定义为第一个节点                        while(firstChild= el.firstChild){              //每次将取到的第一个节点放入内存fragment.appendChild(firstChild)}return fragment;}//2.进行编译compile(fragment){//递归let nodeChild=fragment.childNodes;Array.from(nodeChild).forEach(node=>{if(this.isElememntNode(node)){  //如果是元素,证明里面可能嵌套子节点,递归//元素节点//编译元素this.compileElememnt(node);this.compile(node);}else{//文本节点//编译文本this.compileText(node);}})}//2.1编译元素compileElememnt(node){//带有 v-model 等指令的let attrs=node.attributes;  //取出当前元素的属性Array.from(attrs).forEach(attr=>{//判断属性名字是否包含v-let attrName=attr.name;if(this.isDirective(attrName)){  //取值,放到节点的值中let expr=attr.value;//let type=attrName.slice(2);// node this.vm.$data exprcompileUtile[type](node,this.vm,expr);}})}//2.2文本编译compileText(node){//有{{}}let expr = node.textContent; //取文本中的节点let reg= /\{\{([^}]+)\}\}/g  //正则表达式,匹配{{}}中的内容if(reg.test(expr)){// node this.vm.$data exprcompileUtile['text'](node,this.vm,expr);}}
}
//编译工具 compileUtile
compileUtile={//取值操作getVal(vm,expr){expr=expr.split('.');     //分割成数组return  expr.reduce((prev,next)=>{ //收敛return prev[next];},vm.$data)},getTextVal(vm,expr){return  expr=expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{   return this.getVal(vm,arguments[1]);})},text(node,vm,expr){  //文本处理let ex=expr; let updatafn=this.updata['textUpdater'];let value=this.getTextVal(vm,expr);expr=expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{new watch(vm,arguments[1],()=>{updatafn && updatafn(node,this.getTextVal(vm,ex)); });})updatafn && updatafn(node,value); },set(vm,expr,value){expr= expr.split('.');return expr.reduce((prev,next,currnindex)=>{if(currnindex===expr.length-1){return prev[next]=value;}return prev[next];},vm.$data)},model(node,vm,expr){ //输入框处理let updatafn=this.updata['modelUpdater'];new watch(vm,expr,(newvalue)=>{updatafn && updatafn(node,this.getVal(vm,expr)); })node.addEventListener('input',(e)=>{let newValue=e.target.value;this.set(vm,expr,newValue);})updatafn && updatafn(node,this.getVal(vm,expr)); },updata:{//文本更新  {{}}textUpdater(node,value){node.textContent=value;},//输入框更新 w-modelmodelUpdater(node,value){node.value=value;}}
}

#4.3数据劫持(observe)+发布订阅(dep)

class Observe{constructor(data) {//开始数据劫持this.observe(data);   }observe(data){if(!data || typeof data !=='object')//判断是否为对象类型{return;}else{//开始劫持Object.keys(data).forEach(key=>{this.definReacative(data,key,data[key]);this.observe(data[key]);        //深度递归});}}//定义响应式definReacative(obj,key,value){let that=this;let dep=new Dep();Object.defineProperty( obj,key,{enumerable:true,      //是否可枚举configurable:true,    get(){Dep.target && dep.addSub(Dep.target);return value;},set(newValue){if(newValue!=value){that.observe(newValue);   value=newValue; dep.notify();}}});}
}//发布订阅
class Dep{constructor(){this.subs=[]  //设置订阅数组}addSub(watch){this.subs.push(watch);   //添加到数组中  }notify(){this.subs.forEach(watch=>watch.updata()); //发布}
}

#4.4mvvm架构

class MVVM{constructor(optins){//1.先将传入的参数进行挂载this.$el=optins.el;this.$data=optins.data;//2.进行模板编译if(this.$el){//先进行数据劫持new Observe(this.$data);new Compile(this.$el,this);}}
}

#5.使用方法

引入上述4个文件
w-model (数据的双向绑定) {{}} (插值表达式)

#6.开发不易,点个赞吧。希望一键三连,

vue源码深入解读MVVM(视图模板引擎),你真的了解双向绑定(v-model),数据劫持(observe),发布订阅模式吗?带你手鲁mvvm引擎。源码奉上(详细注释)!相关推荐

  1. vue面试核心,双向数据绑定,数据代理,数据劫持,发布订阅,数据编译,看这个demo就够了

    vue面试核心,双向数据绑定,数据代理,数据劫持,发布订阅,数据编译,看这个demo就够了. 不在怕面试官,你给我怼过去.复制到html文档,浏览器运行即可. mvvm原理剖析 {{singer}}

  2. 从发布-订阅模式到Vue响应系统

    概念 发布-订阅模式又称为观察者模式,它定义的是一种一对多的依赖关系,当一个状态发生改变的时候,所有以来这个状态的对象都会得到通知. 生活中的发布-订阅模式 上面事发布-订阅模式的一个比较正式的解释, ...

  3. vue 发布订阅模式

    vue 发布订阅模式 为什么要使用发布订阅模式 vue 中数据反映到视图中的方式主要是采取声明式渲染+模板编译 声明式渲染: 例如v-mdoe等指令的形式渲染 模板编译原理: 简单来讲就是获取app下 ...

  4. 深入Vue原理_全面剖析发布订阅模式

    文章目录 发布订阅模式优化 优化思路思考 理解发布订阅模式(自定义事件) 收集更新函数 触发更新函数 6.5 总结 总结 写在最后 本期推荐 欢迎各位小伙伴们! 为大家推荐一款刷题神奇哦 点击链接访问 ...

  5. 前端Vue之发布订阅模式

    目录 1.什么是发布订阅模式 2.实现简单的发布订阅 3.收集更新函数 4.触发更新函数 5.总结 一个响应式数据可能会有多个视图部分都需要依赖,也就是响应式数据变化之后,需要执行的更新函数可能不止一 ...

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

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

  7. 发布订阅模式 仿写Vue事件监听手写js实现

    Vue组件中,可以使用 $emit,$on,$off 分别来分发.监听.取消监听事件实现组件通信,比较方便: 最近空闲时间手撸代码实现了发布订阅模式,可以进行组件通信. 话不多说,直接上代码 /*** ...

  8. Vue父组件访问子组件属性和方法、父子组件双向绑定(两种方法)

    Vue父组件访问子组件属性和方法.父子组件双向绑定(两种方法) 1. 使用vue-cli创建项目 目录结构如下图: 2. 编写代码 src/components/HelloWorld.vue < ...

  9. JavaScript 设计模式之发布-订阅模式(上)

    什么是发布订阅模式? 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.在JavaScript开发中,我们一般用事件模型来替 ...

最新文章

  1. the different between Const and Static in C++
  2. Java中switch对整型/字符型/字符串型具体实现细节
  3. [c/c++] programming之路(28)、结构体存储和内存对齐+枚举类型+typedef+深拷贝和浅拷贝...
  4. 0、elasticsearch前言
  5. devops对大会的看法_哪条建议对您在DevOps的职业生涯影响最大?
  6. 怎么在html中加入相对链接,html直接跳转超链接 html中怎么实现同一页面超级链接...
  7. Oracle常见操作和命令
  8. 嵌入式基础面八股文——进程与线程的基本概念(1)
  9. 初创团队配置_咨询整理(熊猫TV首席架构师)
  10. 短信平台接口怎么选择?看这一篇就够了
  11. 采用H264编码视频的参数设定
  12. windows 区域截屏以及延迟截屏
  13. 图解Janusgraph系列-查询图数据过程源码分析
  14. 解决问题(七)——jsf+spring+hibernate整合(一)
  15. RuntimeError: iter() is only supported inside of tf.function or when eager execution is enabled.
  16. Qt实现思维导图功能(三)
  17. 氧化三甲胺(TMAO)及其相关代谢物定量
  18. java手机ua_通过UA判断手机的类型
  19. 计算机音乐植物大战僵尸泳池,植物大战僵尸 泳池关卡 背景音乐
  20. php泥浆配比,农村自建房混凝土配比是多少?

热门文章

  1. 智能社JS学习笔记(一)
  2. 当狗兄弟遇到小猫咪,又怂又爱挑衅,搞笑配音让短视频更有趣
  3. 计算机主机的拆卸的注意事项,如何进行电脑主机除尘及板卡维护
  4. 哎呦,最近流行校内体哦--每个用 power shell 的程序员,上辈子都是折翼的天使
  5. python运算符和基本数据类型你了解吗?
  6. 【每日早报】2019/07/31
  7. Hack The Box - Three(新手友好)
  8. 配置fabric(超级记账本)时docker安装教程
  9. Qt+VLC播放多个视频的Demo
  10. fedora 25 安装字体