摘要:實(shí)際上就是這里要表現(xiàn)的其實(shí)是上下文的替換功能。這就是死模板了,而所謂的活模板,就是這里面的數(shù)據(jù)全部經(jīng)過了數(shù)據(jù)的綁定會自動找到當(dāng)前的上下文,來綁定數(shù)據(jù)。最后顯示出來的就是活模板,也就是經(jīng)過數(shù)據(jù)綁定的模板。
這篇文章是我兩年前在博客園寫的,現(xiàn)在移植過來,不過Angular 1.x 在國內(nèi)用的人已經(jīng)不多了,希望能幫助到有需要的人
在 angular 的服務(wù)中,有一些服務(wù)你不得不去了解,因?yàn)樗梢哉f是 ng 的核心,而今天,我要介紹的就是 ng 的兩個核心服務(wù),$parse 和 $compile 。其實(shí)這兩個服務(wù)講的人已經(jīng)很多了,但是 100 個讀者就有 100 個哈姆雷特,我在這里講講自己對于他們兩個服務(wù)的理解。
大家可能會疑問,$eval 呢,其實(shí)他并不是一個服務(wù),他是 scope 里面的一個方法,并不能算服務(wù),而且它也基于 parse 的,所以只能算是$parse 的另一種寫法而已,我們看一下 ng 源碼中 $eval 的定義是怎樣的就知道了
$eval: function(expr, locals) { return $parse(expr)(this, locals); }
相信看完源碼大家就明白了吧,好了,現(xiàn)在就開始兩種核心服務(wù)的講解了,如果感覺我說的不對的話,歡迎在評論區(qū)或者私聊指出,免得禍害其他讀者。
再講這兩個服務(wù)的時(shí)候,我要先講一個在本貼的概念:上下文
我相信,很多人都聽過這個“上下文”,但是可能有點(diǎn)模糊,在我這里給大家解釋解釋看看大家接不接受這個說法。
還記得 angular 的數(shù)據(jù)綁定嗎?比如:我現(xiàn)在有個有個叫 TestCtrl 的控制器,他的內(nèi)容如下:
.controller("TestCtrl", function($scope) { $scope.test = "Boo!!!" })
而在 html 中我們的代碼是這樣的
{{test}}
那么,大家不用想都知道結(jié)果了,頁面上肯定會顯示 Boo!!! 的字樣。
但是如果我刪掉 ng-controller 的指令呢?也就是我沒有在 html 申明控制器,你直接綁定{{test}}會如何呢?
結(jié)果只有一個,那就是頁面啥都沒有(ps:因?yàn)槟闵昝髁?ng-app)。講到這里大家明白了嗎?
控制器就相當(dāng)于一個上下文的容器,真正的上下文其實(shí)是 $scope ,當(dāng)頁面綁定 test,如果申明了控制器,當(dāng)前上下文就是控制器里面的$scope ,ng 會去找一下你這個控制器的上下文$scope 有沒有 test,如果有,他當(dāng)然就顯示出來了,但是你不申明控制器的時(shí)候呢?他的上下文容器就是 ng-app 了,那么他真正的上下文就是 $rootScope ,這個時(shí)候他就會尋找 $rootScope 有沒有 test。
好了,上下文的概念已經(jīng)講完了,其實(shí)挺容易理解的,基本上和 this 非常相似
那么言歸正傳,我們開始講 $parse,首先我們要看的是 ng 的 API 文檔
var getter = $parse("user.name"); var setter = getter.assign; var context = {user:{name:"angular"}}; var locals = {user:{name:"local"}}; expect(getter(context)).toEqual("angular"); setter(context, "newValue"); expect(context.user.name).toEqual("newValue"); expect(getter(context, locals)).toEqual("local");
大家看到的是 ng 文檔里面對于$parse 服務(wù)性價(jià)比最高的幾行代碼,
getter 和 setter 就是大家所熟知的 get 方法和 set 方法了,context 和 locals 僅僅是 json 對象而已,目的就是模擬上下文關(guān)系
大家看到的下面四個語句最終都能通過測試,現(xiàn)在我們一個個來分析,分析之前我要解釋一遍什么叫 $parse
$parse 服務(wù)其實(shí)就是一種解析表達(dá)式的功能,就像 ng-model=“test”,你在 html 中寫這個東西誰知道你 ng-model=“test”中,其實(shí)你想綁定的是當(dāng)前控制器(上下文容器)中 scope(上下文)中的 test 里面的值,ng 就是通過$parse 服務(wù)去幫助你解析這個表達(dá)式的,所以在調(diào)用$parse 服務(wù)的時(shí)候你需要傳遞上下文對象,讓 ng 知道你是要去哪里的 scope(上下文)去找你這個 test。
所以我們看到第一行測試代碼是這樣的:
getter(context)).toEqual("angular") //實(shí)際上就是 $parse("user.name")(context)
在這個 context 就是上下文,他能返回“angular“這個字符串的原理就是經(jīng)過這三步的:
獲取當(dāng)前的表達(dá)式 user.name
獲取當(dāng)前的上下文對象{user:{name:"angular"}}
在上下問對象中尋找表達(dá)式,最終獲得“angular“這個字符創(chuàng)
所以這句測試代碼是成功的。
我們看第二個方法 setter 方法
setter(context, "newValue"); //實(shí)際上就是 $parse("user.name").assign(context, "newValue") expect(context.user.name).toEqual("newValue"); //測試數(shù)據(jù)上下文的值是否被改變 這里的 setter 方法其實(shí)是改變值得方法
獲取當(dāng)前的表達(dá)式 user.name
獲取當(dāng)前的上下文對象{user:{name:"angular"}}
改變表達(dá)式中的值,將上下文對象編程{user:{name:"newValue"}}
于是上下文對象發(fā)生了改變,重新用 getter 方法去獲取表達(dá)式的時(shí)候,上下文已經(jīng)從 {user:{name:"angular"}} --> {user:{name:"newValue"}},最后獲取的表達(dá)式的值自然就是 newValue 了,所以測試代碼也是通過的。
expect(getter(context, locals)).toEqual("local");//實(shí)際上就是$parse("user.name")(context, locals)
這里要表現(xiàn)的其實(shí)是上下文的替換功能。
在 getter 的方法中我們不僅可以選擇第一個上下文,但是如果我們傳遞了第二個參數(shù),那么第一個上下文就會被第二個上下文覆蓋,注意是覆蓋.
獲取當(dāng)前的表達(dá)式 user.name
獲取當(dāng)前的上下文對象{user:{name:"angular"}}
覆蓋當(dāng)前的上下文{user:{name:"local"}}
獲取解析之后表達(dá)式的值
重新回到 $eval 這個地方,我們看待 $eval 源碼中可以看出$eval 只有 get 功能,而沒有 set 功能,但是有些時(shí)候我們可以選擇傳遞第二個上下文,來達(dá)到修改值得效果。
在這里 $parse 服務(wù)就已將說完了,接下來就是 $compile
如果你了解了 $parse 的概念之后,我想 $compile 也差不多理解了,其實(shí)和 $parse 很像。但是他是解析一段 html 代碼的,他的功能就是將死模板變成活模板,也是指令的核心服務(wù)。
比如你有一段 html 代碼 {{test}}
,如果你將這段代碼直接放在 html 代碼里面,它所呈現(xiàn)的內(nèi)容是怎樣的我不說大家也應(yīng)該懂。這就是死模板了,而所謂的活模板,就是這里面的數(shù)據(jù)全部經(jīng)過了數(shù)據(jù)的綁定 {{test}}會自動找到當(dāng)前的上下文,來綁定數(shù)據(jù)。最后顯示出來的 就是活模板,也就是經(jīng)過數(shù)據(jù)綁定的模板。
$compile("死模板")(上下文對象),這樣就將死模板編程了活模板,你就可以對這段活的 html 代碼做操作了,例如增加到當(dāng)前節(jié)點(diǎn),等等。
但是在指令中,她會返回兩個函數(shù) pre-link 和 post-link
第一個執(zhí)行的是 pre-link,它對于同一個指令的遍歷順序是從父節(jié)點(diǎn)到子節(jié)點(diǎn)的遍歷,在這個階段,dom 節(jié)點(diǎn)還沒有穩(wěn)定下來,無法做一些綁定事件的操作,但是我們可以在這里進(jìn)行一些初始化數(shù)據(jù)的處理。
第二個執(zhí)行的是 post-link,也就是我們常說的 link 函數(shù),他是從子節(jié)點(diǎn)到父節(jié)點(diǎn)遍歷的,在這個階段,DOM 節(jié)點(diǎn)已經(jīng)穩(wěn)定下來了,我們一般會在這里進(jìn)行很多的操作。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/92912.html
摘要:我們提取變量的目的就是為了在函數(shù)中生成相應(yīng)的變量賦值的字符串便于在函數(shù)中使用,例如這樣的話,只需要在調(diào)用這個匿名函數(shù)的時(shí)候傳入對應(yīng)的即可獲得我們想要的結(jié)果了。 showImg(https://segmentfault.com/img/bVSspq?w=4000&h=2670); 題圖:Vincent Guth 注:本文所有代碼均可在本人的個人項(xiàng)目colon中找到,本文也同步到了知乎專欄...
摘要:組件還包含數(shù)據(jù)事件的輸入與輸出,生命周期鉤子和使用單向數(shù)據(jù)流以及從父組件上獲取數(shù)據(jù)的事件對象備份。 說明:參照了Angular1.x+es2015的中文翻譯,并將個人覺得不合適、不正確的地方進(jìn)行了修改,歡迎批評指正。 架構(gòu),文件結(jié)構(gòu),組件,單向數(shù)據(jù)流以及最佳實(shí)踐 來自@toddmotto團(tuán)隊(duì)的實(shí)用編碼指南 Angular 的編碼風(fēng)格以及架構(gòu)已經(jīng)使用ES2015進(jìn)行重寫,這些在Angul...
摘要:他們即不是指令,也不應(yīng)該使用組件代替指令,除非你正在用控制器升級模板指令,組件還包含數(shù)據(jù)事件的輸入與輸出,生命周期鉤子和使用單向數(shù)據(jù)流以及從父組件上獲取數(shù)據(jù)的事件對象。 showImg(https://segmentfault.com/img/bVynsJ); 關(guān)鍵詞 架構(gòu), 文件結(jié)構(gòu), 組件, 單向數(shù)據(jù)流以及最佳實(shí)踐 來自 @toddmotto 團(tuán)隊(duì)的編碼指南 Angular 的編碼...
摘要:屬性為時(shí),指示優(yōu)先級小于當(dāng)前指令的指令都不執(zhí)行,僅執(zhí)行到本指令。 作者:心葉時(shí)間:2018-04-22 10:58 一:自定義指令常用模板 下面是大致的說明,不是全面的,后面來具體說明一些沒有提及的細(xì)節(jié)和重要的相關(guān)知識: angular.module(yelloxingApp, []).directive(uiDirective, function() { return { ...
摘要:具體可以查看抽象語法樹。而則是帶緩存的編譯器,同時(shí)以及函數(shù)會被轉(zhuǎn)換成對象。會用正則等方式解析模板中的指令等數(shù)據(jù),形成語法樹。是將語法樹轉(zhuǎn)化成字符串的過程,得到結(jié)果是的字符串以及字符串。里面的節(jié)點(diǎn)與父節(jié)點(diǎn)的結(jié)構(gòu)類似,層層往下形成一棵語法樹。 寫在前面 因?yàn)閷ue.js很感興趣,而且平時(shí)工作的技術(shù)棧也是Vue.js,這幾個月花了些時(shí)間研究學(xué)習(xí)了一下Vue.js源碼,并做了總結(jié)與輸出。 文...
閱讀 2514·2021-11-22 13:53
閱讀 1193·2021-09-22 16:06
閱讀 1441·2021-09-02 15:21
閱讀 1981·2019-08-30 15:55
閱讀 3192·2019-08-29 11:19
閱讀 1973·2019-08-26 13:23
閱讀 1002·2019-08-23 18:23
閱讀 1811·2019-08-23 16:06