壹 ❀ 引
我在前面花了两篇博客分别系统化介绍了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
若对于本文介绍的知识点有所疑惑,欢迎留言,我会及时回复,那么到这里本文结束。