angularjs中directive指令与component组件有什么区别?

Posted 听风是风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了angularjs中directive指令与component组件有什么区别?相关的知识,希望对你有一定的参考价值。

 壹 ❀ 引

我在前面花了两篇博客分别系统化介绍了angularjs中的directive指令与component组件,当然directive也能实现组件这点毋庸置疑。在了解完两者后,即便我们知道component就像删减版的directive,用法上大同小异,但两者在使用角度仍然存在不少差异,那么本文将详细对比两者,加深大家的认知,那么本文开始。

 贰 ❀ 区别详解

  Directive Component
bindings(用于父传值子)  NO  YES(绑定至控制器)
bindToController  YES  NO
compile function(编译函数)  YES  NO
controller  YES  YES
controllerAs  YES(默认flase)  YES(默认$ctrl)
link functions(链接函数)  YSE  NO
multiElement  YES  NO
priority(组件优先权)  YES  NO
replace  YES  NO
require  YES  YES
restrict  YES  NO(仅支持元素)
scope  YES(绑定至scope)  NO(作用域总是隔离)
template  YES  YES
templateNamespace  YES  NO
templateUrl  YES  YES
terminal(优先权低的组件是否执行)  YES  NO
transclude  YES(默认false)  YES(默认false)

这是一份包含了指令directive与组件component全属性的表格,谁有谁没有已标注,至于具体用法可阅读博主先前完成的两篇博客。现在来说说两者表现不同:

1.创建与使用方式不同

在创建上,directive在指令名后是一个回调函数,函数内返回一个包含指令配置的对象,而component在组件名后紧接一个包含组件配置的对象。

在使用上,directive支持EMAC,即元素注释属性与类名,而component仅支持元素,因此component没有restrict,terminal,replace此类属性。

<!-- 指令 -->
<!-- directive:directive-name -->
<directive-name></directive-name>
<div directive-name></div>
<div class="directive-name"></div>

<!-- 组件 -->
<component-name></component-name>
angular.module(\'myApp\', [])
    .controller(\'myCtrl\', function ($scope) {})
    .directive(\'directiveName\', function () {
        return {
            //定义属性配置
        }
    })
    .component(\'componentName\', {
        //定义属性配置
    });

2.模板使用不同

指令directive在使用模板,不管是template或者templateUrl,都要求模板代码用一个根元素进行包裹,但component并没有这个要求。

angular.module(\'myApp\', [])
    .controller(\'myCtrl\', function ($scope) {})
    .directive(\'directiveName\', function () {
        return {
            template: \'<span>1</span><span>2<span>\', //错误
            template: \'<div><span>1</span><span>2<span></div>\' //正确
        }
    })
    .component(\'componentName\', {
        //定义属性配置
        template: \'<span>1</span><span>2<span>\', //不会报错
    });

3.父子传值表现不同

我们知道指令directivescope传值directive绑在scope上,component绑在this上,所以component要使用钩子函数。当然directive可以使用bindTocontroller让传值也绑定在this上。

我们知道component自带隔离作用域,而directive是否隔离由scope属性决定,false不创建作用域,true创建作用域但不隔离,{}创建隔离作用域。

当拥有隔离作用域时,父子互不相关,所以子无法继承父作用域中的任何数据,component得使用bindings传值,而directive得使用scope:{}传值:

<div ng-controller="myCtrl as vm">
    <echo1 name="name" age="vm.age"></echo1>
    <echo2 name="name" age="vm.age"></echo2>
</div>
angular.module(\'myApp\', [])
    .controller(\'myCtrl\', function ($scope) {
        $scope.name = \'听风是风\';
        this.age = 26;
    })
    .directive(\'echo1\', function () {
        return {
            restrict: \'AE\',
            replace: true,
            scope: {
                name: \'<\',
                age: \'<\'
            },
            template: \'<div>{{name}}{{age}}</div>\',
            controller:function($scope) {
                console.log($scope,this)
            }
        }
    })
    .component(\'echo2\', {
        bindings: {
            name: \'<\',
            age: \'<\'
        },
        template: \'<div>{{$ctrl.name}}{{$ctrl.age}}</div>\',
        controller:function($scope) {
            console.log($scope,this);
        }
    });

在这个例子中,我们在父级控制器中分别在scope以及this上绑定了两条数据,并分别传递给指令echo1与组件echo2,可以看到在两者的模板中使用是有差异的,指令使用传递过来的数据更像$scope的写法,而组件更像构造器。

这是因为directive传值默认是绑定在scope上的,而component传值默认绑定在控制器上,我们可以分别打印两者的scope与this,首先是directive:

可以看到数据传递过来是直接绑定在scope中的,所以用起来与绑在$scope上一样,再来看看component:

可以看到传递过来的数据不是绑定在scope上,而是组件的控制器上,由于我们没有设置controllerAs,所以这里默认可以通过$ctrl访问。

别忘了directive有一个bindToController属性,作用就是将传递过来的值绑定在控制器上,我们修改代码,为directive添加此bindToController:true,再次输出可以看到scope与this发生了变化。

    .directive(\'echo1\', function () {
        return {
            restrict: \'AE\',
            replace: true,
            scope: {
                name: \'<\',
                age: \'<\'
            },
            bindToController: true,
            controllerAs: \'ctrl\',
            template: \'<div>{{ctrl.name}}{{ctrl.age}}</div>\',
            controller: function ($scope) {
                console.log($scope, this)
            }
        }
    })

或许你想问能绑定在scope上干嘛要绑定在控制器上呢?别忘了directive与component都有一个require属性,通过此属性我们能注入其它组价或指令的控制器,也就是说你能用其它指令中定义的属性方法,前提是这些属性方法得绑定在this上,来看个例子:

<div ng-controller="myCtrl as vm">
    <echo1>
        <echo2></echo2>
    </echo1>
</div>
angular.module(\'myApp\', [])
    .controller(\'myCtrl\', function ($scope) {})
    .directive(\'echo1\', function () {
        return {
            restrict: \'AE\',
            replace: true,
            controller: function ($scope) {
                $scope.gender = \'male\';
                this.name = \'听风是风\';
                this.sayName = function(){
                    console.log(this.name);
                }
            }
        }
    })
    .component(\'echo2\', {
        template: \'<div><button ng-click="$ctrl.echoCtrl.sayName()">点我</button></div>\',
        require:{
            echoCtrl:\'?^echo1\'
        },
        controller: function ($scope) {
            this.$onInit = function () {
                console.log(this.echoCtrl);
            }
        }
    });

我在指令echo1的controller中分别在scope以及this上绑定了一些属性,然后在组件echo2中通过require注入echo1的控制器,可以看到我们能通过此做法复用已经定义过的属性方法。同时我们打印注入的控制器,可以看到只有绑定在echo1 this上的属性,scope上的属性并没有传递过来。

4.component的controller中一般结合钩子函数使用,directive不需要

不管是通过scope/bindings传递父作用域数据过来,还是require注入其它指令组件控制器上的属性方法,在模板上直接使用都是没问题的,只是一个在scope上一个在控制器上的区别,前面也有例子展示。

但如果我们要在directive和component的controller中操作传递过来的数据component得使用钩子函数中才能获取到,否则就是undefined,看个例子:

<div ng-controller="myCtrl">
    <echo1 person="person"></echo1>
    <echo2 person="person"></echo2>
</div>
angular.module(\'myApp\', [])
    .controller(\'myCtrl\', function ($scope) {
        $scope.person = {
            name: \'听风是风\',
            age: \'26\'
        }
    })
    .directive(\'echo1\', function () {
        return {
            restrict: \'AE\',
            replace: true,
            scope: {
                person: \'<\',
            },
            template: \'<div>{{name}}</div>\',
            controller: function ($scope) {
                //传递过来的person绑定在scope上
                $scope.name = $scope.person.name;
            }
        }
    })
    .component(\'echo2\', {
        bindings: {
            person: \'<\',
        },
        template: \'<div>{{name}}</div>\',
        controller: function ($scope) {
            //只有在钩子函数中才能获取到
            this.$onInit = function (){
                //传递过来的person绑定在控制器上
                $scope.name = this.person.name
            }
        }
    });

这个例子中我们传递过来的是一个对象,但是我们只需要在视图上渲染对象中的一条属性,所以在controller中做了一次数据加工。directive直接加工没问题,但是component必须在钩子函数中才能取到this.person对象,大家可以尝试注释掉外面的$onInit方法看看区别,这点在使用component的controller处理传递过来的数据一定得注意。

5.require使用不同

前面已经提到directive与component都能使用require注入其它指令组件的控制器,以达到使用控制器中的数据,不过directive与component在require使用上有一点点区别,看个例子:

<div ng-controller="myCtrl">
    <echo>
        <echo1></echo1>
        <echo2></echo2>
    </echo>
</div>
angular.module(\'myApp\', [])
    .controller(\'myCtrl\', function ($scope) {})
    .directive(\'echo\', function () {
        return {
            restrict: \'AE\',
            replace: true,
            controller: function ($scope) {
                this.name = \'听风是风\';
            }
        }
    })
    .directive(\'echo1\', function () {
        return {
            restrict: \'AE\',
            replace: true,
            require: \'?^echo\',
            template: \'<div>{{name}}</div>\',
            link: function (scope, ele, attrs, ctrls) {
                console.log(ctrls);
                scope.name = ctrls.name;
            }
        }
    })
    .component(\'echo2\', {
        require: {
            echoCtrl: \'?^echo\'
        },
        template: \'<div>{{name}}</div>\',
        controller: function ($scope) {
            //只有在钩子函数中才能获取到
            this.$onInit = function () {
                console.log(this.echoCtrl);
                $scope.name = this.echoCtrl.name;
            }
        }
    });

 

在directive中require的值是一个字符串或者一个数组(注入多个时),并且注入的指令/组件控制器将成为link函数的第四个参数,注意是link函数不是controller。

而component的require值一直是个对象,被注入的指令/组件的控制器需要作为自定义key的value,在controller中通过this.key访问,注意,使用同样需要钩子函数。

 叁 ❀ 使用抉择

我们在上文中介绍了directive与component使用时存在的部分差异,那么实际开发中该如何抉择呢,其实在angular官网就已经给出了答案。

在AngularJS中,组件是一种特殊的指令,它使用更简单的配置,在属性默认值和属性配置实用角度上component有更大的优势,例如require key-value形式相比directive的数组更便于使用,controllerAs自带了默认值等。

当然directive也有component无法取代的一面,当我们需要在编译和预链接函数中执行操作时,或者同一元素拥有多个指令需要定义优先级时,directive会比component更强大,没有谁好谁坏,只是根据需求来决定。

 肆 ❀ 总

那么到这里,关于directive与component使用区别介绍完毕了,如果大家对于directive与component使用有疑惑,可以阅读博主这两篇文章:

angularjs 一篇文章看懂自定义指令directive

一篇文章看懂angularjs component组件

若对于本文介绍的知识点有所疑惑,欢迎留言,我会及时回复,那么到这里本文结束。

以上是关于angularjs中directive指令与component组件有什么区别?的主要内容,如果未能解决你的问题,请参考以下文章

学习AngularJs:Directive指令用法(完整版)

AngularJS之指令中controller与link

angularJS 系列——理解指令 understanding directives

AngularJS 全局scope与指令 scope通信

步入angularjs directive(指令)--点击按钮加入loading装态

AngularJS之基础-5 路由(定义路由使用路由)自定义指令(Directive)