angular之中,$state $watch $scope $rootScope 分别是什么?

本文深入解析AngularJS中的关键概念,包括$scope、$rootScope、$watch及$state的作用与使用方法。介绍了它们如何帮助开发者实现数据绑定、状态管理和路由等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里是修真院前端小课堂,每篇分享文从

八个方面深度解析前端知识/技能,本篇分享的是:

【angular之中,$state $watch $scope $rootScope 分别是什么? 】

1.背景介绍

AngularJs是一个用于设计动态web应用的结构框架,使得开发现代的单一页面应用程序变得更加容易。而且能够简化我们的工作流程, 让我们只专心于业务逻辑和数据的处理。$scope $rootScope $watch 和 $state就是里面的几个方法。

2.知识剖析

1、 s c o p e — — S c o p e ( 作 用 域 ) 是 应 用 在 H T M L ( 视 图 ) 和 J a v a S c r i p t ( 控 制 器 ) 之 间 的 纽 带 。 S c o p e 是 一 个 对 象 , 有 可 用 的 方 法 和 属 性 。 S c o p e 可 应 用 在 视 图 和 控 制 器 上 。 scope——Scope(作用域) 是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带。Scope 是一个对象,有可用的方法和属性。 Scope 可应用在视图和控制器上。 scopeScope()HTML()JavaScript()ScopeScopescope 的使用贯穿整个 Angular App 应用,它与数据模型相关联,同时也是表达式执行的上下文。 有了 $scope 就在视图和控制器之间建立了一个通道,基于作用域视图在修改数据时会立刻更新 $scope, 同样的 $scope发生改变时也会立刻重新渲染视图。

2、$rootscope——每一个Angular应用都会有一个 $rootScope。这个 $rootScope 是最顶级的scope,它对应着含有 ng-app 指令属性的那个DOM元素。 如果页面上没有明确设定 $scope ,Angular 就会把数据和函数都绑定到这里。Angular 应用启动并生成视图时,会将根 ng-app 元素与 r o o t S c o p e 进 行 绑 定 . rootScope 进行绑定. rootScope.rootScope 是所有 $scope 的最上层对象, 可以理解为一个 Angular 应用中得全局作用域对象。

3、$scope 的生命周期有4个阶段:

(1)创建:控制器或者指令创建时, Angular 会使用 $injector 创建一个新的作用域,然后在控制器或指令运行时,将作用域传递进去。

(2)链接:Angular 启动后会将所有 $scope 对象附加或者说链接到视图上,所有创建 $scope 对象的函数也会被附加到视图上。 这些作用域将会注册当 Angular 上下文发生变化时需要运行的函数.也就是 $watch 函数, Angular 通过这些函数或者何时开始事件循环。

(3)更新:一旦事件循环开始运行,就会开始执行自己的脏值检测.一旦检测到变化,就会触发 $scope 上指定的回调函数。

(4)销毁:通常来讲如果一个 $scope 在视图中不再需要, 需要对其进行清理。

4、 w a t c h — — 相 信 使 用 过 a n g u l a r j s 的 同 学 都 知 道 , n g 中 有 个 比 较 重 要 的 特 点 , 叫 做 双 向 绑 定 , 那 么 这 个 双 向 绑 定 是 如 何 实 现 的 呢 ? 当 我 们 在 对 绑 定 的 n a m e 属 性 进 行 修 改 的 时 候 , a n g u l a r 内 部 的 watch——相信使用过angularjs的同学都知道,ng中有个比较重要的特点,叫做双向绑定,那么这个双向绑定是如何实现的呢? 当我们在对绑定的name属性进行修改的时候,angular内部的 watch使angularjsngnameangulardigest循环级会执行一次, 他执行的内容是检查我们的 s c o p e 作 用 域 的 内 容 与 上 次 执 行 scope作用域的内容与上次执行 scopedigest循环的时候是否有变化,若有变化就执行$watch()方法绑定的处理函数。 从而 达到了监听作用域属性的效果。

5、$watch(watchExpression, listener, objectEquality);

watchExpression,需要监控的表达式;

listener,处理函数,函数参数如下 function(newValue,oldValue, scope);

objectEquality,是否深度监听,如果设置为true,它告诉Angular检查所监控的对象中每一个属性的变化. 如果你希望监控数组的个别元素或者对象的属性而不是一个普通的值, 那么你应该使用它。

6、 s t a t e — — 在 a j a x 技 术 发 展 普 及 之 后 , 因 为 其 不 会 留 下 历 史 记 录 方 便 浏 览 器 访 问 和 对 于 s e o 不 友 好 的 特 点 , 一 个 新 技 术 应 运 而 生 : 路 由 , state——在ajax技术发展普及之后,因为其不会留下历史记录方便浏览器访问和对于seo不友好的特点,一个新技术应运而生: 路由, stateajax便访seostate就是路由中的一项服务。

7、 s t a t e . g o ( ) — — state.go()—— state.go()state.go()方法可以产生与a链接一样的效果,可以将此方法绑定给一个button按钮, 然后在按钮的点击事件中执行 $state.go(),就可以跳转到相应的页面。

8、 s t a t e p a r a m s — — stateparams—— stateparamsstateParams是一个对象,包含 url 中每个参数的键/值。 s t a t e P a r a m s 可 以 为 控 制 器 或 者 服 务 提 供 u r l 的 各 个 部 分 。 注 意 : stateParams可以为控制器或者服务提供 url 的各个部分。 注意: stateParamsurlstateParams必须与一个控制器相关,并且$stateParams中的“键/值”也必须事先在那个控制器的url属性中有定义。

3.常见问题

watch的深度监听是什么意思呢?有什么优缺点呢?

4.解决方案

w a t c h 在 对 待 原 始 类 型 和 引 用 类 型 会 有 不 同 的 处 理 方 式 , 这 就 要 首 先 说 一 说 watch在对待原始类型和引用类型会有不同的处理方式,这就要首先说一说 watchwatch函数的第三个参数。在前面的例子中, 我们知道, w a t c h 函 数 有 接 收 两 个 参 数 , 第 一 个 参 数 是 需 要 监 视 的 对 象 , 第 二 个 参 数 是 在 监 视 对 象 发 生 变 化 时 需 要 调 用 的 函 数 , 实 际 上 watch函数有接收两个参数,第一个参数是需要监视的对象,第二个参数是在监视对象发生变化时需要调用的函数, 实际上 watchwatch还有第三个参数,它在默认情况下是false。在默认情况下,即不显式指明第三个参数或者将其指明为false时, 我们进行的监视叫做“引用监视”。引用监视的原词的“reference watch”,它的意思是只要监视的对象引用没有发生变化, 就不算它发生了变化。如果我们将 w a t c h 的 第 三 个 变 量 设 置 为 t r u e , 那 么 此 时 我 们 进 行 的 监 视 叫 做 “ 全 等 监 视 ” , 原 词 是 “ e q u a l i t y w a t c h ” 。 此 时 , 只 要 watch的第三个变量设置为true,那么此时我们进行的监视叫做“全等监视”,原词是“equality watch”。 此时,只要 watchtrueequalitywatchwatch的对象有一点风吹草动,它马上就跳出来 既然全等监视这么好,那么我们为什么不直接用全等监视呢?当然,任何事情都有好的坏的两个方面,全等监视固然是好, 但是它在运行时需要先遍历整个监视对象,然后在每次$digest之前使用angular.copy()将整个对象深拷贝一遍然后在运行之后用angular.equal()将前后的对象进行对比。

5.编码实战

6.扩展思考

ui-sref、$state.go 的区别?

(1)ui-sref 一般使用在 …,

例如:message。

(2)$state.go(‘someState’)一般使用在 controller里面,

例如:.controller(‘firstCtrl’, function($scope, $state) {

        $state.go('login');

       });

(3)这两者的本质上是一样的,从ui-sref的源码可以看出ui-sref最后调用的还是$state.go()方法。

7.参考文献

参考一:http://yuankeqiang.lofter.com/post/8de51_1454f93;

参考二:https://blog.youkuaiyun.com/u010451286/article/details/50635839;

参考三:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/;

参考四:https://www.jb51.net/article/80492.html;

参考五:https://www.cnblogs.com/koleyang/p/4576419.html

8.更多讨论

问题一:$stateParams如果没有用url接收会怎样?

答:如果没有在url里面接收,会导致再次提取 s t a t e P a r a m s 中 的 数 据 的 时 候 , 会 找 不 到 该 数 据 ; stateParams中的数据的时候,会找不到该数据; stateParamsstateParams对象中根本就没有该属性。

问题二: $watch脏检测原理是什么?

答:scope通过$watch方法向this.

watchers数组中添加watcher对象(包含watchFn,listenerFn,valueEq,last四个属性)。每当$digest循环被触发时,它会遍历

watchers数组,执行watcher中的watchFn,获取当前scope上某属性的值(一个watcher对应scope上一个被监听属性),然后去同watcher中的last(上一次的值)做比较,若两值不相等,就执行listenerFn。

问题三: r o o t S c o p e 是 否 可 以 替 代 rootScope是否可以替代 rootScopescope?

答:可以,但是不建议这样用,相当于js中的全局变量,定义过多会导致全局污染。

/** * Created by wupeng5 on 2016/5/9. */ var app = angular.module("app",['ui.router','oc.lazyLoad','tableKillModule','selectModule',"select2Module"]).config(function($stateProvider,$controllerProvider,$compileProvider,$filterProvider,$provide,$urlRouterProvider,$httpProvider){ $urlRouterProvider.otherwise('login'); //引入$stateProvider对象,为动态路由做准备 app.register = { stateProvider:$stateProvider } //$locationProvider.html5Mode(true); $httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; $httpProvider.defaults.headers.common["Accept"] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; $httpProvider.defaults.headers.post['X-Requested-With'] = 'XMLHttpRequest'; $httpProvider.defaults.transformRequest = [function(data) { /** * The workhorse; converts an object to x-www-form-urlencoded serialization. * @param {Object} obj * @return {String} */ var param = function(obj) { var query = ''; var name, value, fullSubName, subName, subValue, innerObj, i; for (name in obj) { value = obj[name]; if (value instanceof Array) { for (i = 0; i < value.length; ++i) { subValue = value[i]; fullSubName = name + '[' + i + ']'; innerObj = {}; innerObj[fullSubName] = subValue; query += param(innerObj) + '&'; } } else if (value instanceof Object) { for (subName in value) { subValue = value[subName]; fullSubName = name + '[' + subName + ']'; innerObj = {}; innerObj[fullSubName] = subValue; query += param(innerObj) + '&'; } } else if (value !== undefined && value !== null) { query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&'; } } return query.length ? query.substr(0, query.length - 1) : query; }; return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data; }]; //配合nginx处理本地开发, 服务器调式的问题(接口是跨域的) $httpProvider.interceptors.push(function($q){ return { 'request':function(config){ if(config.url.indexOf('html') == -1){ config.url = "" + config.url; } return config || $q.when(config); } } }); //注销RootScope上面的广播事件 //var deregister = $rootScope.$on("rootEvent", function(event,data) { // //}); // //$scope.$on('$destory', function() { // deregister(); // 退订事件 //}); //注册$onRootScope方法,并在执行完毕后直接销毁, 防止内存溢出 $provide.decorator('$rootScope',['$delegate',function($delegate){ Object.defineProperty($delegate.constructor.prototype,'$onRootScope',{ value:function(name,listener){ var unsubscribe = $delegate.$on(name,listener); this.$on('$destroy',unsubscribe); }, enumerable:false }); return $delegate; }]) //使用,不需要手动去销毁这个事件 //$scope.$onRootScope("key",function(){ // console.log("get key"); //}) //$rootScope.$broadcast("key",{name:1}); }); app.factory('timestampMarker', [function() { return { request: function(config) { config.requestTimestamp = new Date().getTime(); return config; }, response: function(response) { response.config.responseTimestamp = new Date().getTime(); return response; } }; }]); app.config(['$httpProvider', function($httpProvider) { $httpProvider.interceptors.push('timestampMarker'); }]); //$http.get('xxxx').then(function(response) { // var time = response.config.responseTimestamp - response.config.requestTimestamp; // console.log('The request took ' + (time / 1000) + ' seconds.'); //}); //当路由变化的时候触发 app.run(function($rootScope,$templateCache) { $rootScope.isLogin = true; $rootScope.loginStyle = ""; $rootScope.$on('$stateChangeStart', function (event, current, previous) { if(current.name != "login" && $rootScope.currentState != current.name){ // console.log($rootScope.currentState); $rootScope.currentState = current.name; } //清理所有template缓存 if (typeof(current) !== 'undefined'){ $templateCache.removeAll(); } $rootScope.currentName = current.name; if($rootScope.currentName == "login"){ $rootScope.isLogin = false; $rootScope.loginStyle = "login"; $rootScope.col = ""; }else{ $rootScope.isLogin = true; $rootScope.loginStyle = ""; } }); });代码审计一下
最新发布
08-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值