angular1.x 中主要指令引见($eval,$parse和$compile)

这篇文章是我两年前在博客园写的,如今移植过来,不过Angular 1.x 在国内用的人已不多了,希望能协助到有须要的人

在 angular 的效劳中,有一些效劳你不得不去相识,由于他能够说是 ng 的中心,而本日,我要引见的就是 ng 的两个中心效劳,$parse$compile 。实在这两个效劳讲的人已许多了,然则 100 个读者就有 100 个哈姆雷特,我在这里讲讲本身关于他们两个效劳的邃晓。

人人能够会疑问,$eval 呢,实在他并非一个效劳,他是 scope 内里的一个要领,并不能算效劳,而且它也基于 parse 的,所以只能算是$parse 的另一种写法罢了,我们看一下 ng 源码中 $eval 的定义是怎样的就晓得了

$eval: function(expr, locals) {
  return $parse(expr)(this, locals);
}

置信看完源码人人就邃晓了吧,好了,如今就最先两种中心效劳的讲解了,假如觉得我说的不对的话,迎接在批评区或许私聊指出,以免祸患其他读者。

再讲这两个效劳的时刻,我要先讲一个在本贴的观点:上下文

我置信,许多人都听过这个“上下文”,然则能够有点隐约,在我这里给人人诠释诠释看看人人接不接受这个说法。

还记得 angular 的数据绑定吗?比方:我如今有个有个叫 TestCtrl 的控制器,他的内容以下:

.controller('TestCtrl', function($scope) {
  $scope.test = "Boo!!!"
})

而在 html 中我们的代码是如许的

  <body ng-controller="TestCtrl">
    {{test}}
  </body>

那末,人人不必想都晓得结果了,页面上肯定会显现 Boo!!! 的字样。

然则假如我删掉 ng-controller 的指令呢?也就是我没有在 html 说明控制器,你直接绑定{{test}}会怎样呢?

结果只要一个,那就是页面啥都没有(ps:由于你说清楚明了 ng-app)。讲到这里人人邃晓了吗?

控制器就相当于一个上下文的容器,真正的上下文实际上是 $scope ,当页面绑定 test,假如说清楚明了控制器,当前上下文就是控制器内里的$scope ,ng 会去找一下你这个控制器的上下文$scope 有无 test,假如有,他固然就显现出来了,然则你不说明控制器的时刻呢?他的上下文容器就是 ng-app 了,那末他真正的上下文就是 $rootScope ,这个时刻他就会寻觅 $rootScope 有无 test。

好了,上下文的观点已讲完了,实在挺轻易邃晓的,基本上和 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 效劳性价比最高的几行代码,

gettersetter 就是人人所熟知的 get 要领和 set 要领了,contextlocals 仅仅是 json 对象罢了,目标就是模仿上下文关联

人人看到的下面四个语句终究都能经由过程测试,如今我们一个个来剖析,剖析之前我要诠释一遍什么叫 $parse

$parse 效劳实在就是一种剖析表达式的功用,就像 ng-model=“test”,你在 html 中写这个东西谁晓得你 ng-model=“test”中,实在你想绑定的是当前控制器(上下文容器)中 scope(上下文)中的 test 内里的值,ng 就是经由过程$parse 效劳去协助你剖析这个表达式的,所以在挪用$parse 效劳的时刻你须要通报上下文对象,让 ng 晓得你是要去那里的 scope(上下文)去找你这个 test。

所以我们看到第一行测试代码是如许的:

getter(context)).toEqual('angular') //实际上就是 $parse('user.name')(context)

在这个 context 就是上下文,他能返回“angular“这个字符串的道理就是经由这三步的:

  1. 猎取当前的表达式 user.name
  2. 猎取当前的上下文对象{user:{name:’angular’}}
  3. 在上下问对象中寻觅表达式,终究取得“angular“这个字符创

所以这句测试代码是胜利的。

我们看第二个要领 setter 要领

setter(context, 'newValue');  //实际上就是 $parse('user.name').assign(context, 'newValue')
expect(context.user.name).toEqual('newValue');  //测试数据上下文的值是不是被转变  这里的 setter 要领实际上是转变值得要领
  1. 猎取当前的表达式 user.name
  2. 猎取当前的上下文对象{user:{name:’angular’}}
  3. 转变表达式中的值,将上下文对象编程{user:{name:’newValue’}}

因而上下文对象发生了转变,从新用 getter 要领去猎取表达式的时刻,上下文已从 {user:{name:'angular'}} --> {user:{name:'newValue'}},末了猎取的表达式的值天然就是 newValue 了,所以测试代码也是经由过程的。

expect(getter(context, locals)).toEqual('local');//实际上就是$parse('user.name')(context, locals)

这里要表现的实际上是上下文的替代功用。

在 getter 的要领中我们不仅能够挑选第一个上下文,然则假如我们通报了第二个参数,那末第一个上下文就会被第二个上下文掩盖,注重是掩盖.

  1. 猎取当前的表达式 user.name
  2. 猎取当前的上下文对象{user:{name:’angular’}}
  3. 掩盖当前的上下文{user:{name:’local’}}
  4. 猎取剖析以后表达式的值

从新回到 $eval 这个处所,我们对待 $eval 源码中能够看出$eval 只要 get 功用,而没有 set 功用,然则有些时刻我们能够挑选通报第二个上下文,来到达修正值得结果。

在这里 $parse 效劳就已将说完了,接下来就是 $compile

假如你相识了 $parse 的观点以后,我想 $compile 也差不多邃晓了,实在和 $parse 很像。然则他是剖析一段 html 代码的,他的功用就是将死模板变成活模板,也是指令的中心效劳。

比方你有一段 html 代码 <h1>{{test}}</h1>,假如你将这段代码直接放在 html 代码内里,它所显现的内容是怎样的我不说人人也应当懂。这就是死模板了,而所谓的活模板,就是这内里的数据悉数经由了数据的绑定 {{test}}会自动找到当前的上下文,来绑定数据。末了显现出来的 就是活模板,也就是经由数据绑定的模板。

$compile('死模板')(上下文对象),如许就将死模板编程了活模板,你就能够对这段活的 html 代码做操纵了,比方增加到当前节点,等等。

然则在指令中,她会返回两个函数 pre-linkpost-link

第一个实行的是 pre-link,它关于同一个指令的遍历递次是从父节点到子节点的遍历,在这个阶段,dom 节点还没有稳定下来,没法做一些绑定事宜的操纵,然则我们能够在这里举行一些初始化数据的处置惩罚。

第二个实行的是 post-link,也就是我们常说的 link 函数,他是从子节点到父节点遍历的,在这个阶段,DOM 节点已稳定下来了,我们平常会在这里举行许多的操纵。

    原文作者:SzHeJason
    原文地址: https://segmentfault.com/a/1190000013383929
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞