当我们在 Angular 中给属性绑定一个变量的时候,主要有下面三种绑定方式
[property] = "variable"property = ""[attr.property] = "xxx"
下面我们就看看它们之间的区别
[[]] 和 {{}}
这两种方式也是平常使用最多的,我们下面通过一个示例看看它们之间的区别
1 | @Component({ |
然后我们来使用它
1 | <my-test [test]="str" [testTwo]="'[]'"></my-test> |
str 是一个字符串,并且我们通过 input 或者 button 来改变 str 的值的时候,两者在页面上的显示和控制台的打印都是正常的,看起来并没有什么差异,但是在 str 还没有赋值之前,前者打印的是 undefined,而后者打印的是空字符串,也许这样并不能看出什么,所以我们试着传递一个对象过去,这下便可以看出区别了,前者可以正常的显式我们传递的对象,比如 {a: 1, b: 2},但是后者显式的却是 '[Object Object]'
HTML attribute 和 DOM property
在 Angular 中还有一种 attribute 绑定,写法为 [attr.Attribute]="variable",那么它和 [property]="variable" 的区别又在哪里呢?
在看它们两者的区别之前我们先来了解一下 HTML attribute 和 DOM property 两者的区别,这里虽然涉及到一些 HTML 当中的知识,但是仍然适用于 Angular 当中遇到的情况,在本章节当中我们约定,使用『特性』来代指 attribute ,使用『属性』来代指 property,当我们在书写 HTML 代码的时候,我们为 HTML 元素设置特性,例如
1 | <input id="name" value="zhangsan" /> |
我们写了一个 input 标签,并给他定义了两个特性(id 和 value),当浏览器解析这段代码的时候,会把 HTML 源码解析为 DOM 对象,确切的说是解析为 HTMLInputElement 对象,HTMLInputElement 的继承关系是
1 | HTMLInputElement |
通过查看文档会发现,HTMLInputElement 的原型上定义了很多属性和方法,例如 form,name,type,alt,checked,src,value 等等,还有从 HTMLElement 继承来的 id,title,clientTop 等等,如果仔细寻找,就不难发现其中就有我们为 input 标签定义的特性 id 和 value,这是因为当浏览器解析网页时,将 HTML 特性映射为了 DOM 的『属性』
而 Element 类还有一个 Element.attributes 属性,里面包含了所有的特性,但是 HTML attribute 和 DOM property 并不总是一对一的关系,在下面我们会看到
DOM 属性
当浏览器解析完 HTML 后,生成的 DOM 是一个继承自 Object 的常规 JavaScript 对象,因此我们可以像操作任何 JavaScript 对象那样来操作 DOM 对象
1 | Element.foo = 'bar' |
也可以为其添加方法,如果你想给每个 HTML 元素都添加属性或方法,甚至可以直接修改 Element.prototype,不过不建议这么操作
HTML 特性
和 DOM 属性类似,除了那些规范里定义的标准特性外,HTML 也可以添加非标准的属性,例如
1 | <input id="name" value="zhangsan" foo="bar" /> |
当 HTML 特性映射为 DOM 属性时,『只映射标准属性』,访问非标准属性将得到 undefined
1 | const el = document.getElementById('name') |
好在 DOM 对象也提供了操作特性的 API
1 | Element.hasAttribute(name) // 判断某个特性是否存在 |
以上 API 定义在 Element 上,根据 HTML 规范,标签以及特性名是不区分大小写的,因此以下代码是一样的
1 | Element.getAttribute('id') |
并且特性永远都是字符串或 null,如果我们为特性设置非字符串的值,则引擎会将此值转换为字符串,属性是具有类型的
1 | Element.getAttribute('checked') === '' // 特性是字符串 |
即使都是字符串,属性和特性也可能不同,有一个例外就是 href
HTML attribute,对于href返回HTML设置的值DOM property,对于href返回解析后的完整url
特性和属性的同步
当标准的特性更新时,对应的属性也会更新,反之亦然,但是 input.value 的同步是单向的,只是 attribute ==> property,当修改特性时,属性也会更新,但是修改属性后,特性却还是原值
1 | Element.setAttribute('value', 'zhangsan') // 修改特性 |
非标准特性
非标准 HTML 特性并不会自动映射为 DOM 属性,当我们使用 data- 开头的特性时,会映射到 DOM 的 dataset 属性,中划线格式会变成驼峰格式
1 | Element.setAttribute('data-name', 'zhangsan') |
自定义特性 VS 非规范特性
HTML 允许我们自定义标签,也可以扩展标签的特性,但是我们推荐使用已经进入 HTML5 规范的自定义特性 data-*,比如我们想为div 标签增加一个 age 特性,我们可以有两种选择
1 | <div age="18">zhangsan</div> |
虽然第一种代码更短,但是却有一个潜在的风险,因为 HTML 规范是一直发展变化的,也许在未来的某个版本中,age 被添加进了标准特性里面,这将会引起潜在的 bug
总结
property指的是DOM中的属性,是JavaScript里的对象attribute指的是HTML标签上的特性,它的值只能够是字符串DOM中的有一个attribute的属性,其中就是HTML标签上的特性列表- 两者之间的数据绑定是单向的,更改
attribute会同步到property,但反之则不然 - 更改
property和attribute的值,都会将更新反映到HTML页面中
在 Angular 当中,由于 element 没有这些属性,中括号 [] 的属性绑定语法自然不行,而且模版编译的时候会报错,所以对于非基本属性,Angular 提供了这种 [attr.Attribute]="variable" 的特性绑定语法
当然,在 property 是基本属性时,使用 [property]="variable" 绑定属性,然后改变 variable 的值,对应的 attribute 也会跟着属性同步,Angular 应该是做了属性和特性的数据双向绑定的工作,所以只有 property 是基本属性,两者一样,例如 [id] 和 [attr.id] 的作用并无区别
两个需要注意的地方
一个就是常见的传值的差异
1 | [property]="false" |
特别需要注意,因为后者其实绑定的是 'false'(字符串),所以一旦用在了 if 语句当中就可能出现问题
另外一个就是 Input 标签的 maxlength 特性对应的是 maxLength 属性(注意 L 是大写),虽然是大写,但是在浏览器当中解析出来的依然是 <input maxlength="10" />(小写),所以写成 [attr.maxlength]、[attr.maxLength]、[maxLength] 都是可以,但是 [maxlength] 就不行,同理 minlength/minLength 是一样的
小结
综上所诉,属性绑定最好还是用中括号,双大括号用于展示,如
1 | <div>{{obj | json}}</div> |
实际上在渲染视图之前,Angular 把这些插值表达式翻译成相应的属性绑定,还有就是,它依然可以用于执行 Angular 的模板语法,例如
1 | property="{{fun()}}" |
只是它会做多一步,把 return 的值转换成 string