vue大括号里接受一个函数_vue源码探究(第四弹)

vue源码探究(第四弹)

结束了上一part的数据代理,这一部分主要讲讲vue的模板解析,感觉这个有点难理解,而且内容有点多,hhh。

模板解析

废话不多说,先从简单的入手。

按照之前的套路,先举一个例子 :

<div id="test"><p>{{name}}</p>
</div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">new MVVM({el: '#test',data: {name: '喵喵喵'}})// 这时候,我们的页面还是渲染出 喵喵喵
</script>

接下来讲讲内部的相关实现:

我们的MVVM中的构造函数中有什么东西,可以解析我们的模板呢?

// 创建一个用来编译模板的compile对象
this.$compile = new Compile(options.el || document.body, this)

什么是Compile?

一行一行注释着解读

function Compile(el, vm) {// 保存vmthis.$vm = vm;// 保存el元素this.$el = this.isElementNode(el) ? el : document.querySelector(el);// 如果el元素存在if (this.$el) {// 1. 取出el中所有子节点, 封装在一个framgment对象中// 这里的node2Fragment 就是将node -> 放入 Fragment中,documentFragment将node进行批量处理this.$fragment = this.node2Fragment(this.$el);// 2. 编译fragment中所有层次子节点this.init();// 3. 将fragment添加到el中this.$el.appendChild(this.$fragment);}
}Compile.prototype = {node2Fragment: function (el) {var fragment = document.createDocumentFragment(),child;// 将原生节点拷贝到fragmentwhile (child = el.firstChild) {fragment.appendChild(child);}return fragment;},init: function () {// 编译fragmentthis.compileElement(this.$fragment);},compileElement: function (el) {// 得到所有子节点var childNodes = el.childNodes,// 保存compile对象me = this;// 遍历所有子节点[].slice.call(childNodes).forEach(function (node) {// 得到节点的文本内容var text = node.textContent;// 正则对象(匹配大括号表达式)var reg = /{{(.*)}}/;  // {{name}}// 这里提出一个问题,为什么这里的正则匹配要用/{{(.*)}}/,而不是/{{.*}}/呢?// 其实/{{.*}}/就可以匹配到{{xxx}},这里加一个()的意义是,用于.$1,来取得{{}}中的值,eg:name// 如果是元素节点if (me.isElementNode(node)) {// 编译元素节点的指令属性me.compile(node);// 如果是一个大括号表达式格式的文本节点} else if (me.isTextNode(node) && reg.test(text)) {// 编译大括号表达式格式的文本节点me.compileText(node, RegExp.$1); // RegExp.$1: 表达式   name}// 如果子节点还有子节点if (node.childNodes && node.childNodes.length) {// 递归调用实现所有层次节点的编译me.compileElement(node);}});},compile: function (node) {// 得到所有标签属性节点var nodeAttrs = node.attributes,me = this;// 遍历所有属性[].slice.call(nodeAttrs).forEach(function (attr) {// 得到属性名: v-on:clickvar attrName = attr.name;// 判断是否是指令属性if (me.isDirective(attrName)) {// 得到表达式(属性值): testvar exp = attr.value;// 得到指令名: on:clickvar dir = attrName.substring(2);// 事件指令if (me.isEventDirective(dir)) {// 解析事件指令compileUtil.eventHandler(node, me.$vm, exp, dir);// 普通指令} else {// 解析普通指令compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);}// 移除指令属性node.removeAttribute(attrName);}});},compileText: function (node, exp) {// 调用编译工具对象解析compileUtil.text(node, this.$vm, exp);},isDirective: function (attr) {return attr.indexOf('v-') == 0;},isEventDirective: function (dir) {return dir.indexOf('on') === 0;},isElementNode: function (node) {return node.nodeType == 1;},isTextNode: function (node) {return node.nodeType == 3;}
};// 指令处理集合
var compileUtil = {// 解析: v-text/{{}}text: function (node, vm, exp) {this.bind(node, vm, exp, 'text');},// 解析: v-htmlhtml: function (node, vm, exp) {this.bind(node, vm, exp, 'html');},// 解析: v-modelmodel: function (node, vm, exp) {this.bind(node, vm, exp, 'model');var me = this,val = this._getVMVal(vm, exp);node.addEventListener('input', function (e) {var newValue = e.target.value;if (val === newValue) {return;}me._setVMVal(vm, exp, newValue);val = newValue;});},// 解析: v-classclass: function (node, vm, exp) {this.bind(node, vm, exp, 'class');},// 真正用于解析指令的方法bind: function (node, vm, exp, dir) {/*实现初始化显示*/// 根据指令名(text)得到对应的更新节点函数// 取到一个object的属性,有2个方法,一个是obj. 一个是obj[]// 当我们要取得属性是一个变量的时候,使用obj[]var updaterFn = updater[dir + 'Updater'];// 如果存在调用来更新节点updaterFn && updaterFn(node, this._getVMVal(vm, exp));// 创建表达式对应的watcher对象new Watcher(vm, exp, function (value, oldValue) {/*更新界面*/// 当对应的属性值发生了变化时, 自动调用, 更新对应的节点updaterFn && updaterFn(node, value, oldValue);});},// 事件处理eventHandler: function (node, vm, exp, dir) {// 得到事件名/类型: clickvar eventType = dir.split(':')[1],// 根据表达式得到事件处理函数(从methods中): test(){}fn = vm.$options.methods && vm.$options.methods[exp];// 如果都存在if (eventType && fn) {// 绑定指定事件名和回调函数的DOM事件监听, 将回调函数中的this强制绑定为vmnode.addEventListener(eventType, fn.bind(vm), false);}},// 得到表达式对应的value_getVMVal: function (vm, exp) {// 这里为什么要forEach呢?// 如果你的exp是a.b.c.c.d呢 就需要forEach 如果只是一层 当然不需要遍历啦var val = vm._data;exp = exp.split('.');exp.forEach(function (k) {val = val[k];});return val;},_setVMVal: function (vm, exp, value) {var val = vm._data;exp = exp.split('.');exp.forEach(function (k, i) {// 非最后一个key,更新val的值if (i < exp.length - 1) {val = val[k];} else {val[k] = value;}});}
};// 包含多个用于更新节点方法的对象
var updater = {// 更新节点的textContenttextUpdater: function (node, value) {node.textContent = typeof value == 'undefined' ? '' : value;},// 更新节点的innerHTMLhtmlUpdater: function (node, value) {node.innerHTML = typeof value == 'undefined' ? '' : value;},// 更新节点的classNameclassUpdater: function (node, value, oldValue) {var className = node.className;className = className.replace(oldValue, '').replace(/s$/, '');var space = className && String(value) ? ' ' : '';node.className = className + space + value;},// 更新节点的valuemodelUpdater: function (node, value, oldValue) {node.value = typeof value == 'undefined' ? '' : value;}
};

最后

未完待续...
接下来,还有一个更有趣的东西

下一章继续~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457601.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类级别的分装 ---四种访问级别

privateprivate成员为类的私有性质&#xff0c;仅有类本身和友元可以访问&#xff1b;protected和private类似&#xff0c;区别于protected可以被该类所有派生类访问&#xff1b;publicpublic的成员可以被外界的所有客户代码直接访问published和public的区别仅在于published的成…

python自省与反射

DAY 5. python自省 这是很久之前写的&#xff0c;当时对自省和反射的概念没理解&#xff0c;学习Java以后多了一点理解&#xff0c;自省是获取对象的能力&#xff0c;反射是操纵对象的能力&#xff0c;python中使用getattr()和setattr()实现反射&#xff0c;而其他的则是自省&…

vb.net 窗体接收键盘事件_(十五)C#WinFrom自定义控件系列-键盘(二)

前提入行已经7,8年了&#xff0c;一直想做一套漂亮点的自定义控件&#xff0c;于是就有了本系列文章。本系列文章将讲解各种控件的开发及思路&#xff0c;欢迎各位批评指正。此系列控件开发教程将全部在原生控件基础上进行重绘开发&#xff0c;目标的扁平化、漂亮、支持触屏。如…

centos下cmake安装

步骤一、安装gcc等必备程序包&#xff08;已安装则略过此步&#xff0c;用gcc -v检测&#xff09; yum install -y gcc gcc-c make automake 步骤二、安装wget &#xff08;已安装则略过此步&#xff09; yum install -y wget 步骤三、获取CMake源码包 wget http://www.cmake.…

python 生成式,迭代器,生成器

DAY 6. 生成式,迭代器&#xff0c;生成器 6.1 生成式 6.1.1 列表生成式 list [index for index in range(10)]6.1.2 字典生成式 dict {zhangsan: 10,lisi: 12,wangwu: 18 } # 实现键值互换 dict {k:v for v,k in dict.items() if k > 12}6.1.3 集合生成式 # 100以内…

shell MAC 地址 校验

/**************************************************************************************** shell MAC 地址 校验* 说明&#xff1a;* 要对MAC地址进行校验&#xff0c;记录一下正则表达式写法&#xff0c;有些方法在PC上验证是可行的&…

移动端Web开发如何处理横竖屏

<!Doctype html> <html> <head> <meta charset"utf-8"> <meta id"viewport" name"viewport" content"widthdevice-width,initial-scale1.0;"> <title>横竖屏切换检测</title> <style ty…

恩智浦智能车大赛2020_内蒙古科技大学第九届智能车大赛校内公开赛总决赛

为了激发学生的创新意识&#xff0c;提高学生的动手能力&#xff0c;培养团队合作意识&#xff0c;秉承“实践源于真知&#xff0c;创新放飞梦想”的思想。2020年12月6日&#xff0c;内蒙古科技大学第九届智能车大赛总决赛如约而至。本次大赛有来自各院系的223支队伍报名参加了…

python格式化字符串的三种方法(%,format,f-string)

DAY 7. 格式化字符串 到目前为止&#xff0c;我所知道的&#xff0c;python格式化字符串有三种方法&#xff0c;第一是早期就有的%&#xff0c;其次是2.5之后的format(),还有就是3.6添加的f字符串调试 7.1 %格式化字符串 %格式化字符串是python最早的&#xff0c;也是能兼容…

Android App 优化之 ANR 详解

为了便于阅读, 应邀将Android App性能优化系列, 转移到掘金原创上来.掘金的新出的"收藏集"功能可以用来做系列文集了. 今天先来聊聊ANR. 1, 你碰到ANR了吗 在App使用过程中, 你可能遇到过这样的情况: 恭喜你, 这就是传说中的ANR. 1.1 何为ANR ANR全名Application Not…

微信高级群发接口正文乱码解决方案

content里面的内空如果含有html标签的话&#xff0c;需要对内容进行一下转义。如果里面含有style".."类似于这样的带""号的内容的话&#xff0c;就更需要注意了。 foreach ($news as &$item) {foreach ($item as $key > $val){if ($key content){$…

python *args和**kwargs以及序列解包

DAY 8. *args和**kwargs *args&#xff1a;多值元组&#xff0c;**kwargs多值字典&#xff0c;他们是python函数传参时两个特殊的参数&#xff0c;args和kwargs并不是强制的&#xff0c;但习惯使用这两个&#xff0c;如果在函数参数列表中声明了*args&#xff0c;则允许传递任…

解读直播连麦与点播加密

近年来&#xff0c;直播热潮持续升温。有需求就会有变革&#xff0c;直播的相关技术也在不断更新&#xff0c;为直播行业带来更好地服务。如&#xff1a;直播连麦与点播加密技术等。 直播连麦&#xff0c;即主播与连麦者通过互动直播中心进行实时互动&#xff0c;信息在云端混流…

血红蛋白判断access程序答案_普渡大学开发智能手机应用程序 帮助评估贫血症情况...

医生诊断贫血疾病的方法之一&#xff0c;就是通过观察患者的眼皮&#xff0c;判断眼皮是否发红&#xff0c;从而判断红细胞的数量。但对医生来说&#xff0c;面临的挑战是&#xff0c;这种简单的测试不够精确&#xff0c;无法不从患者身上抽取血样就能给出诊断。美国普渡大学的…

学习笔记:AC自动机

话说AC自动机有什么用......我想要自动AC机 AC自动机简介&#xff1a; 首先简要介绍一下AC自动机&#xff1a;Aho-Corasick automation&#xff0c;该算法在1975年产生于贝尔实验室&#xff0c;是著名的多模匹配算法之一。一个常见的例子就是给出n个单词&#xff0c;再给出一段…

python闭包和装饰器

DAY 9. 闭包和装饰器 9.1 闭包 闭包就是内部函数对外部函数作用域内变量的引用 可以看出 闭包是针对函数的&#xff0c;还有两个函数&#xff0c;内部函数和外部函数闭包是为了让内部函数引用外部函数作用域内的变量的 我们先写两个函数 def fun1():print("我是fun1&q…

学历是铜牌,能力是银牌,人脉是金牌,思维是王牌

有人工作&#xff0c;有人上学&#xff0c;大家千万不要错过这篇文章&#xff0c;能看到这篇文章也是一种幸运&#xff0c;真的受益匪浅&#xff0c;对我有很大启迪&#xff0c;这篇文章将会改变你我的一生&#xff0c;真的太好了&#xff0c;希望与有缘人分享&#xff0c;也希…

石头剪刀布python编程_《python核心编程第二版》练习题——游戏:石头剪刀布

习题里比较有意思的一个题目&#xff0c;实现石头剪刀布这个游戏&#xff0c;起初设计的时候走弯路了(主要时被习题里那个“尽量少用if判断”给整晕了)&#xff0c;想的太复杂&#xff0c;后来发现其实非常简单&#xff0c;完全可以不写if语句。还是枚举法&#xff1a;#! /usr/…

SpringMvc面试题

f-sm-1. 讲下SpringMvc和Struts1,Struts2的比较的优势 性能上Struts1>SpringMvc>Struts2 开发速度上SpringMvc和Struts2差不多,比Struts1要高f-sm-2. 讲下SpringMvc的核心入口类是什么,Struts1,Struts2的分别是什么 SpringMvc的是DispatchServlet,Struts1的是ActionServl…

python 鸭子类型

DAY 10. 鸭子类型 这个概念来源于美国印第安纳州的诗人詹姆斯惠特科姆莱利&#xff08;James Whitcomb Riley,1849-1916&#xff09;的诗句&#xff1a;”When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”…