• Angular面试题四


    二十、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;

    }<br>

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

    <div ng-controller="TestCtrl as vm">

    <p>{{name}}</p>

    <div ng-if="vm.name">

    <input type="text" ng-model="vm.name">

    </div>

    </div>

     

    问题

    使用 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 = /^functions*[^(]*(s*([^)]*))/m;

        var STRIP_COMMENTS = /((//.*$)|(/*[sS]*?*/))/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){

      ...

    }])

     

    二十一、 compile和link的区别: 看到一个比较6的答案。性能力(性能和能力)

    编译的时候,compile转换dom,碰到绑定监听器的地方就先存着,有几个存几个,到最后汇总成一个link函数,一并执行,提升了性能。

    function compile(tElement, tAttrs, transclude) { ... }tElement为编译前的element

    function link(scope, iElement, iAttrs, controller) { ... }  iElement为编译后的element,已经与作用域关联起来,所以可以数据绑定

    如果指令只进行DOM的修改,不进行数据绑定,那么配置在compile函数中,如果指令要进行数据绑定,那么配置在link函数中。

     

    二十二、. $apply()和 $digest()的区别

    安全性:$apply()可以接收一个参数作为function(),这个 function 会被包装到一个 try … catch 块中,所以一旦有异常发生,该异常会被 $exceptionHandler service 处理。

    $apply会使ng进入 $digest cycle , 并从$rootScope开始遍历(深度优先)检查数据变更。

    $digest仅会检查该scope和它的子scope,当你确定当前操作仅影响它们时,用$digest可以稍微提升性能。

     

  • 相关阅读:
    网页布局
    Block Formatting Context
    SEO初识
    新的一个月,就这么不知不觉的来临了
    Js结束,项目进行中
    JS学习中....
    ws快捷键
    Html的学习以及webstorm的使用
    从事前端开发应该了解的CSS原理
    jQuery动画
  • 原文地址:https://www.cnblogs.com/ndos/p/8331693.html
Copyright © 2020-2023  润新知