AngularJS_简介、特性及基本使用_及其工作原理

转自:angularJS 的工作原理

转自:通过<script>标签引入到 HTML 中那么此时 Angular 就做为一个普通的 DOM 节点等待浏览器解析

当浏览器解析到这个节点时,发现它是一个 js 文件,那么浏览器会停止解析剩余的 DOM 节点,开始执行这个js(即angular.js)

同时 Angular 会设置一个事件监听器来监听浏览器的 DOMContentLoaded 事件

当 Angular 监听到 DOMContentLoaded 事件时,就会启动 Angular 应用

1. 初始化阶段

Angular 开始启动后,它会查找 ng-app 指令

然后初始化一系列必要的组件(即 $injector、$compile 服务以及 $rootScope),接着重新开始解析 DOM 树

2. 编译、链接阶段

$compile 服务通过遍历 DOM 树的方式查找有声明指令的 DOM 元素

当碰到带有一个或多个指令的 DOM 元素时,它会排序这些指令(基于指令的priority优先级)

然后使用 $injector 服务查找 和 收集指令的 compile 函数并执行它

每个节点的编译方法运行之后,$compile 服务就会调用链接函数。这个链接函数为绑定了封闭作用域的指令设置监控。这一行为会创建实时视图

最后,在$compile 服务完成后,AngularJS 运行时就准备好了

3. 运行阶段

Angular提供了自己的事件循环

指令自身会注册事件监听器,因此当事件被触发时,指令函数就会运行在 AngularJS 的 $digest 循环中

$digest 循环会等待 $watch 表达式列表

当检测到模型变化后,就会调用 $watch 函数,然后再次查看 $watch 列表以确保没有模型被改变

一旦 $digest 循环稳定下来,并且检测到没有潜在的变化了,执行过程就会离开 Angular 上下文并且通常会回到浏览器中,DOM 将会被渲染到这里

当你用浏览器去访问 index.html 的时候,angularJS 做了以下事情去渲染页面:

1. 加载 html,然后解析成 DOM
2. 加载 angular.js 脚本
3. AngularJS 等待 DOMContentLoaded 事件的触发
4. AngularJS 寻找 ng-app 指令,根据这个指令确定应用程序的边界
5. 使用 ng-app 中指定的模块配置$injector
6. 使用 $injector 创建 $compile 服务 和 $rootScope 
7. 使用 $compile 服务编译 DOM 并把它链接到 $rootScope 上
8. ng-init 指令对 scope 里面的变量 xxx 进行赋值
9. 对表达式 {{xxx}} 进行替换,于是乎,页面显示数据

  • angular 和浏览器进行交互,粗略来讲分为 3 个阶段:

1. 浏览器的事件回路一直等待着事件的触发,事件包括用户的交互操作、定时事件或者网络事件(如服务器的响应等)

2. 一旦有事件触发,就会进入到 Javascript 的 context 中,一般通过回调函数来修改 DOM

3. 等到回调函数执行完毕之后,浏览器又根据新的 DOM 来渲染新的页面

  • AngularJS 修改了一般的 Javascript 工作流,并且提供了它自己的事件处理机制

这样就把 Javascript 的 context 分隔成两部分

一部分是原生的 Javascript 的 context

另一部分是 AngularJS 的 context

只有处在 AngularJS 的 context 中的操作才能享受到 Angular 的 data-binding、exception handling、property watching 等服务

但是对于外来者(如原生的 Javascript 操作、自定义的事件回调、第三方的库等)Angular 也不是一概不接见

可以使用 AngularJS 提供的 $apply() 函数,将这些外来者包进 AngularJS 的 context 中,让 Angular 感知到他们产生的变化

1. 首先,浏览器会一直处于监听状态,一旦有事件被触发,就会被加到一个 event queue 中,event queue 中的事件会一个一个的执行

2. event queue 中的事件如果是被 $apply() 包起来的话,就会进入到 AngularJS 的 context 中,这里的 fn() 是我们希望在 AngularJS 的 context 中执行的函数

3. AngularJS 将执行 fn() 函数,通常情况下,这个函数会改变应用的某些状态

4. 然后 AngularJS 会进入到由两个小循环组成的 $digest 循环中

一个循环是用来处理 $evalAsync 队列的

用来 schedule 一些需要在渲染视图之前处理的操作,通常通过 setTimeout(0) 实现

速度会比较慢,可能会出现视图抖动的问题

一个循环是处理 $watch 列表的

是一些表达式的集合,一旦有改变发生,那么 $watch 函数就会被调用

$digest 循环会一直迭代知道 $evalAsync 队列为空并且 $watch 列表也为空的时候,即 model 不再有任何变化。

5. 一旦 AngularJS 的 $digest 循环结束,整个执行就会离开 AngularJS 和 Javascrip 的 context,紧接着浏览器就会把数据改变后的视图重新渲染出来

这段代码有了一个 input 来接收用户的输入

在用浏览器去访问这个 html 文件的时候,input上的 ng-model 指令会给 input 绑上keydown事件

并且会给 name 变量建立一个 $watch 来接收变量值改变的通知。

在交互阶段主要会发生以下一系列事件:

1. 当用户按下键盘上的某一个键的时候(比如说A),触发 input 上的 keydown 事件

2. input 上的指令察觉到 input 里值的变化,调用 $apply(“name=‘A'”) 更新处于 AngularJS 的 context 中的 model

3. AngularJS 将'A'赋值给 name

4. $digest 循环开始,$watch 列表检测到 name 值的变化,然后通知 {{name}} 表达式,更新 DOM

5. 退出 AngularJS 的 context,然后退出 Javascript 的 context 中的 keydown 事件

6. 浏览器重新渲染视图

AngularJS

Google 推出的开源的前端 JS 结构化框架,主体是 页面中的动态数据与内存的读取

相较于 jQuery

jQuery 是前端函数库,封装简化 DOM 操作

应用

构建单页面 SPA Web 应用____Single Page Application

将所有的活动局限于一个 html 页面 (即使页面跳转了,也是在本页面跳转)

当页面中有部分数据发生了变化,不会刷新整个页面,而是局部刷新

利用的就是 ajax 技术,路由

Web App 应用

饿了么微信网页版

后台管理应用: 阿里云、土豆后台、唯品会... ...

特性:

双向数据绑定

声明式依赖注入

解耦应用逻辑, 数据模型和视图

完善的页面指令

定制表单验证

Ajax 封装

老版本 angular-1.2.xx

 

新版本 angular-1.5.xx

 

输入框的内容,实时显示到下方:

  • $(function(){
        $('input').keyup(function(){    // 不能使用 change,在失去焦点时触发
            $('span').html(this.value);
        });
    });
    
    /*************** angular-1.2***************/

<body ng-app>    是 <body ng-app=""> 的简写____指向创建模块的名字

angularJS 不必写一行 js 代码,即可实现,且速度更快

使用:(ng 是核心模块,其他都是功能扩展模块)

1. 引入 angular.js

<script src='./js/angular-1.2.29/angular.js'></script>

2. ng-app 指令,通常 <body ng-app>____使用 插件 ng-inspector 进行数据查看

告诉 angular 核心,管理 当前标签 所包含的整个区域

并且自动创建 $rootScope 根作用域对象

3. 在管理的标签区域内使用 angularJS

ng-model    将当前输入框的值 与 xx 关联(属性名: 属性值),并且作为当前作用域对象 $rootScope 的属性

{{表达式}}    显示数据,从 当前作用域对象 $rootScope 的指定属性名上取

通常有一个返回值,可以放到任何需要的位置,

剖析: 

数据流向

页面上初始化时无数据____ng-model 是双向数据绑定

首先是从页面流向内存,再从内存流向页面的 angular 表达式 和 angular 指令

② 页面上初始化时有数据____ng-init="username='SunWuKong'"

ng-init 是单向数据绑定,从页面 view 流向 model 内存

{{表达式}} 也是单向数据绑定,从内存 model 流向 view 页面

下载: ng-inspector 插件打开的就是 angularJS 的 内存____最大的对象就是 $rootScope 

数据绑定:

数据从一个地方 A 移动(传递)到另一个地方 B____这个过程由框架去完成

双向数据绑定(视图 view <----> model 模型)---- (网页 <----> 内存)____页面主要就是 angular 指令 和 angular 表达式

数据可以从 view 流向 model,也可以从 model 流向 view

使用 ng-model 使得 页面 的数据存储到 内存

单向数据绑定

View-->Model  : ng-init

Model-->View  : {{表达式}} 去内存中获取数据,渲染到页面

依赖注入

依赖对象: 完成某个特定功能必须要某个对象才能实现____比如 定义子对象,必须形参 $scope

依赖注入: 依赖对象 以形参的形式 被注入进来使用____声明式依赖注入 (对应的一个叫 命令式依赖注入'遍历指定数组每个元素')

命令式 更注重过程 (解答题)

声明式 更注重结果,是对命令式的局部包装 (选择题)

又由于代码压缩,会改变 形参 的命名,所以必须使用 ["形参", function(形参){...}] 显示声明依赖注入

MVC 模式

M: Model, 即模型, 储存数据的容器, 提供操作数据的方法

在 angular 中为 scope

V: View, 即视图, 显示 Model 的数据, 将数据同步到 Model, 与用户交互

在 angular 中为 页面,包括: html/css/directive/expression

C: Controller, 即控制器, 初始化 Model 数据, 为 Model 添加行为方法

在 angular 中为 angular 的 ng-controller

MVVM 模式

在 MVVM 中 angular 的 controller 不再是架构的核心

angular 的 controller 只是起辅助作用,用来辅助 $scope 对象,即 VM 层

M: Model, 即数据模型

在 angular 中为 scope 中的各个数据对象

V: View, 即视图

在 angular 中为 页面

VM: ViewModel, 即视图模型

在 angular 中为 scope 对象

根作用域对象

一个 js 实例对象,ng-app 指令会默认创建一个 根作用域对象 $rootSScope

作用域对象

根作用域对象的 属性和方法 与页面中的 指令/表达式 是关联的

控制器对象

用于控制 angularJS 应用数据的 实例对象

ng-controller 指定控制器构造函数____形参必须是 $scope____依赖注入

angular 会自动 new 这个构造函数创建控制器对象

同时 还会创建一个新的作用域对象 $scope ,  它是 $rootScope 的 (继承关系)子对象____$scope____作用域对象

(不可能用 ng-init 初始化所有对象)

  • API: ng/function/angular.module()____一个参数是获取,多个参数是创建

可以在全局位置创建、注册、获取 Angular 模块

所有模块都必须使用这个机制注册 才能在应用中生效

使用:

1. 创建模块对象        <body ng-app="myApp">____ng-app 指向创建模块的名字

var myModule = angular.module("myApp", []);

2. 生成作用域对象____取代 自定义构造函数

myModule.controller("myController1", function($scope){

$scope.empName = "SunWuKong";

});

myModule.controller("myController2", function($scope){

$scope.empName = "ZhuBaJie";

});

--------------------- 优化 ----------------------链式调用 (返回值为 作用域对象)

angular.module("myApp", []).controller("myController1", function($scope){

$scope.empName = "SunWuKong";

}).controller("myController2", function($scope){    // 隐式声明依赖注入

$scope.empName = "ZhuBaJie";

});

注意:

js 代码压缩时,会改变形参,导致 angular 无法解析

解决: 显示声明依赖注入

angular.module("myApp", []).controller("myController1", ['$scope', function($scope){

$scope.empName = "SunWuKong";

}]).controller("myController2", ['$scope', function($scope){

$scope.empName = "ZhuBaJie";

}]);

  • {{表达式}}

单项数据绑定

如果是变量 会在作用域链中寻找变量 {{abc.split("").reverse().join("")}}____ 字符串反转

还可以是 {{123}}{{'abc'}}{{true}}

{{null}}、{{undefine}}、{{NaN}}、{{Infinity}} 会被解析成 空串 "",不显示任何内容 

  • 项目

一个项目下来,首先是 UI 设计页面效果  

UI 从 14 年开始火,比 美工 高一个档次  

然后前端工程师分析 UI 设计图,进行静态 html 页面设计 

接下来是,数据的动态展现,与后端交互

然后是用户的交互,数据的展现

angularJS 开发第一步就是 新建 ng-app="myApp"

ng-controller 实现子作用域对象 $scope

ng-model 实现双向数据绑定

{{表达式}} 从内存显示数据到页面

  • 常用指令
  • 点击事件

<button ng-click="getTotalPrice()">计算</button>

  • 遍历数组显示数据____数据有几个数组就会产生几个新的作用域

<div>

<h2>人员信息列表</h2>

<ul>

<li  ng-repeat="person  in persons">

{{$index}} ---- {{person.username}} ---- {{person.age}}

{{$first}} ---- 第一个 返回 true

{{$last}} ---- 最后一个 返回 true

{{$odd}} ---- 奇数返回 true,从 1 开始计算

{{$even}} ---- 偶数返回 true,从 1 开始

</li>

</ul>

</div>

 

  • ng-bind 指令 解决数据闪屏:由于用户网速不好,第一次打开页面会看到 {{表达式}}

浏览器引擎从上往下解析

因为浏览器还没有解析到引入的 angular.js,所以页面还没有生效,导致了 数据闪屏

<div>

<p>{{123}}</p>

优化为:

<p ng-bind="123"></p>

</div>

  • ng-show 布尔类型,如果为 true 则 显示
  • ng-hide 布尔类型,如果为 true 则 隐藏

<div>

<button ng-click="switchLike()">切换喜欢</button>

<p ng-show="isLike">我喜欢刘亦菲</p>

<p ng-hide="isLike">刘亦菲喜欢我</p>

</div>

------------------- javascript ------------------

$scope.isLike = true;

$scope.switchLike = function(){

$scope.isLike  = !$scope.isLike ;

};

---------------------------------------- 控制 css 杨样式 --------------------------------------------------

  • ng-style  动态引用 js 指定的的样式对象 {"color":"blue", "backgrouond": "red"};

  • ng-mouseenter
  • ng-mouseleave

  • ng-class 动态引用定义的样式    {"aClass": true, bClass: true};

 

字数统计,实时显示

  • <html>
        <head>
            <meta charset="UTF-8">
            <style type="text/css">
                textarea {
                    resize: none;
                }
            </style>
        </head>
        <body ng-app="myApp">
            <div ng-controller="MyCtrl" >
                <h2>个性签名:</h2>
                <textarea cols="30" rows="10" ng-model="words"></textarea>
                <div>
                    <button ng-click="saveWords()">保存</button>
                    <button ng-click="readWords()">读取</button>
                    <button ng-click="clearWords()">删除</button>
                </div>
                <p>剩余字数: <span ng-bind="getCount()"></span></p>
            </div>
            
            <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.10/angular.js"></script>
            <script type="text/javascript">
                var limitLength = 100;    // 字数限制 100 字
                //window.onDOMContentLoaded = function(){    // 会报错 ---- 看上面的工作原理
                    angular.module("myApp", []).controller("MyCtrl", ["$scope", function($scope){
                   $scope.words = "哈哈";
                        
                   $scope.getCount = function(){
                        $scope.wordsLength = limitLength - $scope.words.length;
                            
                        if($scope.words.length > 100){    // 超出字数限制,让用户输入失效
                             $scope.words = $scope.words.slice(0, limitLength);
                        };
                            
                        return $scope.wordsLength;
                    };
                        
                    $scope.saveWords = function(){
                        sessionStorage.setItem("session_key", JSON.stringify($scope.words));
                        console.log("已经保存到 sessionStorage ");
                    };
                        
                    $scope.readWords = function(){
                        $scope.words = JSON.parse(sessionStorage.getItem("session_key")) || "";    // 如果读不到,则返回 null
                        console.log("已经读取 sessionStorage ");
                    };
                        
                    $scope.clearWords = function(){
                        sessionStorage.removeItem("session_key");
                        $scope.words = "";
                    };
                 }]);
             //};
            </script>
        </body>
    </html>

数据的动态展示

  • <html>
        <head>
            <meta charset="UTF-8">
            <style type="text/css">
                textarea {
                    resize: none;
                }
            </style>
        </head>
        <body ng-app="myApp">
            <div ng-controller="MyCtrl" >
                <h2>Hero  </h2>
                
                <div>
                    <input type="text" ng-model="heroName"/>
                    <button ng-click="addHero()">添加</button>
                </div>
                
                <div ng-repeat="each in heros">
                    <input type="checkbox" ng-model="each.isChecked" /><span ng-bind="each.name"></span>
                </div>
                
                <button ng-click="removeHreo()">删除所选对象</button>
            </div>
            
            <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.10/angular.js"></script>
            <script type="text/javascript">
                // window.onDOMContentLoaded = function(){    // 会报错 ---- 看上面的工作原理
                    angular.module("myApp", []).controller("MyCtrl", ["$scope", function($scope){
                    $scope.heros = [
                        {"name": "Groot", "isChecked": false},
                        {"name": "玩锤子的", "isChecked": false},
                        {"name": "浩克", "isChecked": false},
                        {"name": "洛基", "isChecked": false}
                    ];
                        
                    $scope.addHero = function(){
                        var isExit = false;
                        $scope.heros.forEach(function(each, index){
                            if(each.name === $scope.heroName){
                                isExit = true;
                                $scope.heroName = "";
                            };
                        });
                            
                        if($scope.heroName && !isExit){
                            $scope.heros.unshift({"name": $scope.heroName, "isChecked": false});
                            $scope.heroName = "";
                        };
                    };
                    /****  // 方式1 ---- 删除 数组中不符合条件的元素
                    $scope.removeHreo = function(){
                         $scope.heros.forEach(function(each, index){
                             if(each.isChecked){
                                 $scope.heros.splice(index, 1);    // 从 index 开始删,删 1 个
                                 $scope.removeHreo();
                             };
                         });
                    };
                    ****/
                           // 方式2 ---- 保留 数组中符合条件的元素
                           $scope.removeHreo = function(){
                         var tempArr = $scope.heros;
                         $scope.heros = [];
                         tempArr.forEach(function(each, index){
                             if(!each.isChecked){
                                 $scope.heros.push(each);
                             };
                         });
                     };
                 }]);
             // };
            </script>
        </body>
    </html>

 

posted @ 2019-01-05 00:14  耶梦加德  阅读(1562)  评论(0编辑  收藏  举报