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 | NO | 
| 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中directive指令与component组件有什么区别?的更多相关文章
- AngularJS中Directive指令系列 - scope属性的使用
		
文章是转的,我做下补充.原文地址:https://segmentfault.com/a/1190000002773689 每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部 ...
 - AngularJS中Directive指令系列 - 基本用法
		
参考: https://docs.angularjs.org/api/ng/service/$compile http://www.zouyesheng.com/angular.html Direct ...
 - angularjs学习之六(angularjs中directive指令的一般编程事件绑定 模板使用等)
		
angular js 中模板的使用.事件绑定以及指令与指令之间的交互 相应教学视频地址(需FQ):v=aG8VD0KvUw4">angularjs教学视频 <!doctype h ...
 - AngularJS中Directive指令系列
		
近段时间在研究Angular中的directive用法,打算写个系列.以官方文档为主.并参考诸多教程.加上自己的思考. 基本概念及用法 scope属性的使用. &, <, =, @ 符 ...
 - AngularJS中Directive指令系列 - bindToController属性的使用
		
默认false.这个属性用来绑定scope的属性直接赋给controller.可以为true或者和scope相同格式的对象. 此外使用此属性,要设置controller的别名,通常通过"co ...
 - angularJS中directive父子组件的数据交互
		
angularJS中directive父子组件的数据交互 1. 使用共享 scope 的时候,可以直接从父 scope 中共享属性.使用隔离 scope 的时候,无法从父 scope 中共享属性.在 ...
 - AngularJS中的指令全面解析(转载)
		
说到AngularJS,我们首先想到的大概也就是双向数据绑定和指令系统了,这两者也是AngularJS中最为吸引人的地方.双向数据绑定呢,感觉没什么好说的,那么今天我们就来简单的讨论下AngularJ ...
 - 牛客网Java刷题知识点之什么是JSP的3大常用指令、JSP的6大哪些动作、JSP中include指令和include动作有什么区别
		
不多说,直接上干货! JSP的3大常用指令 包含指令(Include directive):用来包含文件和合并文件内容到当前的页面. 页面指令(Page directive):用来定义JSP页面中特定 ...
 - AngularJS中的指令
		
欢迎大家讨论与指导 : ) 前言 当AngularJS中的内置指令不能满足我们的需求,或者当我们需要创建一个能够用于多个AngularJS程序的自包含的功能单元时,我们应该创建自定义指令来满足需求. ...
 
随机推荐
- 为什么有ASP.NET
			
最近读了一些文章,总结一下: 在1999年,当时微软的windows系统运行的所有的应用程序都是有组件对象模型为根本基础开发的,用VB来处理数据访问和复杂的用户界面,缺点是不能使用函数指针,因为当时的 ...
 - redis(5)--redis集群之哨兵机制
			
哨兵机制 在前面讲的master/slave模式,在一个典型的一主多从的系统中,slave在整个体系中起到了数据冗余备份和读写分离的作用.当master遇到异常终端后,需要从slave中选举一个新的m ...
 - 提示用户输入一个1-40之间的数字,使用if语句根据输入数字的大小进行判断,如果输入的数字在
			
提示用户输入一个1-40之间的数字,使用if语句根据输入数字的大小进行判断,如果输入的数字在 num_user=input('输入一个1-40之间的整数:') num_int=int(num_user ...
 - 205K+程序员关注过的问题:为什么不应该使用Java的原始类型?
			
在逛 Stack Overflow 的时候,发现了一些访问量像熊耳山一样高的问题,比如说这个:为什么不应该使用Java的原始类型?访问量足足有 205K+,这不得了啊!说明有很多很多的程序员被这个问题 ...
 - mysql #1062 - Duplicate entry '2147483647' for key '*'
			
一.当我看到这报错的时候,第一眼是认为存在重复记录,但是找了很久没找到2147483647 二.一条条的插入数据(有一批数据),直到找到报错的数据,发现是长度超了,定义了int插入的值却有11位长,哭 ...
 - 在MSSQL中的简单数据类型递归
			
在某些特定的项目需求中,我们需要实现树状数据结构, 由此,我们需要用递归将数据查询出来. WITH T AS ( SELECT ID,PID FROM TableName WHERE ID=1 UNI ...
 - 前端day01
			
目录 软件开发架构 web服务的本质 HTTP协议 HTML的注释 HTML的文档结构 标签的分类 标签的分类 列表标签 表格标签 软件开发架构  c/s  b/s  b/s本质也是c/s  ...
 - 图文结合深入理解JS中的this值
			
文章目录 Js 中奇妙的this值 1. 初探this 2. this指向总结 2.1 普通函数调用 2.2 对象的方法调用 2.3 构造函数调用 2.4 利用call,apply,bind方法调用函 ...
 - 解读setTimeout, promise.then, process.nextTick, setImmediate的执行顺序
			
最近在看<Node.js调试指南>的时候遇到有意思的几道题,是关于setTimeout, promise.then, process.nextTick, setImmediate的执行顺序 ...
 - 使用VMware安装CentOS 7
			
环境:Windows10 , VMware Workstation 15 Player, CentOS 7 为什么选择CentOS ? 主流: 目前的Linux操作系统主要应用于生产环境,主流企业级L ...