使用 Angular开发,可以极大地提升开发效率。然而,它的性能问题一直被诟病,使它不得不重新设计架构,从 Angular2.0到 Angular4.0再到2017年刚刚发布的 Angular5.0,让开发者再一次看到了 Angular的未来,一个着眼于未来的企业级框架。
然而,回到现实中, Angular1.0版本却依旧有着无可替代的市场占有率,因此,关于 Angular1.0中的作用域、数据双向绑定的实现、数据丢失、自定义过滤器、自定义指令、自定义服务、路由等核心技术依旧是开发者需要掌握的重点内容。1、ng-if与ng-show/hide的区别有哪些?第一个区别是,ng-if在后面表达式为true时才创建DOM节点,ng-show则在初始时就创建了,可以通过切换ng-hide类控制元素的显示或隐藏。第二个区别是,ng-if会(隐式地)产生新作用域。这样在ng-if中通过ng- model绑定的数据改变时,不会影响外面元素中插入的数据。下列代码说明ng-if指令内外的name属性变量已经是两个变量了。<p>{ { name } }</p>
<div ng-if="true">
<nput type="text" ng-model="name">
</div>
ng-show不存在此问题。而避免这类问题出现的办法是,始终为页面中的元素绑定对象的属性( data.msg),而不是直接绑定到基本变量(msg)上。<p>{ { data.msg } }</p>
<div ng-if="true">
<input type="text" ng-model="data.msg">
</div>
2、当使用 ng -repeat指令迭代数组时,如果数组中有相同值,会有什么问题?如何解决?会提示 Duplicates in a repeater are not allowed,通过加 track by $index表达式可解决。当然,也可以通过 track by跟踪任何一个普通的值,只要能唯一地标识数组中的每一项即可(建立DOM和数据之间的关联)<div ng-repeat="item in arr track by $index"> { {item } }</div>3、ng-cick中写的表达式,能使用 JavaScript原生对象上的方法吗?不只是ng-click中的表达式,只要是在页面中,都不能直接调用原生的 JavaScript方法,因为这些并不存在于与页面对应的 Controller的 $scope中。<div>{ { parseInt(100.5)} }</div>
但如果在$scope中定义了以下 parseInt函数,就没有问题了。$scope. parseInt =function(num){
return parseInt(num)
}
对于这种需求,使用一个 filter是不错的选择。<div> { {12.34 | icktParseInt)</div>
.filter('icktParseInt', function() {
return function(value){
return parseInt(value)
}
} )
filter表示格式化数据,接受一个输入,按某规则处理,返回处理结果。内置 filter有以下9种。<div> { { time | date : 'yyyy-MM-dd' }}</div>
.controller('main', function($scope, $filter) {
console.log($filter('date')(Date. now(),'HH:mm:ss'))
6、factory、 service和 provider是什么关系?它们都是用来实现自定义服务的方法。但是定义服务的方式不同, factory通过return暴露服务的接口对象。factory(' ickt', function () {
return {
color: 'red '
}
})
service通过this暴露服务的接口对象
.service('ickt', function() {
this .color = 'green '
})
provider创建一个可通过 config配置的服务,通过$get方法的返回值暴露接口。.provider('ickt', function () {
this .$get = function () {
return {
color:'blue'
}
}
})
从底层实现上来看, service调用了 factory,返回其实例;factory调用了 provider,返回其$get中定义的内容。factory和 service的功能类似,只不过 factory是普通 function,可以返回任何数据。service是构造器,不需要返回;provider是加强版 factory,返回一个可配置的 factory。7、Angular的数据绑定采用什么机制?请详述其原理。双向数据绑定是 Angular的核心机制之一。当视图中有任何数据变化时,会更新到模型,当模型中数据有变化时,视图也会同步更新。显然,这需要一个监控。数据绑定实现原理是 Angular在 scope模型上设置了一个监听队列,用来监听数据变化并更新视图。每次绑定一个东西到视图上时 Angular就会往$watch队列里插入条$watch,用来检测它监视的模型里是否有变化的内客容。当浏览器接收到可以被Angular context处理的事件时, $digest循环就会触发,遍历所有的$watch,最后更新DOM。例如,在以下代码中, click事件会产生一次更新操作(至少触发两次 $digest循环)。<button ng-click="val=val+1">demo</button>
按下上式定义的按钮,浏览器接收到一个事件,进入 Angular contexto。$digest循环开始执行,查询毎个$watch是否变化。由于监视 $scope.val的 $watch发现了变化,因此强制再执行一次 $digest循环。发现数据发生了变化,浏览器更新DOM显示 $scope.val的新值。$digest循环的上限是10次(超过10次后抛出一个异常,防止无限循环)。8、对于两个平级界面模块a和b,如果a中触发一个事件,有哪些方式能让b知道?详述原理。这个问题换一种说法就是,如何在平级界面模块间进行通信。有两种方法种是共用服务,一种是基于事件。在 Angular中,通过 factory可以生成一个单例对象,在需要通信的模块a和b中注入这个对象即可。第一种是借助父控制器。在子 controller中向父控制器发射( $emit)一个事件,然后在父控制器中监听($on)事件,再广播($ broadcast)给其他子控制器,这样通过事件携带的参数,实现了数据经过父控制器,向同级控制器传播。第二种是借助 $rootScope。每个 Angular应用默认有一个根作用域 $rootScope,根作用域位于最顶层,从它向下挂着各级作用域。所以,如果子控制器直接使用 $rootScope广播和接收事件,那么就可实现平级之间的通信。9、一个 Angular应用应当如何进行目录结构的划分?对于小型项目,可以按照文件类型划分,比如以下形式。CSS
JS
controllers
models
services
filters
templates
但是,对于规模较大的项目,最好按业务模块划分,比如以下形式。CSS
modules
user
controllers
models
services
filters
templates
activity
controllers
models
services
filters
templates
modules下最好再有一个 common目录来存放公共数据。10、Angular应用通常使用哪些路由库?各自的区别是什么?Angular应用通常使用 neRoute和 ui.router。无论是 ngRoute还是ui. router,作为框架额外的附加功能,都必须以模块依赖的形式引入。(1)ngRoute模块是 Angular自带的路由模块,而 ui.router模块是基于 ngRoute模块开发的第三方模块,属于UI指令规范。(2) ui.router是基于状态( state)的, ngRoute是基于URL的, ui.router模块具有更强大的功能,主要体现在视图的嵌套方面。(3)使用 ui.router能够定义有明确父子关系的路由,并通过ui-view指令将子路由模板插入父路由模板的<diⅳ ui-view</dⅳ>中,从而实现视图嵌套。而在 neRoute中不能这样定义,如果同时在父子视图中使用了<diⅳ ng-view></diⅴ>会陷入死循环。ngRoute
angular .module('ickt',[' ngRoute'])
.config(function ($routeProvider){
$routeprovider
.when(' / home', {
template:'<hl>home { { color } }</h1>',
controller:function($scope){
$scope. color ='red'
}
})
.otherwise ({
redirectTo:'/home'
})
ui .router
angular .module('ickt',[ 'ui .router' ] )
.config ( function($stateProvider ) {
$stateProvider
.state (' home',{
url:'/home/:id?a&b,
template:'<h1>home page { { color } }</h1>' ,
//定义控制器
controller:function($scope){
$scope. color ='red'
}
})
})
11、对于不同团队开发的 Angular应用,如果要做整合,可能会遇到哪些问题?如何解决?可能会遇到不同模块之间的冲突比如一个团队的开发在 moduleA下进行,另一团队的开发在 moduleB下进行。angular .module('myApp.moduleA', [ ] )
.factory('serviceA, function ( ) {
...
} )
angular .module('myApp. moduleB', [ ] )
.factory('serviceA ', function( ) {
...
})
angular .module('myApp,[' myApp. moduleA','myApp. moduleB'])
这会导致两个 module下面的 serviceA发生覆盖。在 Angular 1.x中并没有很好的解决办法,所以最好在前期进行统一规划,做好约定,严格按照约定开发,每个开发人员只写特定的区块代码。12、Angular的缺点有哪些?如何克服这些缺点?强约束,导致学习成本较高,对前端不友好。当遵守 Angular的约定时,效率会提高。但不利于SEO,因为所有内容都是动态获取并渲染生成的,搜索引擎无法爬取种解决办法是,对于正常用户的访问,服务器响应 Angular应用的内容;对于搜索引擎的访问,则响应专门针对SEO的HTML页面。作为MVVM框架,因为实现了数据的双向绑定,对于大数组、复杂对象会存在性能问题。(1)减少监控项(比如对不会变化的数据采用单向绑定)。(2)主动设置索引(指定 track by,简单类型默认用自身当索引,对象默认使用 $$hashKey,比如改为 track by item id)。(3)降低渲染数据量(比如分页,或者毎次取一小部分数据,根据需要再取)。(4)数据扁平化(比如对于树状结构,使用扁平化结构,构建map和树状数据。当操作树时,由于跟扁平数据是同一个引用,因此树状数据变更会同步到原始的扁平数据)。14、如何看待 Angular1.2中引入的 controllerAs语法?在 Angular1.2以前,在view上的任何绑定都是直接绑定在 $scope上的。function myCtrl($scope){
$scope.a=='aaa';
$scope.foo = function ( ) {
...
}
}
使用 controllerAs,不需要再注入 $scope服务了, controller变成了一个很简单的 JavaScript对象,一个更纯粹的ViewModel 。var vm = this;
vm.a = 'aaa';
从源码实现上来看, controllerAs语法只是把 controller这个对象的实例用 controllerAs别名表示,并在 $scope上创建了一个属性。if (directive .controllerAs){
locals. $scope [ directive .controllerAs ]= controllerInstance;
}
与 Angular 1.x相比, Angular 2的改动很大,几乎是一个全新的框架。Angular2基于 TypeScript语法实现,在大型项目团队协作时,使用强语言类型更有利优点是组件化,提升开发和维护的效率。其他优点还有 module支持动态加载,以及new router、 promise的原生支持等。它迎合未来标准,吸纳其他框架的优点,值得期待。它依赖于 $interpolation服务,在初始化页面HTML后,它会找到这些表达式,并进行标记,于是每遇见一个{{} },就会设置一个$ watch。而 $interpolation会返回一个带有上下文参数的函数,最后执行该函数,渲染数据。17、Angular中的 digest周期是如何触发的?在每个 digest周期中, Angular总会对比 scope上 model的值,一般 digest周期都是自动触发的,也可以使用 $apply进行手动触发。要停止$timeout,可以用 cancel方法。示例如下。var timebar =$timeout ( function ( ){
$scope,msg='有课前端网'
},1000)
$timeout.cancel(timebar)
var watchFn = $scope. $watch('msg ', function(newValue) {
if (newValue ==='icketang){
// $watch()会返回一个停止注册的函数,可以执行该函数进行注销
watchFn ( )
}
console. log(newValue)
} )
21、自定义指令中, scope配置中的@、=和&修饰符有什么区别?在 scope中,@、=和&在进行值绑定时分别表示如下含义。&表示父作用域传递的属性或方法等数据,在子作用域中作为方法获取。通过$on注册事件,在其他作用域之间通过使用$emit或$broadcast发布消息实现通信。通过 $scope的$parent等属性,访问父子或者兄弟作用域,实现通信。(1)关闭 debug,方法是使用 $compileProvider 。myApp. config(function ($compileProvider){
$compileProvider .debugInfoEnabled(false);
});
(4)在无限滚动加载中避免使用 ng-repeat(5)使用性能测试小工具挖掘 Angular的性能问题。24、你认为在 Angular中使用 jQuery好吗?普遍认为在 Angular中使用 jQuery并不是一个很好的尝试。其实当我们学习Angular的时候,应该做到从零去接受 Angular的思想,例如,数据绑定、使用 Angular自带的一些API、合理地组织路由、写相关指令和服务等。Angular自带的很多API完全可以替代 jQuery中常用的API,可以使用 angular.element、$http、 $timeout、ng-init等。或许引入 jQuery可以在解决问题(比如使用插件)时有更多的选择。当然,这通过影响代码组织来提高工作效率。随着对 Angular的理解深入,在重构时会逐渐摒弃当初引入 jQuery时的一些代码。所以我们还是应该尽力遵循 Angular的设计理念。(1)每个双向绑定的元素都有一个 watcher。(2)在某些事件发生的时候,调用 digest对脏数据进行检测。这些事件有表单元素内容变化、Ajax请求响应、单击按钮执行回调函数等。(3)脏数据检测,会检测 rootscope下所有被 watcher监控的元素。26、Angular中的$http服务有什么作用?$http是 Angular中的一个核心服务,用于读取远程服务器的数据。可以使用内置的 $http服务直接与服务器端进行通信。$http服务只是简单地封装了浏览器原生的 XMLHttpRequEst对象。27、在写 controller逻辑时,需要注意什么?DOM操作只能出现在指令( directive)中。不应该出现在服务( service)中。Angular倡导以测试驱动开发,在服务或者控制器中出现了DOM操作,也就意味着无法通过测试。当然,这只是一方面。重要的是,使用 Angular中的数据双向绑定技术,就能专注于处理业务逻辑,无须关心各种DOM操作。如果在 Angular的代码中还有各种DOM操作,还不如直接使用 jQuery去开发。restrict:定义指令在DOM中的声明形式,如E(元素)、A(属性)、C(类名)、 M(注释)。compile:可以返回一个对象或函数。如果设置了 compile函数,说明我们希望在指令将数据绑定到DOM之前,进行DOM操作,在这个函数中进行诸如添加和删除节点等DOM操作是安全的。29、Angular和 jQuery的区别是什么?Angular基于数据驱动,所以 Angular适合做数据操作比较繁杂的项目。jQuery基于DOM驱动, jQuery适合做DOM操作多的项目。30、什么是作用域数据丢失?如何解决作用域数据丢失问题?当在作用域中修改数据的时候,更新的数据没有渲染到页面中,或者没有触发相应的业务逻辑执行,我们就说这部分数据丢失了。解决数据丢失问题有4种方式。使用相应的服务,如 setTimeout方法可以用 $timeout服务完美代替。使用$digest方法进行检测,但只能检测该方法执行前修改的数据,后面修改的数据无法检测。使用$apply方法进行检测,但只能检测该方法执行前修改的数据,后面修改的数据无法检测,本质上,$apply在内部调用$digest方法实现检测在 Sapply方法的回调函数中修改数据,$apply方法可以屏蔽错误中断应用程序的执行,但只能检测前面以及回调函数内部修改的数据,在该方法后面修改的数据无法检测。31、Angular2应用程序的生命周期 hooks是什么?Angular2组件/指令具有生命周期事件,是由@ angular/core管理的。@ Angular/core会创建组件,渲染它,创建并呈现它的后代。当@ angular/core的数据绑定属性更改时,处理就会更改,在从DOM中删除其模板之前,就会销毁它。Angular提供了一组生命周期hooks(特殊事件)函数,并在需要时执行操作。构造函数会在所有生命周期事件之前执行。每个接口都有一个前缀为ng的hook方法。ngAfterContentInit:组件内容初始化完成之后。
ngAfterContentChecked:在 Angular检查投影到其视图中绑定的数据之后。
ngAfterViewInitAngular:创建组件的视图后。
ngAfterViewChecked:在 Angular检查组件视图的绑定之后。
32、和使用 Angular1相比,使用 Angular2有什么优势?Angular2是一个平台,它不仅是一种语言,还具有更快的速度、更好的性能和更简单的依赖注入。它支持模块化开发,跨平台,具备了 EMAScript6和 Typescript的优点,如路由灵活,具备延迟加载功能等。路由是能够让用户在视图纽组件之间导航的机制。Angular2简化了路由,并提供了在模抉级的(延迟加载)配置,定义更灵活。Angular应用程序具有路由器服务的单个实例,并且每当URL改变时,相应的路由就与路由配置数组进行匹配。在成功匹配时,它会应用重定向。此时,路由器会构建 ActivatedRoute对象的树,同时包含路由器的当前状态。在重定向之前,路由器将通过运行保护( CanActivate)来检查是否允许新的状态。Route Guard只是用来检查路由授权的接口方法。保护运行后,它将解析路由数据并通过将所需的组件实例化到< router-outlet>< router- outlet>中来激活路由器状态。34、什么是事件发射器?它是如何在 Angular2中工作的?Angular2不具有双向检测系统,这与 Angular1不同。在 Angular2中,组件中发生的任何改变总是从当前组件传播到其所有的子组件中。如果一个子组件的更改需要反映到其父组件的层次结构中,就可以使用事件发射器API(吞吐器)来发出事件简而言之, EventEmitter是在@ angular/core模块中定义的类,由组件和指令使用,用来发出自定义事件。使用emit( value)方法发出事件。通过模块的任何一个组件,可以使用订阅方法来实现事件发射的订阅,例如以下代码@Directive ({
selector:'[app Demo]',
outputs:[ 'sendMessage' ]
})
export class DemoDirective {
sendMessage = new EventEmitter ( ) ;
constructor ( ) {
setInterval(o=>
},2000)
<hI (sendMessage)="receiveMessage() appDemo >demo</h1>
export class AppComponent i
//定义接收数据的方法receiveMessage() t console. log(' success)
大多数企业应用程序包含各种用于特定业务案例的模块。捆绑整个应用程序代码并完成加载,会在初始调用时,产生巨大的性能开销。延迟加载使我们只加载用户正在交互的模块,而其余的模块会在运行时按需加载延迟加载通过将代码拆分成多个包并以按需加载的方式,来加速应用程序初始加载过程。36、在 Angular2应用中,应该注意哪些安全威胁?就像任何其他客户端或Web应用程序一样,Angular2应用程序也应该遵循一些基本准则来减轻安全风险,例如以下基本准则。如果使用外部HTML,也就是来自数据库或应用程序之外的HTML,就需要清理它。通过限制API,选择使用已知或安全环境/浏览器的App来防止XSRF攻击。37、如何优化 Angular2应用程序来获得更好的性能?优化取决于应用程序的类型和大小以及许多其他因素。但一般来说,在优化Angular2应用程序时,应考虑以下几点。所有 dependencies和dev- dependencies都是明确分离的。如果应用程序较大,建议考虑延迟加载而不是完全捆绑的应用程序。38、什么是 Shadow dom?它是如何帮助 Angular2更好地执行的?Shadow DOM是HTML规范的一部分,它允许开发人员封装自己的HTML标记、 CSS和 JavaScript代码。Shadow DOM以及其他一些技术,使开发人员能够像< audio>标签样构建自己的一级标签、Web组件和API。总的来说,这些新的标签和API称为Web组件。Shadow dom提供了更好的关注分离,通过其他的 HTML DOM元素实现了更少的样式与脚本的冲突。因为 Shadow DOM本质上是静态的,同时也是开发人员无法访问的,所以它是一个很好的候选对象。它缓存的DOM将在浏览器中呈现得更快,并提供更妤的性能。此外,还可以较好地管理 Shadow dom,同时检测 Angular2应用的改变,并且可以有效地管理视图的重新绘制。AOT编译,即 Ahead Of Time编译(运行前编译)。在构建时, Angular编译器会将 Angular组件和模板编译为原生 JavaScript和HTML。编译好的HTML和 JavaScript将会部署到Web服务器中,以便浏览器节省编译和渲染时间。更快的下载速度。因为应用程序已经编译,许多 Angular编译器相关库就不再需要捆绑,应用程序包变得更小,所以该应用程序可以更快地下载。更少的HTTP请求数。如果没有捆绑应用程序来支持延迟加载,对于每个关联的HTML和CSS,都会有一个单独的服务器请求。但是预编译的应用程序会将所有模板和样式与组件对齐,因此到达服务器的HTTP请求数量会更少。更快的渲染速度。如果应用程序不使用AOT编译,那么应用程序完全加载时,编译过程会发生在浏览器中。这需要等待下载所有必需的组件,然后等待编译器花费大量时间来编译应用程序。使用AOT编译,就能实现优化。在构建时检测错误。由于预先编译可以检测到许多编译时错误,因此能够为应用程序提供更好的稳定性。仅适用于HTML和CSS,其他文件类型需要前面的构建步骤。没有 watch模式,必须手动执行并编译所有文件。40、如何评价 Backbone和 Angular?Backbone具有依赖性,依赖于 underscore.js。Backbone+ Underscore+ jQuery(或Zepto)比一个Angular应用多出了两次HTTP请求。Backbone的模型没有与UI视图数据绑定,而是需要在视图中自行操作DOM来更新或读取UⅠ数据。Angular与此相反,模型直接与UⅠ视图绑定。模型与U视图的关系,通过指令封装。Angular内置的通用指令就能实现大部分操作了。也就是说,基本不必关心视图与UI视图的关系,直接操作视图即可,UI视图自动更新。在你输入特定数据时, Angular的指令就能渲染相应的U视图。它是一个比较完善的前端MVW框架,包含模板、数据双向绑定、路由、模块化、服务、依赖注入等功能模板的功能强大,并且是声明式的,自带丰富的Aη gular指令。ionic是一个用来开发混合手机应用的、开源的、免费的代码库。可以优化HTML、 CSS和 JavaScript的性能,构建高效的应用程序。Angular通过新的属性和表达式扩展了HTML. Angular可以构建一个单一页面应用程序( Single Page Application,SPA)。ionic是一个混合App开发工具,它以 Angular为中间脚本工具,所以如果要使用 ionic开发App,就必须了解 Angular。42、在用双大括号绑定元素时,如何解决内容闪烁的问题?在 Angular内部,可以向元素中添加ng- clock属性来实现元素的隐藏效果。<div ng-clock> { {message } } </div>如果绑定纯文字的内容,建议使用ng-bind的方式,而非双大括号。<div ng-bind="message"></div>
43、如何理解ng-repeat指令中的作用域继承关系?在调用 ng-repeat指令显示数据时,在新建DOM元素时, ng-repeat也为每个新建的DOM元素创建了独立的作用域。尽管如此,它们的父级作用域是相同的,都是构建控制器时注入的$scope对象,调用 angular.element( element).scope方法可以获取某个DOM元素对应的作用域。通过某个DOM元素对应的作用域又可以访问它的父级作用域,从而修改绑定的数据源。Angular是一个比较完善的前端MVVM框架,包含了模板、数据双向绑定、路由、服务、过滤器、依赖注入等功能。与其他一些只关注前端某一方面的框架相比, Angular4支持的功能更加全面广泛。