• AngularJS学习笔记


    AngularJS在加载启动时,会做3件事情:
    1.依赖注入
    2.创建 root scope作为整个模型的上下文
    3.从ngApp开始编译DOM,处理后续的指令和绑定

    当它启动后,它会等待浏览器的输入事件(鼠标、HTTP请求等),若输入事件改变了model,那么AngularJS会通过更新绑定,将model的改变反应到view上。

    AngularJS中指令都是带有短划线(-)的,自定义的属性都是驼峰命名的
    1.ng-app:
    2.ng-controller:定义了一个叫做PhoneListController的控制器,并且向module(phonecatApp)注册。model模型(手机的数据)在controller中,model由controller实例化,controller函数就是一个简单的构造函数。参数scope在该controller范围内都是有效的。scope是template、model、controller之间的联系纽带(template可以理解为是view中的一部分,包括数据绑定、展示逻辑)。
    我们在scope下分配参数,可以在其他页面(使用相同的controller)使用该参数,这样交互就会显得比较灵活。但是这种方法只适用于小型的系统,若在大型系统上,紧耦合就会成为一个问题。
    3.ng-repeat:
    PS:不要搞错module模块和model模型,MVC里的M是指model

    // Define the `phonecatApp` module
    var phonecatApp = angular.module('phonecatApp', []);
    
    // Define the `PhoneListController` controller on the `phonecatApp` module
    phonecatApp.controller('PhoneListController', function PhoneListController($scope) {
      $scope.phones = [
        {
          name: 'Motorola XOOM™ with Wi-Fi',
          snippet: 'The Next, Next Generation tablet.'
        }, {
          name: 'MOTOROLA XOOM™',
          snippet: 'The Next, Next Generation tablet.'
        }
      ];
    });

    component

    相对于上述例子,还有一些地方我们可以改进:
    1.如果我们要复用template和controller,在上面的例子中,我们需要把他们都复制到复用的地方,这样容易出错。
    2.我们把变量都赋值到scope下面,这样会造成混乱。

    //组件greetUser向模块myAPP注册
    angular.
      module('myApp').
      component('greetUser', {
        template: 'Hello, {{$ctrl.user}}!',
        controller: function GreetUserController() {
          this.user = 'world';
        }
      });

    我们可以在html中使用标签<greet-user></greet-user>,然后AngularJS会用template去替换它。$ctrl在默认情况下,代表着controller。我们在controller的构造函数中用this代替了在scope下赋值。标签<greet-user></greet-user>可以自任何地方使用,也实现了最大限度的解耦。我们也不用担心component会在其他地方被修改,因为template、controller都在component里面。
    避免直接使用scope,这是一条最佳实践。

    文件组织的最佳实践

    1.一个特性一个文件

    不要把controller都放在一个js文件里,不要把component都放在一个js文件里。比如上述的componentgreetUser,需要放在greetUser.component.js里,那么在这js文件里就不要放其他东西了。

    2.按特性组织

    按照1中的方法,一个文件放置一个特性,那么在app/目录下会产生许多文件,我们需要将这些文件按特性组织成文件夹。如果在许多的地方都会用到同一个特性,那么我们就把这个特性文件夹放到aap/core/里面。

    app/
      phone-list/
        phone-list.component.js
        phone-list.component.spec.js(test)
      core/
        feature1/
          xxx.js
          xxx.spec.js
      app.js
    // phone-list.component.js
    // Register `phoneList` component to module 'phonecatApp'
    angular.
      module('phonecatApp').
      component('phoneList',...)
    // app.js
    // Define the `phonecatApp` module
    // 模块phonecatApp没有依赖其他模块,只有它本身
    angular.module('phonecatApp', []);

    3.模块化

    如果现在有其他应用要用到phone-list,我们不需要重复发明轮子,只需要把目录phone-list/复制到新应用下。但是我们发现,原来在phone-list/中,componentphoneList是向modulephonecatApp注册的,在新的应用中,应用名就不叫phonecatApp了,这时我们需要把phonecatApp都改为当前的应用名。显然,这样的改动容易出错且重复劳动。我们有更好的方法去做这件事。引用一句最近在《Head first 设计模式》上看到的话,对改动关闭,对扩展开放,我们不要去改动已有代码,这样可能在修改一个bug的同时代入另外一个bug,我们应该是封装、抽象、扩展我们已有的代码。
    1.新建一个phone-list.module.js文件,把component提高到module层次。
    2.在新的应用newApp中,添加phonecatApp依赖

    app/
      phone-list/
        phone-list.module.js
        phone-list.component.js
        phone-list.component.spec.js
      core/
        feature1/
          xxx.js
          xxx.spec.js
      app.js
    //app/phone-list/phone-list.module.js:
    // Define the `phonecatApp` module
    angular.module('phonecatApp', []);
    // phone-list.component.js
    // Register `phoneList` component to module 'phonecatApp'
    angular.
      module('phonecatApp').
      component('phoneList',{
        template:
          '<ul>' +
            '<li ng-repeat="phone in $ctrl.phones">' +
              '<span>{{phone.name}}</span>' +
              '<p>{{phone.snippet}}</p>' +
            '</li>' +
          '</ul>',
          ...
      })
    // app.js
    // Define the `newApp` module
    angular.module('newApp', [
      // ...which depends on the `phonecatApp` module
      'phonecatApp'
    ]);

    4.使用外部模板

    在3中,component里面的template是内嵌式的html,这样不易于维护和模块化,我们把内嵌式的html改为外部的html。这里有一个点需要注意:当AngularJS解析到templateUrl时,会发起一个HTTP请求,虽然AngularJS使用了延时加载和缓存技术,但随着外部模板变多,HTTP请求也不可避免的变多。详细可以参阅$templateRequest$templateCache这两项服务。

    // phone-list.component.js
    angular.
    module('phoneList').
    component('phoneList', {
      // Note: The URL is relative to our `index.html` file
      templateUrl: 'phone-list/phone-list.template.html',
      controller: ...
    });
    // phone-list.template.html
    <ul>
      <li ng-repeat="phone in $ctrl.phones">
        <span>{{phone.name}}</span>
        <p>{{phone.snippet}}</p>
      </li>
    </ul>
    //最终的目录
    app/
      phone-list/
        phone-list.component.js
        phone-list.component.spec.js
        phone-list.module.js
        phone-list.template.html
      app.css
      app.js
      index.html

    第六节.双向数据绑定

    双向数据绑定是指view和model在两个方向上都绑定住了。如果我们在浏览器上改变view,那么就会反应到model里面,进而改变model。如果我们在代码里面改变model,那么就会向外反应到view上。

    第七节.依赖注入

    使用内置的http服务为例子。
    $http服务会让AngularJS在客户端发起一个http请求,在下面的例子中,http去请求一个json文件,路径是相对于index.html来说的。服务的名字需要作为参数传入到controller的构造函数中。服务的名字很重要,不能写错,注入器就是依靠名字去寻找依赖的。注入器在加载一个服务时,会分析这个服务会依赖其他哪几个服务,然后依次去加载服务。

    angular.
      module('phoneList').
      component('phoneList', {
        templateUrl: 'phone-list/phone-list.template.html',
        controller: function PhoneListController($http) {
          var self = this;//this对象在闭包中会发生变化,在这里将this保存成一个变量,该变量在闭包中的含义不会发生变化
          self.orderProp = 'age';
          //phone的数据不像之前被写死,而是由http请求去json文件里面读取
          $http.get('phones/phones.json').then(function(response) {
            self.phones = response.data;
          });
        }
      });

    AngularJS内置的服务都是带有$前缀的,如果我们要定义自己的服务,就不要带$前缀。而且我们会看到在Scope下面,有些变量是带有$$前缀的,那说明这个变量是私有的,虽然我们还是可以去访问,但不建议这么做。

    JS在发布前都会经过压缩混淆,混淆其中有个步骤就是替换局部变量,如果$http这个变量被替换了,AngularJS就识别不出来使用了内置的http服务,所以针对混淆,代码要换个结构。有以下两种方法:
    1.创建$inject属性
    $inject后面跟一组字符串,字符串就不会被js混淆替换掉,这些字符串就是要依赖的服务。

    function PhoneListController($http) {...}
    PhoneListController.$inject = ['$http'];
    ...
    .component('phoneList', {..., controller: PhoneListController});

    2.使用内联的注解
    在controller后面跟一组字符串。

    function PhoneListController($http) {...}
    ...
    .component('phoneList', {..., controller: ['$http', PhoneListController]});

    2.1使用内联的注解
    当我们使用方法2时,一般都是把构造函数也内联进来。(在刚接触AngularJS时,经常看到这种形式的内联,当时就感觉JS代码一层套一层好复杂,一句话好长,而且对$http出现2次感到不解)

    .component('phoneList', {..., controller: ['$http', function PhoneListController($http) {...}]});

    对本例代码使用2.1的注解形式:

    //app/phone-list/phone-list.component.js
    angular.
      module('phoneList').
      component('phoneList', {
        templateUrl: 'phone-list/phone-list.template.html',
        controller: ['$http',
          function PhoneListController($http) {
            var self = this;
            self.orderProp = 'age';
    
            $http.get('phones/phones.json').then(function(response) {
              self.phones = response.data;
            });
          }
        ]
      });

    第九节.路由和多视图

    依赖注射器做以下3件事:
    1.加载定义的module
    2.注册所有在module后面定义的Providers
    3.当要使用服务了,这些服务和他们的依赖,就会通过他们的Providers被传入注射依赖函数
    Providers是一个对象,这个对象可以创建相应的服务。
    在url上加前缀!是一个最佳实践,如urlhttp://127.0.0.1:8000/#!/phones

    关于自模块的依赖:

    // app/app.js
    angular.module('phonecatApp', [
      'ngRoute',
      'phoneDetail',
      'phoneList'
    ]);
    // app/phone-detail/phone-detail-module.js
    angular.module('phoneDetail', [
      'ngRoute'
    ]);

    顶层模块phonecatApp和子模块phoneDetail都包含ngRoute服务,如果我们把phoneDetail中的ngRoute去掉,程序也是可以工作的,但不建议这么做,因为这样会破坏模块化,子模块不应该依赖由父模块继承下来的服务。假如phoneDetail模块被放到其他地方,这个顶层模块没有ngRoute,那么phoneDetail模块就不能正常工作。我们不用担心多个模块包含了多份一样的服务会造成多余的浪费,AngularJS只会加载一份服务。

  • 相关阅读:
    iOS企业版打包 发布在线安装包 plist
    iOS企业版打包(转载)
    微信小程序开发入门教程
    SVN常用命令说明
    高仿QQ、微信效果的图片浏览器(支持原图和缩略图、多种手势、CocoaPods)
    iOS 调试大法
    MFMailComposeViewController发送邮件的实例
    自定义UISearchBar外观
    npm 包命令:不是内部或外部命令 问题分析
    angular 子路由跳转出现Navigation triggered outside Angular zone, did you forget to call ‘ngZone.run() 的问题修复
  • 原文地址:https://www.cnblogs.com/season-peng/p/6759516.html
Copyright © 2020-2023  润新知