Angular 2 Change Detection - 2

更新时间 - 2017-03-20 16:15;
更新内容 - 我有话说模块

Angular 2 Change Detection - 1 文章中,我们介绍了浏览器渲染、Zone、NgZone 的概念,本文将详细介绍 Angular 2 组件中的变化检测器。

组件和变化检测器

如你所知,Angular 2 应用程序是一颗组件树,而每个组件都有自己的变化检测器,这意味着应用程序也是一颗变化检测器树。顺便说一句,你可能会想。是由谁来生成变化检测器?这是个好问题,它们是由代码生成。 Angular 2 编译器为每个组件自动创建变化检测器,而且最终生成的这些代码 JavaScript VM友好代码。这也是为什么新的变化检测是快速的 (相比于 Angular 1.x 的 $digest)。基本上,每个组件可以在几毫秒内执行数万次检测。因此你的应用程序可以快速执行,而无需调整性能。

另外在 Angular 2 中,任何数据都是从顶部往底部流动,即单向数据流。下图是 Angular 1.x 与 Angular 2 变化检测的对比图:

让我们来看一下具体例子:

child.component.ts

import { Component,Input } from '@angular/core';

@Component({
    selector: 'exe-child',template: `
     <p>{{ text }}</p>
    `
})
export class ChildComponent {
    @Input() text: string;
}

parent.component.ts

import { Component,Input } from '@angular/core';

@Component({
    selector: 'exe-parent',template: `
     <exe-child [text]="name"></exe-child> 
    `
})
export class ParentComponent {
    name: string = 'Semlinker';
}

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'exe-app',template: `
    <exe-parent></exe-parent>
  `
})
export class AppComponent{ }

变化检测总是从根组件开始。上面的例子中,ParentComponent 组件会比 ChildComponent 组件更早执行变化检测。因此在执行变化检测时 ParentComponent 组件中的 name 属性,会传递到 ChildComponent 组件的输入属性 text 中。此时 ChildComponent 组件检测到 text 属性发生变化,因此组件内的 p 元素内的文本值从空字符串 变成 'Semlinker' 。这虽然很简单,但很重要。另外对于单次变化检测,每个组件只检查一次。

OnChanges

当组件的任何输入属性发生变化的时候,我们可以通过组件生命周期提供的钩子 ngOnChanges 来捕获变化的内容。具体示例如下:

import { Component,Input,OnChanges,SimpleChange } from '@angular/core';

@Component({
    selector: 'exe-child',template: `
     <p>{{ text }}</p>
    `
})
export class ChildComponent implements OnChanges{
    @Input() text: string;

    ngOnChanges(changes: {[propName: string]: SimpleChange}) {
        console.dir(changes['text']);    
    }
}

以上代码运行后,控制台的输出结果:

我们看到当输入属性变化的时候,我们可以通过组件提供的生命周期钩子 ngOnChanges 捕获到变化的内容,即 changes 对象,该对象的内部结构是 key-value 键值对的形式,其中 key 是输入属性的值,value 是一个 SimpleChange 对象,该对象内包含了 previousValue (之前的值) 和 currentValue (当前值)。

需要注意的是,如果在组件内手动改变输入属性的值,ngOnChanges 钩子是不会触发的。具体示例如下:

import { Component,template: `
     <p>{{ text }}</p>
     <button (click)="changeTextProp()">改变Text属性</button>
    `
})
export class ChildComponent implements OnChanges {
    @Input() text: string;

    ngOnChanges(changes: { [propName: string]: SimpleChange }) {
        console.dir(changes['text']);
    }

    changeTextProp() {
        this.text = 'Text属性已改变';
    }
}

当你点击 '改变Text属性' 的按钮时,发现页面中 p 元素的内容会从 'Semlinker' 更新为 'Text属性已改变' ,但控制台却没有输出任何信息,这验证了我们刚才给出的结论,即在组件内手动改变输入属性的值,ngOnChanges 钩子是不会触发的。

变化检测性能优化

在介绍如何优化变化检测的性能前,我们先来看几张图:

变化检测前:

变化检测时:

我们发现每次变化检测都是从根组件开始,从上往下执行。虽然 Angular 2 优化后的变化检测执行的速度很快,但我们能否只针对那些有变化的组件才执行变化检测或灵活地控制变化检测的时机呢 ? 答案是有的,接下来我们看一下具体怎么进行优化。

变化检测策略

在 Angular 2 中我们可以在定义组件的 metadata 信息时,设定每个组件的变化检测策略。接下来我们来看一下具体示例:

profile-name.component.ts

import { Component,Input} from '@angular/core';

@Component({
    selector: 'profile-name',template: `
        <p>Name: {{name}}</p>
    `
})
export class ProfileNameComponent {
   @Input() name: string;
}

profile-age.component.ts

import { Component,Input } from '@angular/core';

@Component({
    selector: 'profile-age',template: `
        <p>Age: {{age}}</p>
    `
})
export class ProfileAgeComponent {
    @Input() age: number;
}

profile-card.component.ts

import { Component,Input } from '@angular/core';

@Component({
    selector: 'profile-card',template: `
       <div>
         <profile-name [name]='profile.name'></profile-name>
         <profile-age [age]='profile.age'></profile-age>
       </div>
    `
})
export class ProfileCardComponent {
    @Input() profile: { name: string; age: number };
}

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'exe-app',template: `
    <profile-card [profile]='profile'></profile-card>
  `
})
export class AppComponent {
  profile: { name: string; age: number } = {
    name: 'Semlinker',age: 31
  };
}

上面代码中 ProfileCardComponent 组件,有一个 profile 输入属性,而且它的模板视图只依赖于该属性。如果使用默认的检测策略,每当发生变化时,都会从根组件开始,从上往下在每个组件上执行变化检测。但如果 ProfileCardComponent 中的 profile 输入属性没有发生变化,是没有必要再执行变化检测。针对这种情况,Angular 2 为我们提供了 OnPush 的检测策略。

OnPush策略

import { Component,ChangeDetectionStrategy } from '@angular/core';

@Component({
    selector: 'profile-card',template: `
       <div>
         <profile-name [name]='profile.name'></profile-name>
         <profile-age [age]='profile.age'></profile-age>
       </div>
    `,changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProfileCardComponent {
    @Input() profile: { name: string; age: number };
}

当使用 OnPush 策略的时候,若输入属性没有发生变化,组件的变化检测将会被跳过,如下图所示:

实践是检验真理的唯一标准,我们马上来个例子:

app.component.ts

import { Component,OnInit } from '@angular/core';

@Component({
  selector: 'exe-app',template: `
    <profile-card [profile]='profile'></profile-card>
  `
})
export class AppComponent implements OnInit{

  profile: { name: string; age: number } = {
    name: 'Semlinker',age: 31
  };

 ngOnInit() {
   setTimeout(() => {
    this.profile.name = 'Fer';
   },2000);
 }
}

以上代码运行后,浏览器的输出结果:

我们发现虽然在 AppComponent 组件中 profile 对象中的 name 属性已经被改变了,但页面中名字的内容却未同步刷新。在进一步分析之前,我们先来介绍一下 Mutable 和 Immutable 的概念。

Mutable(可变) and Immutable(不可变)

在 JavaScript 中默认所有的对象都是可变的,即我们可以任意修改对象内的属性:

var person = {
    name: 'semlinker',age: 31
};
person.name = 'fer'; 
console.log(person.name); // Ouput: 'fer'

上面代码中我们先创建一个 person 对象,然后修改 person 对象的 name 属性,最终输出修改后的 name 属性。接下来我们调整一下上面的代码,调整后的代码如下:

var person = {
    name: 'semlinker',age: 31
};

var aliasPerson = person;
person.name = 'fer';
console.log(aliasPerson === person); // Output:  true

在修改 person 对象前,我们先把 person 对象赋值给 aliasPerson 变量,在修改完 person 对象的属性之后,我们使用 === 比较 aliasPerson 与 person,发现输出的结果是 true。也许你已经知道了,我们刚才在 AppComponent 中模型更新了,但视图却未同步更新的原因。

接下来我们来介绍一下 Immutable

Immutable 即不可变,表示当数据模型发生变化的时候,我们不会修改原有的数据模型,而是创建一个新的数据模型。具体示例如下:

var person = {
    name: 'semlinker',age: 31
};

var newPerson = Object.assign({},person,{name: 'fer'});

console.log(person.name,newPerson.name); // Output: 'semliker' 'fer'
console.log(newPerson === person); // Output: false

这次要修改 person 对象中的 name 属性,我们不是直接修改原有对象,而是使用 Object.assign 方法创建一个新的对象。介绍完 Mutable 和 Immutable 的概念 ,我们回过头来分析一下 OnPush 策略,该策略内部使用 looseIdentical 函数来进行对象的比较,looseIdentical 的实现如下:

export function looseIdentical(a: any,b: any): boolean {
  return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b);
}

因此当我们使用 OnPush 策略时,需要使用的 Immutable 的数据结构,才能保证程序正常运行。为了提高变化检测的性能,我们应该尽可能在组件中使用 OnPush 策略,为此我们组件中所需的数据,应仅依赖于输入属性。

OnPush 策略是提高应用程序性能的一个简单而好用的方法。不过,我们还有其他方法来获得更好的性能。 即使用 Observable 与 ChangeDetectorRef 对象提供的 API,来手动控制组件的变化检测行为。

ChangeDetectorRef

ChangeDetectorRef 是组件的变化检测器的引用,我们可以在组件中的通过依赖注入的方式来获取该对象:

import { ChangeDetectorRef } from '@angular/core';

@Component({}) class MyComponent {
    constructor(private cdRef: ChangeDetectorRef) {}
}

ChangeDetectorRef 变化检测类中主要方法有以下几个:

export abstract class ChangeDetectorRef {
  abstract markForCheck(): void;
  abstract detach(): void;
  abstract detectChanges(): void;
  abstract reattach(): void;
}

其中各个方法的功能介绍如下:

  • markForCheck() - 在组件的 metadata 中如果设置了 changeDetection: ChangeDetectionStrategy.OnPush 条件,那么变化检测不会再次执行,除非手动调用该方法。

  • detach() - 从变化检测树中分离变化检测器,该组件的变化检测器将不再执行变化检测,除非手动调用 reattach() 方法。

  • reattach() - 重新添加已分离的变化检测器,使得该组件及其子组件都能执行变化检测

  • detectChanges() - 从该组件到各个子组件执行一次变化检测

接下来我们先来看一下 markForCheck() 方法的使用示例:

child.component.ts

import { Component,OnInit,ChangeDetectionStrategy,ChangeDetectorRef } from '@angular/core';

@Component({
    selector: 'exe-child',template: `
     <p>当前值: {{ counter }}</p>
    `,changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
    @Input() counter: number = 0;

    constructor(private cdRef: ChangeDetectorRef) {}
    
    ngOnInit() {
        setInterval(() => {
            this.counter++;
            this.cdRef.markForCheck();
        },1000);
    }
}

parent.component.ts

import { Component,ChangeDetectionStrategy } from '@angular/core';

@Component({
    selector: 'exe-parent',template: `
     <exe-child></exe-child> 
    `,changeDetection: ChangeDetectionStrategy.OnPush
})
export class ParentComponent { }

ChildComponent 组件设置的变化检测策略是 OnPush 策略,此外该组件也没有任何输入属性。那么我们应该怎么执行变化检测呢 ?我们看到在 ngOnInit 钩子中,我们通过 setInterval 定时器,每隔一秒钟更新计数值同时调用 ChangeDetectorRef 对象上的 markForCheck() 方法,来标识该组件在下一个变化检测周期,需执行变化检测,从而更新视图。

接下来我们来讲一下 detach() 和 reattach() 方法,它们用来开启/关闭组件的变化检测。让我们看下面的例子:

child.component.ts

import { Component,template: `
     Detach: <input type="checkbox" 
        (change)="detachCD($event.target.checked)">
     <p>当前值: {{ counter }}</p>
    `
})
export class ChildComponent implements OnInit {
    counter: number = 0;
  
    constructor(private cdRef: ChangeDetectorRef) { }

    ngOnInit() {
        setInterval(() => {
            this.counter++;
        },1000);
    }

    detachCD(checked: boolean) {
        if (checked) {
            this.cdRef.detach();
        } else {
            this.cdRef.reattach();
        }
    }
}

该组件有一个用于移除或添加变化检测器的复选框。 当复选框被选中时,detach() 方法将被调用,之后组件及其子组件将不会被检查。当取消选择时,reattach() 方法会被调用,该组件将会被重新添加到变化检测器树上。

Observables

使用 Observables 机制提升性能和不可变的对象类似,但当发生变化的时候,Observables 不会创建新的模型,但我们可以通过订阅 Observables 对象,在变化发生之后,进行视图更新。使用 Observables 机制的时候,我们同样需要设置组件的变化检测策略为 OnPush。我们马上看个例子:

counter.component.ts

import { Component,ChangeDetectorRef } from '@angular/core';
import { Observable } from 'rxjs/Rx';

@Component({
    selector: 'exe-counter',template: `
      <p>当前值: {{ counter }}</p>
    `,changeDetection: ChangeDetectionStrategy.OnPush
})
export class CounterComponent implements OnInit {
    counter: number = 0;

    @Input() addStream: Observable<any>;

    constructor(private cdRef: ChangeDetectorRef) { }

    ngOnInit() {
        this.addStream.subscribe(() => {
            this.counter++;
            this.cdRef.markForCheck();
        });
    }
}

app.component.ts

import { Component,OnInit } from '@angular/core';
import { Observable } from 'rxjs/Rx';

@Component({
  selector: 'exe-app',template: `
   <exe-counter [addStream]='counterStream'></exe-counter>
  `
})
export class AppComponent implements OnInit {
  counterStream: Observable<any>;
  
  ngOnInit() {
     this.counterStream = Observable.timer(0,1000); 
  }
}

现在我们来总结一下变化检测的原理:Angular 应用是一个响应系统,变化检测总是从根组件到子组件这样一个从上到下的顺序开始执行,它是一棵线性的有向树,默认情况下,变化检测系统将会走遍整棵树,但我们可以使用 OnPush 变化检测策略,在结合 Observables 对象,进而利用 ChangeDetectorRef 实例提供的方法,来实现局部的变化检测,最终提高系统的整体性能。

我有话说

1.ChangeDetectionStrategy 变化检测策略总共有几种 ?

export declare enum ChangeDetectionStrategy {
    OnPush = 0,// 变化检测器的状态值是 CheckOnce
    Default = 1,// 组件默认值 - 变化检测器的状态值是 CheckAlways,即始终执行变化检测
}

2.变化检测器的状态有哪几种 ?

export declare enum ChangeDetectorStatus {
    CheckOnce = 0,// 表示在执行detectChanges之后,变化检测器的状态将会变成Checked
    Checked = 1,// 表示变化检测将被跳过,直到变化检测器的状态恢复成CheckOnce
    CheckAlways = 2,// 表示在执行detectChanges之后,变化检测器的状态始终为CheckAlways
    Detached = 3,// 表示该变化检测器树已从根变化检测器树中移除,变化检测将会被跳过
    Errored = 4,// 表示在执行变化检测时出现异常
    Destroyed = 5,// 表示变化检测器已被销毁
}

3.markForCheck()、detectChanges()、detach()、reattach() (@angular/core version: 2.2.4)

markForCheck()

ViewRef_.prototype.markForCheck = function () { 
  this._view.markPathToRootAsCheckOnce(); 
};

AppView.prototype.markPathToRootAsCheckOnce = function () {
    var c = this;
    while (isPresent(c) && c.cdMode !== ChangeDetectorStatus.Detached) {
       if (c.cdMode === ChangeDetectorStatus.Checked) {
              c.cdMode = ChangeDetectorStatus.CheckOnce;
           }
           if (c.type === ViewType.COMPONENT) {
                    c = c.parentView;
           }
           else {
                    c = c.viewContainer ? 
                       c.viewContainer.parentView : null;
           }
        }
};

detectChanges()

ViewRef_.prototype.detectChanges = function () {
   this._view.detectChanges(false);
   triggerQueuedAnimations();
};

AppView.prototype.detectChanges = function (throwOnChange) {
   var s = _scope_check(this.clazz);
   if (this.cdMode === ChangeDetectorStatus.Checked ||
         this.cdMode === ChangeDetectorStatus.Errored ||
         this.cdMode === ChangeDetectorStatus.Detached)
         return;
         
   if (this.cdMode === ChangeDetectorStatus.Destroyed) {
         this.throwDestroyedError('detectChanges');
   }
   
        this.detectChangesInternal(throwOnChange);
   if (this.cdMode === ChangeDetectorStatus.CheckOnce)
         this.cdMode = ChangeDetectorStatus.Checked;
         this.numberOfChecks++;
         wtfLeave(s);
};

detach()

ViewRef_.prototype.detach = function () {
  this._view.cdMode = ChangeDetectorStatus.Detached; 
};

reattach()

ViewRef_.prototype.reattach = function () {
  this._view.cdMode = this._originalMode;
  this.markForCheck();
};

总结

通过两篇文章,我们详细介绍了 Angular 2 变化检测的内容,此外在了解变化检测的基础上,我们还介绍了如何基于 OnPush 变化检测策略,进行变化检测的优化,从而提高应用程序的性能。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


ANGULAR.JS:NG-SELECTANDNG-OPTIONSPS:其实看英文文档比看中文文档更容易理解,前提是你的英语基础还可以。英文文档对于知识点讲述简明扼要,通俗易懂,而有些中文文档读起来特别费力,基础差、底子薄的有可能一会就会被绕晕了,最起码英文文档中的代码与中文文档中的代码是一致的,但知识点讲述实在是差距太大。Angular.jshasapowerfuldire
AngularJS中使用Chart.js制折线图与饼图实例  Chart.js 是一个令人印象深刻的 JavaScript 图表库,建立在 HTML5 Canvas 基础上。目前,它支持6种图表类型(折线图,条形图,雷达图,饼图,柱状图和极地区域区)。而且,这是一个独立的包,不依赖第三方 JavaScript 库,小于 5KB。   其中用到的软件:   Chart.js框架,版本1.0.2,一
IE浏览器兼容性后续前言 继续尝试解决IE浏览器兼容性问题,结局方案为更换jquery、angularjs、IE的版本。 1.首先尝试更换jquery版本为1.7.2 jquery-1.9.1.js-->jquery-1.7.2.js--> jquery2.1.4.js 无效 2.尝试更换IE版本IE8 IE11-
Angular实现下拉菜单多选写这篇文章时,引用文章地址如下:http://ngmodules.org/modules/angularjs-dropdown-multiselecthttp://dotansimha.github.io/angularjs-dropdown-multiselect/#/AngularJSDropdownMultiselectThisdire
在AngularJS应用中集成科大讯飞语音输入功能前言 根据项目需求,需要在首页搜索框中添加语音输入功能,考虑到科大讯飞语音业务的强大能力,遂决定使用科大讯飞语音输入第三方服务。软件首页截图如下所示: 涉及的源代码如下所示: //语音识别$rootScope.startRecognize = function() {var speech;
Angular数据更新不及时问题探讨前言 在修复控制角标正确变化过程中,发觉前端代码组织层次出现了严重问题。传递和共享数据时自己使用的是rootScope,为此造成了全局变量空间的污染。根据《AngularJs深度剖析与最佳实践》,如果两个控制器的协作存在大量的数据共享和交互可以利用Factory等服务的“单例”特性为它们注入一个共享对象来传递数据。而自己在使用rootScope
HTML:让表单、文本框只读,不可编辑的方法有时候,我们希望表单中的文本框是只读的,让用户不能修改其中的信息,如使中国">的内容,"中国"两个字不可以修改。实现的方式归纳一下,有如下几种。方法1:onfocus=this.blur()中国"onfocus=this.blur()>方法2:readonly中国"readonly>中国"readonly="tru
在AngularJS应用中实现微信认证授权遇到的坑前言 项目开发过程中,移动端新近增加了一个功能“微信授权登录”,由于自己不是负责移动端开发的,但最后他人负责的部分未达到预期效果。不能准确实现微信授权登录。最后还得靠自己做进一步的优化工作,谁让自己是负责人呢?原来负责人就是负责最后把所有的BUG解决掉。 首先,熟悉一下微信授权部分的源代码,如下所示:
AngularJS实现二维码信息的集成思路需求 实现生成的二维码包含订单详情信息。思路获取的内容数据如下: 现在可以获取到第一级数据,第二级数据data获取不到。利用第一级数据的获取方法获取不到第二级数据。for(i in data){alert(i); //获得属性 if(typeof(data[i]) == "o
Cookie'data'possiblynotsetoroverflowedbecauseitwastoolarge(5287>4096bytes)!故事起源 项目开发过程中遇到以上问题,刚开始以为只是个警告,没太在意。后来发现直接影响到了程序的执行效果。果断寻找解决方法。问题分析 根据Chrome浏览器信息定位,显示以下代码存在错误:
AngularJS控制器controller之间如何通信angular控制器通信的方式有三种:1,利用作用域继承的方式。即子控制器继承父控制器中的内容2,基于事件的方式。即$on,$emit,$boardcast这三种方式3,服务方式。写一个服务的单例然后通过注入来使用利用作用域的继承方式由于作用域的继承是基于js的原型继承方式,所以这里分为两种情况,当作用域上面的值
AngularJS路由问题解决遇到了一个棘手的问题:点击优惠详情时总是跳转到药店详情页面中去。再加一层地址解决了,但是后来发现问题还是来了:Couldnotresolve'yhDtlMaintain/yhdetail'fromstate'yhMaintain'药店详情http://192.168.1.118:8088/lmapp/index.html#/0优惠券详情
书海拾贝之特殊的ng-src和ng-href在说明这两个指令的特殊之前,需要先了解一下ng的启动及执行过程,如下:1)浏览器加载静态HTML文件并解析为DOM;2)浏览器加载angular.js文件;3)angular监听DOMContentLoaded事件,监听到时开始启动;4)angular寻找ng-app指令,确定作用范围;
angularjs实现页面跳转并进行参数传递Angular页面传参有多种办法,我在此列举4种最常见的:1.基于ui-router的页面跳转传参(1)在AngularJS的app.js中用ui-router定义路由,比如现在有两个页面,一个页面(producers.html)放置了多个producers,点击其中一个目标,页面跳转到对应的producer页,同时将producerId
AngularJS实现表格数据的编辑,更新和删除效果实现首先,我们先建立一些数据,当然你可以从你任何地方读出你的数据var app = angular.module('plunker', ['ui.bootstrap']);app.controller('MainCtrl', function($scope) { $scope.name = 'World'; $sc
ANGULAR三宗罪之版本陷阱      坑!碰到个大坑,前面由于绑定日期时将angular版本换为angular-1.3.0-beta.1时,后来午睡后,登录系统,发现无论如何都登陆不进去了,经过调试,发现数据视图已经无法实现双向绑定了。自己还以为又碰到了“僵尸程序”了呢,对比药店端的程序发现并没有什么不同之处。后来自己经过一番思索才隐约感觉到是不是angular的版本造成的,将版本换为之前
JS实现分页操作前言 项目开发过程中,进行查询操作时有可能会检索出大量的满足条件的查询结果。在一页中显示全部查询结果会降低用户的体验感,故需要实现分页显示效果。受前面“JS实现时间选择插件”的启发,自己首先需要查看一下HTML5能否实现此效果。 整了半天,不管是用纯CSS3也好,还是用tmpagination.js还是bootstrap组件也好,到最后自己静下心来理
浏览器兼容性解决之道前言 浏览器兼容性一直是前端开发中不得不面对的一个问题。而最突出的就是IE。对绝大多数公司来说,兼容IE6的性价比已经很低,而IE7则几乎已经绝迹。所以,常见的兼容性下限是IE8。这也正是Angular1.2x的兼容性目标,Angular团队声明:Angular的持续集成服务器会在IE8下运行所有的测试。但这些测试不会运行在IE7及以下版本,它们也不会保证An
JS利用正则表达式校验手机号绪 由于项目需求,需要在前端实现手机号码的校验。当然了,对于基本的格式校验应该放在客户端进行,而不需要再将待校验的手机号发送至服务端,在服务端完成校验,然后将校验结果返回给客户端,客户端根据返回的结果再进行进一步的处理。如此反而复杂化了处理过程。 其实,处于安全考虑,应该在服务端进行二次校验。以下为在客户端的JS中校验手机号码格式
基于项目实例解析ng启动加载过程前言 在AngularJS项目开发过程中,自己将遇到的问题进行了整理。回过头来总结一下angular的启动过程。 下面以实际项目为例进行简要讲解。1.载入ng库2.等待,直到DOM树构造完毕。3.发现ng-app,自动进入启动引导阶段。4.根据ng-app名称找到相应的路由。5.加载默认地址。6.Js顺序执行,加载相应模版页sys_tpls/