使用Angular.js开发的注意事项


Posted in Javascript onOctober 19, 2016

前言

近期一直在玩Angularjs,不得不说,相对于Knockout,Angularjs这一MVVM框架更强大,也更复杂,各种教程网上到处都是,不过真正用到项目的时候会遇到各种坑。

一、ng-repeat

ng-repeat 用于标识某个 elem 需要重复输出,同时重复输出的内容需为唯一

<div ng-app="app" ng-controller="control">
  <h3 ng-repeat="content in repeatContent">ng-repeat: {{ content }}</h3>
</div>
let app = angular.module("app", []);
app.controller("control", ($scope) => {
  // 输出李滨泓
  $scope.repeatContent = ["李", "滨", "泓"];
  // 下面存在两个“泓”,会报错
  // $scope.repeatContent = ["李", "滨", "泓", "泓"];
})

二、provider, service, factory 之间的关系

factory

factory 很像 service,不同之处在于,service 在 Angular 中是一个单例对象,即当需要使用 service 时,使用 new 关键字来创建一个(也仅此一个)service。而 factory 则是一个普通的函数,当需要用时,他也仅仅是一个普通函数的调用方式,它可以返回各种形式的数据,例如通过返回一个功能函数的集合对象来将供与使用。

定义:

let app = angular.module("app", []);

// 这里可以注入 $http 等 Provider
app.factory("Today", () => {
  let date = new Date();
  return {
    year: date.getFullYear(),
    month: date.getMonth() + 1,
    day: date.getDate()
  };
});

使用注入:

app.controller("control", (Today) => {
  console.log(Today.year);
  console.log(Today.month);
  console.log(Today.day);
});

service

service 在使用时是一个单例对象,同时也是一个 constructor,它的特点让它可以不返回任何东西,因为它使用 new 关键字新建,同时它可以用在 controller 之间的通讯与数据交互,因为 controller 在无用时其作用域链会被销毁(例如使用路由跳转到另一个页面,同时使用了另一个 controller)

定义:

let app = angular.module("app", []);

// 这里可以注入 $http 等 Provider
// 注意这里不可以使用 arrow function
// arrow function 不能作为 constructor
app.service("Today", function() {
  let date = new Date();
  this.year = date.getFullYear();
  this.month = date.getMonth() + 1;
  this.day = date.getDate();
});

使用注入:

app.controller("control", (Today) => {
  console.log(Today.year);
  console.log(Today.month);
  console.log(Today.day);
});

provider

provider 是 service 的底层创建方式,可以理解 provider 是一个可配置版的 service,我们可以在正式注入 provider 前对 provider 进行一些参数的配置。

定义:

let app = angular.module("app", []);

// 这里可以注入 $http 等 Provider
// 注意这里不可以使用 arrow function
// arrow function 不能作为 constructor
app.provider("Today", function() {
  this.date = new Date();
  let self = this;

  this.setDate = (year, month, day) => {
    this.date = new Date(year, month - 1, day);
  }

  this.$get = () => {
    return {
      year: this.date.getFullYear(),
      month: this.date.getMonth() + 1,
      day: this.date.getDate()
    };
  };
});

使用注入:

// 这里重新配置了今天的日期是 2015年2月15日
// 注意这里注入的是 TodayProvider,使用驼峰命名来注入正确的需要配置的 provider
app.config((TodayProvider) => {
  TodayProvider.setDate(2015, 2, 15);
});

app.controller("control", (Today) => {
  console.log(Today.year);
  console.log(Today.month);
  console.log(Today.day);
});

三、handlebars 与 angular 符号解析冲突

场景:

当我使用 node.js 作为服务端,而其中使用了 handlebars 作为模板引擎,当 node.js 对某 URL 进行相应并 render,由于其模板使用 { {} } 作为变量解析符号。同样地,angular 也使用 { {} } 作为变量解析符号,所以当 node.js 进行 render 页面后,如果 { {} } 内的变量不存在,则该个区域会被清空,而我的原意是这个作为 angular 的解析所用,而不是 handlebars 使用,同时我也想继续使用 handlebars,那么此时就需要将 angular 默认的 { {} } 解析符号重新定义。即使用依赖注入 $interpolateProvider 进行定义,如下示例:

app.config($interpolateProvider => {
  $interpolateProvider.startSymbol('{[{');
  $interpolateProvider.endSymbol('}]}');
});

四、ng-annotate-loader

ng-annotate-loader 应用于 webpack + angular 的开发场景,是用于解决 angular 在进行 JS 压缩后导致依赖注入失效并出现错误的解决方法

安装

$ npm install ng-annotate-loader --save-dev

配置

// webpack.config.js
{
  test: /\.js?$/,
  exclude: /(node_modules|bower_components)/,
  loader: 'ng-annotate!babel?presets=es2015'
},

五、双向数据绑定

当我们使用非 Angular 自带的事件时,$scope 里的数据改变并不会引起 $digest 的 dirty-checking 循环,这将导致当 model 改变时,view 不会同步更新,这时我们需要自己主动触发更新

HTML

<div>{{ foo }}</div>
<button id="addBtn">go</button>

JavaScript

app.controller("control", ($scope) => {
  $scope.foo = 0;
  document.getElementById("addBtn").addEventListener("click", () => {
    $scope.foo++;
  }, false);
})

很明显,示例的意图是当点击 button 时,foo 自增长并更新 View,但是实际上,$scope.foo 是改变了,但是 View 并不会刷新,这是因为 foo 并没有一个 $watch 检测变化后 $apply,最终引起 $digest,所以我们需要自己触发 $apply 或者创建一个 $watch 来触发或检测数据变化

JavaScript(使用 $apply)

app.controller("control", ($scope) => {
  $scope.foo = 0;
  document.getElementById("addBtn").addEventListener("click", () => {
    
    $scope.$apply(function() {
      $scope.foo++;
    });

  }, false);
})

JavaScript(使用 $watch & $digest)

app.controller("control", ($scope) => {
  $scope.foo = 0;
  $scope.flag = 0;

  $scope.$watch("flag", (newValue, oldValue) => {

    // 当 $digest 循环检测 flag 时,如果新旧值不一致将调用该函数
    $scope.foo = $scope.flag;
  });

  document.getElementById("addBtn").addEventListener("click", () => {
    
    $scope.flag++;
    // 主动触发 $digest 循环
    $scope.$digest();
  }, false);
})

六、$watch(watchExpression, listener, [objectEquality])

注册一个 listener 回调函数,在每次 watchExpression 的值发生改变时调用

watchExpression 在每次 $digest 执行时被调用,并返回要被检测的值(当多次输入同样的值时,watchExpression 不应该改变其自身的值,否则可能会引起多次的 $digest 循环,watchExpression 应该幂等)

listener 将在当前 watchExpression 返回值和上次的 watchExpression 返回值不一致时被调用(使用 !== 来严格地判断不一致性,而不是使用 == 来判断,不过 objectEquality == true 除外)

objectEquality 为 boolean 值,当为 true 时,将使用 angular.equals 来判断一致性,并使用 angular.copy 来保存此次的 Object 拷贝副本供给下一次的比较,这意味着复杂的对象检测将会有性能和内存上的问题

七、$apply([exp])

$apply 是 $scope 的一个函数,用于触发 $digest 循环

$apply 伪代码

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

使用 $eval(expr) 执行 expr 表达式

如果在执行过程中跑出 exception,那么执行 $exceptionHandler(e)

最后无论结果,都会执行一次 $digest 循环

总结

以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
在IE和VB中支持png图片透明效果的实现方法(vb源码打包)
Apr 01 Javascript
jQuery获取样式中的背景颜色属性值/颜色值
Dec 17 Javascript
jquery $(&quot;#variable&quot;) 循环改变variable的值示例
Feb 23 Javascript
js不能获取隐藏的div的宽度只能先显示后获取
Sep 04 Javascript
js设置cookie过期当前时间减去一秒相当于立即过期
Sep 04 Javascript
JavaScript实现非常简单实用的下拉菜单效果
Aug 27 Javascript
jQuery实现页面顶部显示的进度条效果完整实例
Dec 09 Javascript
Javascript中构造函数要注意的一些坑
Jan 23 Javascript
vue项目中api接口管理总结
Apr 20 Javascript
vue axios封装及API统一管理的方法
Apr 18 Javascript
vue+webpack 更换主题N种方案优劣分析
Oct 28 Javascript
Vue实现菜单切换功能
Nov 08 Javascript
js表单登陆验证示例
Oct 19 #Javascript
jQuery插件ajaxFileUpload异步上传文件
Oct 19 #Javascript
Angular和百度地图的结合实例代码
Oct 19 #Javascript
Bootstrap Table使用方法解析
Oct 19 #Javascript
JavaScript 中 avalon绑定属性总结
Oct 19 #Javascript
Ubuntu 16.04 64位中搭建Node.js开发环境教程
Oct 19 #Javascript
jQuery实现点击任意位置弹出层外关闭弹出层效果
Oct 19 #Javascript
You might like
解析smarty 截取字符串函数 truncate的用法介绍
2013/06/20 PHP
PDO防注入原理分析以及使用PDO的注意事项总结
2014/10/23 PHP
php字符串过滤与替换小结
2015/01/26 PHP
Discuz不使用插件实现简单的打赏功能
2019/03/21 PHP
Mootools 1.2教程 滚动条(Slider)
2009/09/15 Javascript
改变javascript函数内部this指针指向的三种方法
2010/04/23 Javascript
十个迅速提升JQuery性能让你的JQuery跑得更快
2012/12/10 Javascript
得到jQuery detach()后节点中的某个值实现代码
2013/02/05 Javascript
JS 实现点击a标签的时候让其背景更换
2013/10/15 Javascript
禁止页面刷新让F5快捷键及右键都无效
2014/01/22 Javascript
使用JSON.parse将json字符串转换成json对象的时候会出错
2014/09/04 Javascript
jQuery模拟新浪微博首页滚动效果的方法
2015/03/11 Javascript
JS+CSS实现仿雅虎另类滑动门切换效果
2015/10/13 Javascript
深入浅析AngularJS和DataModel
2016/02/16 Javascript
javascript特殊日历控件分享
2016/03/07 Javascript
jQuery EasyUI中的日期控件DateBox修改方法
2016/11/09 Javascript
vue-prop父组件向子组件进行传值的方法
2018/03/01 Javascript
vue-router判断页面未登录自动跳转到登录页的方法示例
2018/11/04 Javascript
vue-cli中vue本地实现跨域调试接口
2019/01/16 Javascript
Python实现检测文件MD5值的方法示例
2018/04/11 Python
Python统计python文件中代码,注释及空白对应的行数示例【测试可用】
2018/07/25 Python
Django框架视图介绍与使用详解
2019/07/18 Python
HTML5+CSS3实例 :canvas 模拟实现电子彩票刮刮乐代码
2016/12/30 HTML / CSS
德国孕妇装和婴童服装网上商店:bellybutton
2018/04/12 全球购物
采购文员岗位职责
2013/11/20 职场文书
高三历史教学反思
2014/01/09 职场文书
中医临床专业自我鉴定范文
2014/01/15 职场文书
工程招投标邀请书
2014/01/26 职场文书
单身联谊活动方案
2014/01/29 职场文书
教师简历自我评价
2014/02/03 职场文书
《傅雷家书》教学反思
2014/04/20 职场文书
高中生逃课检讨书
2014/10/10 职场文书
升学宴祝酒词
2015/08/11 职场文书
年中了,该如何写好个人述职报告?
2019/07/02 职场文书
使用css样式设计一个简单的html登陆界面的实现
2021/03/30 HTML / CSS
golang switch语句的灵活写法介绍
2021/05/06 Golang