news 2026/4/22 17:23:27

AngularJS 事件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AngularJS 事件

AngularJS 事件处理学习笔记(详细版 v1.x)

📌核心区分:AngularJS 中有两类完全不同的“事件”

  1. DOM 事件:用户交互触发(点击、输入、键盘等),通过ng-*指令绑定。
  2. 作用域事件(Scope Events):组件/控制器间通信机制,通过$emit/$broadcast/$on实现。

⚠️现代 AngularJS (1.5+) 建议:优先使用组件绑定&回调 /<单向数据)替代作用域事件。作用域事件易导致“事件面条代码”,仅建议在遗留系统或特殊跨级通信中使用。


🖱️ 一、DOM 事件绑定(内置指令)

1. 常用事件指令

指令触发时机备注
ng-click/ng-dblclick鼠标单击/双击最常用
ng-mousedown/ng-mouseup/ng-mouseenter/ng-mouseleave鼠标按下/抬起/进入/离开替代 jQueryhover
ng-keydown/ng-keyup/ng-keypress键盘按键推荐ng-keyup处理输入逻辑
ng-change模型值改变必须配合ng-model,不监听原生change事件
ng-blur/ng-focus失去/获得焦点表单验证常用
ng-submit表单提交绑定在<form>上,自动阻止默认提交
ng-copy/ng-cut/ng-paste剪贴板操作需配合$event获取数据

2. 语法与传参

<!-- 基础绑定 --><buttonng-click="vm.save()">保存</button><!-- 传递参数 + 事件对象 --><buttonng-click="vm.delete(item.id, $event)">删除</button><!-- 结合表达式(不推荐复杂逻辑) --><inputng-keyup="vm.searchText && vm.doSearch()">
vm.delete=function(id,$event){$event.preventDefault();// 阻止默认行为$event.stopPropagation();// 阻止冒泡// 业务逻辑...};

3.ng-change与防抖优化

ng-change默认在每次输入时触发,频繁触发会导致性能问题。

<!-- 推荐:使用 ng-model-options 防抖 --><inputng-model="vm.keyword"ng-change="vm.search()"ng-model-options="{ debounce: 300, updateOn: 'default blur' }">

debounce: 300:停止输入 300ms 后才触发ng-change
updateOn: 'blur':仅在失焦时更新模型(适合表单验证)


📡 二、作用域事件通信($emit/$broadcast/$on

1. 传播方向对比

方法传播方向适用场景性能影响
$scope.$emit('name', data)子 → 父 → $rootScope(向上冒泡)子组件通知父组件低(仅遍历祖先)
$scope.$broadcast('name', data)父 → 子 → 所有后代(向下广播)父组件通知多个子组件高(遍历整棵子树)
$rootScope.$broadcast()全局广播跨模块通信(不推荐)极高(遍历所有 scope)

2. 监听事件:$on

// 监听事件varderegister=$scope.$on('user:updated',function(event,data){console.log('收到数据:',data);console.log('事件名:',event.name);console.log('触发源 scope:',event.targetScope);console.log('当前 scope:',event.currentScope);// 控制传播event.stopPropagation();// 仅对 $emit 有效event.preventDefault();// 标记 event.defaultPrevented = true});// ⚠️ 重要:手动注销(尤其绑定在 $rootScope 时)$scope.$on('$destroy',deregister);

3. DOM$eventvs Scope$event

属性/方法DOM 事件 (ng-click)作用域事件 ($on)
来源浏览器原生事件(jqLite/jQuery 包装)Angular 内部构造的事件对象
event.target触发事件的 DOM 节点❌ 不存在
event.preventDefault()阻止浏览器默认行为仅设置defaultPrevented = true
event.stopPropagation()阻止 DOM 冒泡阻止 Scope 事件继续传播
传参方式ng-click="fn($event)"$emit('name', arg1, arg2)$on('name', (e, arg1, arg2)=>{})

⚡ 三、与原生/第三方事件的集成

Angular 只能自动检测自身上下文内的变化。外部事件需手动触发$digest

1. 安全同步方案

// ❌ 危险:可能报 $digest already in progresselement.addEventListener('click',function(){$scope.$apply(()=>vm.count++);});// ✅ 推荐 1:$applyAsync(合并到下一次 digest,防报错)element.addEventListener('click',function(){$scope.$applyAsync(()=>vm.count++);});// ✅ 推荐 2:$timeout(自动 $apply,异步安全)$timeout(()=>vm.count++);// ✅ 推荐 3:$evalAsync(在当前 digest 周期末尾执行)$scope.$evalAsync(()=>vm.count++);

2. 第三方库集成模板(如 ECharts、Swiper)

link:function(scope,element){varchart=echarts.init(element[0]);chart.on('click',function(params){// 第三方回调不在 Angular 上下文scope.$applyAsync(function(){scope.vm.selectedData=params.data;});});// 销毁时清理scope.$on('$destroy',function(){chart.dispose();});}

🛑 四、常见坑与避坑指南

现象原因解决方案
$digest already in progress在 Angular 上下文中重复调用$apply()改用$applyAsync()/$timeout()/$evalAsync()
$rootScope.$on导致内存泄漏$rootScope永不销毁,监听器不会自动清理必须保存返回值并在$destroy时调用注销函数
ng-change不触发未绑定ng-model,或值未真正改变检查ng-model;对象引用未变时不会触发
$broadcast页面卡顿广播遍历整个作用域树,Watcher 过多改用 Service 共享状态 / 组件&回调 / RxJS Subject
事件绑定后重复触发未解绑 DOM 事件或$on,指令重复编译scope.$on('$destroy', () => element.off())+ 注销$on
$event.stopPropagation()无效$broadcast中使用(仅对$emit有效)广播无法中途停止,需改用条件判断或重构通信方式

📊 五、性能优化与最佳实践

✅ 推荐做法

  1. 优先使用组件绑定替代作用域事件

    // 父组件<child-component on-save="vm.handleSave(data)"></child-component>// 子组件定义bindings:{onSave:'&'},controller:function(){this.submit=function(){this.onSave({data:this.formData});// 传递命名参数};}
  2. 全局通信使用 Service + 发布订阅

    app.factory('EventBus',function($rootScope){varbus={};bus.on=function(event,fn){return$rootScope.$on(event,fn);// 返回注销函数};bus.emit=function(event,data){$rootScope.$emit(event,data);// 用 $emit 替代 $broadcast 提升性能};returnbus;});
  3. 高频事件防抖/节流

    • 输入搜索:ng-model-options="{ debounce: 300 }"
    • 滚动/resize:使用lodash.throttle+$applyAsync
  4. 严格清理事件监听

    varunbindDom=element.on('scroll',handler);varunbindScope=$scope.$on('custom:event',handler);$scope.$on('$destroy',function(){unbindDom();unbindScope();});

🚫 反模式(Avoid)

  • ❌ 在 Controller 中直接document.addEventListener
  • ❌ 滥用$rootScope.$broadcast做全局状态管理
  • ❌ 在ng-repeat内部绑定大量独立事件(使用事件委托)
  • ❌ 用$watch替代事件通信(性能更差)

📦 六、完整示例:安全的事件通信模式

// parent.component.jsangular.module('app').component('parentPanel',{template:`<h3>父组件</h3> <p>收到消息: {{ $ctrl.message }}</p> <child-item on-notify="$ctrl.handleNotify(msg)"></child-item>`,controller:function(){varvm=this;vm.message='无';vm.handleNotify=function(msg){vm.message=msg;};}});// child.component.jsangular.module('app').component('childItem',{template:`<button ng-click="$ctrl.send()">通知父级</button>`,bindings:{onNotify:'&'},controller:function(){varvm=this;vm.send=function(){// 使用 & 绑定回调,替代 $emitvm.onNotify({msg:'来自子组件: '+Date.now()});};}});

💡 此模式完全避免$scope事件,符合 AngularJS 1.5+ 组件化规范,易于测试与维护。


🔄 七、向 Angular (2+) 迁移提示

AngularJS (1.x)Angular (2+)
ng-click="fn()"(click)="fn()"
$scope.$emit / $broadcast@Output() event = new EventEmitter()+ RxJSSubject
$on('$destroy')ngOnDestroy()生命周期钩子
$applyAsync()变更检测自动处理(Zone.js)
ng-model-options debounceRxJS debounceTime()+ 响应式表单

📚 延伸学习

  • 📘 官方文档:ngClick | [r o o t S c o p e . S c o p e ] ( h t t p s : / / d o c s . a n g u l a r j s . o r g / a p i / n g / t y p e / rootScope.Scope](https://docs.angularjs.org/api/ng/type/rootScope.Scope](https://docs.angularjs.org/api/ng/type/rootScope.Scope) | ngModelOptions
  • 🔍 调试工具:ChromeAngularJS Batarang→ 查看 Scope 树与事件传播路径
  • 📖 推荐阅读:《AngularJS 权威教程》第 8 章(事件与作用域通信)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 17:21:24

CRM高可用架构设计与实践指南

永不掉线的CRM架构设计原则高可用性是CRM系统架构设计的核心目标&#xff0c;需遵循多节点部署、无状态服务、自动化故障转移等原则。异地多活架构确保单机房故障不影响业务连续性&#xff0c;数据分片与副本机制避免单点数据丢失。微服务化与容器编排技术采用Spring Cloud或Du…

作者头像 李华
网站建设 2026/4/22 17:18:38

消息防撤回技术解密:如何让撤回的消息无处可藏?

消息防撤回技术解密&#xff1a;如何让撤回的消息无处可藏&#xff1f; 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁&#xff08;我已经看到了&#xff0c;撤回也没用了&#xff09; 项目地址: https://gitco…

作者头像 李华
网站建设 2026/4/22 17:17:35

从Keil迁移到STM32CubeIDE:一个嵌入式工程师的真实体验与避坑指南

从Keil迁移到STM32CubeIDE&#xff1a;一个嵌入式工程师的真实体验与避坑指南 当我在2022年第一次被迫从Keil转向STM32CubeIDE时&#xff0c;内心是抗拒的。毕竟Keil的界面已经刻进了肌肉记忆&#xff0c;而CubeIDE这个基于Eclipse的"新玩意"看起来笨重又陌生。但两年…

作者头像 李华
网站建设 2026/4/22 17:17:33

Belle II实验DNN触发系统设计与FPGA实现

1. 项目背景与挑战Belle II实验是当前高能物理领域最重要的前沿实验之一&#xff0c;位于日本筑波市的高能加速器研究机构(KEK)。作为B工厂实验的升级版&#xff0c;它使用SuperKEKB对撞机将7 GeV电子与4 GeV正电子对撞&#xff0c;目标累积50 ab⁻的积分亮度&#xff0c;峰值亮…

作者头像 李华
网站建设 2026/4/22 17:13:54

地下水数值模拟软件实战选型指南:从GMS、FEFLOW到MODFLOW家族

1. 地下水数值模拟软件概览 第一次接触地下水数值模拟时&#xff0c;我被各种软件缩写搞得晕头转向。GMS、FEFLOW、MODFLOW...它们看起来功能相似&#xff0c;但实际应用中各有侧重。经过多个项目的实战验证&#xff0c;我发现选对软件能让工作效率提升数倍。 地下水数值模拟软…

作者头像 李华