先记住这个答案
属性绑定是 [target]="expression",Angular 在变更检测时从组件类读取表达式值,写入目标属性或指令输入;事件绑定是 (event)="handler()",当原生事件或 @Output() 触发时,Angular 调用类中的处理方法。两者都遵循单向数据流:属性绑定自上而下,事件绑定自下而上,没有隐式的反向自动更新。
- 属性绑定在变更检测阶段单向从上往下写入
- 事件绑定在事件触发时从下往上调用类方法
- 两者分向,不能隐式自动反向修改类属性
绑定机制与各自的单向管道
属性绑定 [value]="user.name" 在每次组件变更检测时,Angular 先对表达式求值,若值与上次不同则更新 DOM 属性或组件 @Input()。它只传递组件类到模板的单向数据,模板内不能直接改类属性,除非通过事件。计算时机位于每次变更检测周期中:对首次创建的组件,绑定求值与写入发生在构造函数之后、生命周期 ngOnChanges 之前;后续更新发生在其 ngOnChanges(若被调用)之前。
事件绑定 (click)="save()" 在浏览器触发事件时,Angular 在事件调度阶段调用表达式,通常调用类方法。这个调用可修改类属性,然后触发新一轮变更检测,重新执行属性绑定。因此事件绑定可以间接影响属性绑定的值,但属性绑定本身绝不会调用类方法。两个绑定方向不同,生命周期上事件先于随后的变更检测。
@Component({
selector: 'app-counter',
template: `<button (click)="increment()">{{count}}</button>`
})
export class Counter {
count = 0;
increment() { this.count++; }
}点击事件调用 increment 修改 count,之后变更检测重新渲染 {{count}},事件绑定从模板到类,属性插值从类到模板。
输入框值实时同步的场景
输入:文本框需要随组件状态更新,同时用户在输入时反向修改状态。若只写 [value]="name",当用户输入时 name 不更新;若只写 (input)="name=$event.target.value",当 name 被外部修改时,输入框不刷新。约束是必须保持单一数据源。
决策:使用事件绑定更新类属性,并在通过属性绑定将类属性重新给 value。结果:外部更新 name 时输入框反映新值,用户输入时类属性同步。注意输入事件的 $event.target 需要类型断言,且不要在事件处理中读取同一属性后写回,避免冗余。处理后,属性绑定每次变更检测都执行,但不一定每次都更新 DOM,因为值未变。
失效边界与判断方法
使用 [value] 绑定输入框时,Angular 只在变更检测更新,如果组件使用 ChangeDetectionStrategy.OnPush 且没有触发标记,来自外部且不可变对象的更新可能不刷新。此时事件绑定引发的对象修改若不替换引用,也可能导致界面不更新。
处理:在事件处理器中替换对象引用或调用 ChangeDetectorRef.markForCheck()。但过度依赖 markForCheck 增加维护成本。更稳妥是使用 [(ngModel)] 或 FormControl 结合响应式表单,它们封装了更新逻辑。判断标准:检查值是否为新引用,若否且 OnPush 则需显式标记。
容易答错的地方
- 认为事件绑定可以自动更新类属性
- 事件绑定的表达式只是函数调用,除非你显式赋值,否则不会修改组件状态。正确理解:事件给类一个响应机会,数据流方向由你写的代码决定。
- 混淆 property 与 attribute 绑定
[attr.aria-label]设置 HTML 属性,[aria-label]不工作。property 绑定操作 DOM property,attribute 绑定操作 HTML attribute。对自定义组件,@Input()是 property 绑定。不能混用。
面试官还会怎么问?
属性绑定的表达式什么时候求值?
在每次变更检测周期中,Angular 从组件树根到叶求值所有绑定表达式。若使用默认策略,每次异步事件后都会触发;OnPush 则仅当输入引用变化或自身事件等。
如何阻止事件向上冒泡?
在模板事件绑定中调用 $event.stopPropagation(),例如 (click)="handle($event); $event.stopPropagation()"。注意 Angular 事件绑定直接绑定到模板元素,原生 DOM 冒泡需要自己阻止。
属性绑定与插值 `{{}}` 有何区别?
插值实际上编译为属性绑定,但主要用于文本节点,属性绑定用于元素属性或组件输入。插值总是将表达式转为字符串,属性绑定可传递对象、布尔等非字符串值。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。