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.pswp.cn/news/457601.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457601.shtml
英文地址,請注明出處:http://en.pswp.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.”…