02
2020
06

angular的缺点有哪些?

1.强约束


导致学习成本较高,对前端不友好。


但遵守AngularJS的约定时,生产力会很高,对Java程序员友好。


2.不利于SEO


因为所有内容都是动态获取并渲染生成的,搜索引擎没法爬取。


一种解决办法是,对于正常用户的访问,服务器响应AngularJS应用的内容;对于搜索引擎的访问,则响应专门针对SEO的HTML页面。


3..性能问题


作为MVVM框架,因为实现了数据的双向绑定,对于大数组、复杂对象会存在性能问题。


可以用来优化Angular应用的性能 的办法:


减少监控项(比如对不会变化的数据采用单向绑定)


主动设置索引(指定track by,简单类型默认用自身当索引,对象默认使用$$hashKey,比如改为track by item.id)


降低渲染数据量(比如分页,或者每次取一小部分数据,根据需要再取)


数据扁平化(比如对于树状结构,使用扁平化结构,构建一个map和树状数据,对树操作时,由于跟扁平数据同一引用,树状数据变更会同步到原始的扁平数据)


另外,对于Angular1.x,存在 脏检查 和 模块机制 的问题。


4.移动端


可尝试Ionic,但并不完善。


参考如何看2015年1月Peter-Paul Koch对Angular的看法?


如何看待angular 1.2中引入的controller as语法?


5.最根本的好处


在angular 1.2以前,在view上的任何绑定都是直接绑定在$scope上的


function myCtrl($scope){


$scope.a = 'aaa';


$scope.foo = function(){


...


}


}


使用controllerAs,不需要再注入$scope,controller变成了一个很简单的javascript对象(POJO),一个更纯粹的ViewModel。


function myCtrl(){


//使用vm捕获this可避免内部的函数在使用this时导致上下文改变


var vm = this;


vm.a = 'aaa';


}


原理


从源码实现上来看,controllerAs语法只是把controller这个对象的实例用as别名在$scope上创建了一个属性。


if (directive.controllerAs) {


locals.$scope[directive.controllerAs] = controllerInstance;


}


但是这样做,除了上面提到的使controller更加POJO外,还可以避免遇到AngularJS作用域相关的一个坑(就是上文中ng-if产生一级作用域的坑,其实也是javascript原型链继承中值类型继承的坑。因为使用controllerAs的话view上所有字段都绑定在一个引用的属性上,比如vm.xx,所以坑不再存在)。




{{name}}







问题


使用controllerAs会遇到的一个问题是,因为没有注入$scope,导致$emit、$broadcast、$on、$watch等$scope下的方法无法使用。这些跟事件相关的操作可以封装起来统一处理,或者在单个controller中引入$scope,特殊对待。


栗子


依赖注入是一种软件设计模式,目的是处理代码之间的依赖关系,减少组件间的耦合。


举个栗子,如果没有使用AngularJS,想从后台查询数据并在前端显示,可能需要这样做:


var animalBox = document.querySelector('.animal-box');



var httpRequest = {


get: function(url, callback){


console.log(url + ' requested');


var animals = ['cat', 'dog', 'rabbit'];


callback(animals);


}


}



var render = function(el, http){


http.get('/api/animals', function(animals){


el.innerHTML = animals;


})


}



render(httpRequest, animalBox);


但是,如果在调用render的时候不传参数,像下面这样,会报错,因为找不到el和http(定义的时候依赖了,运行的时候不会自动查找依赖项)


render();


// TypeError: Cannot read property 'get' of undefined


而使用AngularJS,可以直接这样


function myCtrl = ($scope, $http){


$http.get('/api/animals').success(function(data){


$scope.animals = data;


})


}


也就是说,在Angular App运行的时候,调用myCtrl,自动做了$scope和$http两个依赖性的注入。



原理


AngularJS是通过构造函数的参数名字来推断依赖服务名称的,通过toString()来找到这个定义的function对应的字符串,然后用正则解析出其中的参数(依赖项),再去依赖映射中取到对应的依赖,实例化之后传入。


简化一下,大概是这样:


var inject = {


//存储依赖映射关系


storage: {},


//注册依赖


register: function(name, resource){


this.storage[name] = resource;


},


//解析出依赖并调用


resolve: function(target){


var self = this;


var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;


var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;


fnText = target.toString().replace(STRIP_COMMENTS, '');


argDecl = fnText.match(FN_ARGS)[1].split(/, ?/g);



var args = [];


argDecl.forEach(function(arg){


if(self.storage[arg]){


args.push(self.storage[arg]);


}


})



return function(){


target.apply({}, args);


}


}


}


//使用这个injector,前面那个不用AngularJS的栗子这样改造一下就可以调用了


inject.register('el', animalBox);


inject.register('ajax', httpRequest);


render = inject.resolve(render);


render();



问题


因为AngularJS的injector是假设函数的参数名就是依赖的名字,然后去查找依赖项,那如果按前面栗子中那样注入依赖,代码压缩后(参数被重命名了),就无法查找到依赖项了。


//压缩前


function myCtrl = ($scope, $http){


...


}



//压缩后


function myCtrl = (a, b){


...


}


所以,通常会使用下面两种方式注入依赖(对依赖添加的顺序有要求)。


//数组注释法


myApp.controller('myCtrl', ['$scope', '$http', function($scope, $http){


...


}])


//显式$inject


myApp.controller('myCtrl', myCtrl);


function myCtrl = ($scope, $http){


...


}


myCtrl.$inject = ['$scope', '$http'];


补充


对于一个DI容器,必须具备三个要素:依赖项的注册,依赖关系的声明和对象的获取。


在AngularJS中,module和$provide都可以提供依赖项的注册;内置的injector可以获取对象(自动完成依赖注入);依赖关系的声明,就是前面问题中提到的那样。


下面是个栗子


//对于module,传递参数不止一个,代表新建模块,空数组代表不依赖其他模块


//只有一个参数(模块名),代表获取模块



//定义myApp,添加myApp.services为其依赖项


angular.module('myApp', ['myApp.services']);


//定义一个services module,将services都注册在这个module下面


angular.module('myApp.services', [])



// $provider有factory, service, provider, value, constant


//定义一个HttpService


angular.module('myApp.services').service('HttpService', ['$http', function($http){


...


}])

« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。