LitElement属性
Posted jixiaohua
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LitElement属性相关的知识,希望对你有一定的参考价值。
1、概述
LitElement管理您声明的属性及其对应的属性。默认情况下,LitElement将:
- 确保在任何声明的属性更改时进行元素更新。
- 捕获已声明属性的实例值。在浏览器注册自定义元素定义之前设置的所有属性值。
- 使用每个属性的小写名称设置观察到的(未引用的)属性。
- 处理声明为字符串,数字,布尔值,数组和对象类型的属性的属性转换。
- 使用直接比较(oldValue !== newValue)测试属性更改。
- 应用超类声明的所有属性选项和访问器。
属性选项
属性声明是具有以下格式的对象:
{ optionName1: optionValue1, optionName2: optionValue2, ... }
也可以是以下选项:
converter
: Convert between properties and attributes.type
: Use LitElement’s default attribute converter.attribute
: Configure observed attributes.reflect
: Configure reflected attributes.noAccessor
: Whether to set up a default property accessor.hasChanged
: Specify what constitutes a property change.
可以在静态属性getter中或使用TypeScript装饰器指定所有属性声明选项。
2、声明属性
通过实现静态 properties
或使用装饰器(注解)来声明元素的属性,就像C的声明与定义分开的写法:
// properties getter static get properties() { return { prop1: { type: String } }; }
// Decorators (requires TypeScript or Babel) export class MyElement extends LitElement { @property( { type : String } ) prop1 = ‘‘;
2.1 在静态properties getter中声明属性
static get properties() { return { prop1: { type: String }, prop2: { type: Number }, prop3: { type: Boolean } }; }
注意:如果实现静态属性获取器,请在元素构造函数中初始化属性值。
constructor() { // Always call super() first super(); this.prop1 = ‘Hello World‘; ... }
请记住,首先要在构造函数中调用super(),否则元素将无法渲染。
示例:使用静态属性获取器声明属性
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties() { return { prop1: { type: String }, prop2: { type: Number }, prop3: { type: Boolean }, prop4: { type: Array }, prop5: { type: Object } };} constructor() { super(); this.prop1 = ‘Hello World‘; this.prop2 = 5; this.prop3 = false; this.prop4 = [1,2,3]; this.prop5 = { subprop1: ‘prop 5 subprop1 value‘ } } render() { return html` <p>prop1: ${this.prop1}</p> <p>prop2: ${this.prop2}</p> <p>prop3: ${this.prop3}</p> <p>prop4[0]: ${this.prop4[0]}</p> <p>prop5.subprop1: ${this.prop5.subprop1}</p> `; } } customElements.define(‘my-element‘, MyElement);
2.2 用注解声明属性
翻译为装饰器,但我更喜欢称之为注解(Java框架),一般用在TS文件中
@property({type : String}) prop1 = ‘Hello World‘;
装饰器是javascript的一项建议功能,因此您需要使用Babel或Translate编译器(例如TypeScript编译器)来使用装饰器。
如果您使用的是Babel,则需要使用@babel/plugin-proposal-decorators插件
如果你使用TypeScript,你需要激活 experimentalDecorators 编译选项(例如:在tsconfig.json配置文件中设置"experimentalDecorators": true),不推荐也不必要激活emitDecoratorMetadata选项
用注解声明属性的例子:
my-element.ts
import { LitElement, html, customElement, property } from ‘lit-element‘; @customElement(‘my-element‘) export class MyElement extends LitElement { @property({type : String}) prop1 = ‘Hello World‘; @property({type : Number}) prop2 = 5; @property({type : Boolean}) prop3 = true; @property({type : Array}) prop4 = [1,2,3]; @property({type : Object}) prop5 = { subprop1: ‘prop 5 subprop1 value‘ }; render() { return html` <p>prop1: ${this.prop1}</p> <p>prop2: ${this.prop2}</p> <p>prop3: ${this.prop3}</p> <p>prop4[0]: ${this.prop4[0]}</p> <p>prop5.subprop1: ${this.prop5.subprop1}</p> `; } }
index.ts
import ‘./my-element.ts‘;
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script> <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script> <title>lit-element code sample</title> </head> <body> <my-element></my-element> </body> </html>
3、初始化属性值
3.1 在构造方法中初始化属性值
static get properties() { return { /* Property declarations */ }; } constructor() { // Always call super() first super(); // Initialize properties this.prop1 = ‘Hello World‘; }
注意:组件实现类的所有构造方法中都必须手动调用父类的构造方法 super()
完整的例子:
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties() { return { prop1: { type: String }, prop2: { type: Number }, prop3: { type: Boolean }, prop4: { type: Array }, prop5: { type: Object } };} constructor() { super(); this.prop1 = ‘Hello World‘; this.prop2 = 5; this.prop3 = true; this.prop4 = [1,2,3]; this.prop5 = { stuff: ‘hi‘, otherStuff: ‘wow‘ }; } render() { return html` <p>prop1: ${this.prop1}</p> <p>prop2: ${this.prop2}</p> <p>prop3: ${this.prop3}</p> <p>prop4: ${this.prop4.map((item, index) => html`<span>[${index}]:${item} </span>`)} </p> <p>prop5: ${Object.keys(this.prop5).map(item => html`<span>${item}: ${this.prop5[item]} </span>`)} </p> `; } } customElements.define(‘my-element‘, MyElement);
3.2 使用TypeScript 注解初始化属性值
在TypeScript中使用@property注解来初始化属性值
@property({ type : String }) prop1 = ‘Hello World‘;
即可以直接在声明的同时初始化,将 static properties getter()方法和 constructor()方法所作的事情合而为一
例子:
import { LitElement, html, customElement, property } from ‘lit-element‘; @customElement(‘my-element‘) export class MyElement extends LitElement { // Declare and initialize properties @property({type : String}) prop1 = ‘Hello World‘; @property({type : Number}) prop2 = 5; @property({type : Boolean}) prop3 = true; @property({type : Array}) prop4 = [1,2,3]; @property({type : Object}) prop5 = { subprop1: ‘hi‘, thing: ‘fasdfsf‘ }; render() { return html` <p>prop1: ${this.prop1}</p> <p>prop2: ${this.prop2}</p> <p>prop3: ${this.prop3}</p> <p>prop4: ${this.prop4.map((item, index) => html`<span>[${index}]:${item} </span>`)} </p> <p>prop5: ${Object.keys(this.prop5).map(item => html`<span>${item}: ${this.prop5[item]} </span>`)} </p> `; } }
3.3 在标签中初始化属性值
ndex.html
<my-element mystring="hello world" mynumber="5" mybool myobj=‘{"stuff":"hi"}‘ myarray=‘[1,2,3,4]‘></my-element>
就像普通的html写法一样
4、配置属性
4.1 properties 和 attributes的转换
虽然元素 properties 可以是任何类型,但是 attributes 始终是字符串。这会影响非字符串属性的 observed attributes 和 reflected attributes:
要 observe 一个属性(set a property from an attribute),属性值必须由string类型转换为匹配的类型
要 reflect 一个属性(set an attribute from a property),属性值必须被转化为string
4.1.1 使用默认转换
在处理 String,Number, Boolean,Array, and Object 属性类型时,LitElement有一个默认的转换规则
要使用默认的转换规则,需要在你的属性声明中指明 type 选项
// Use LitElement‘s default converter prop1: { type: String }, prop2: { type: Number }, prop3: { type: Boolean }, prop4: { type: Array }, prop5: { type: Object }
以下信息显示默认转换器如何处理每种类型的转换。
从 attribute 转换到 property :
- 对于Strings类型, 直接使用,无需转换
- 对于Numbers类型,调用 Number(attributeValue) 构造函数将
- 对于Boolean 类型,非null转化为true,null转化为 false
- 对于Arrays 和 Objects,调用 JSON.parse(attributeValue)
从 property 转换到 attribute :
- 对于Strings,如果 property 是
- null,删除该 attribute
- undefined,不做改变
- 否则直接 attribute 作为 property 值
- 对于Numbers,同上
- 对于 Booleans,property 值
- 为真则创建属性
- 为假则删除属性
- 对于 Objects and Arrays ,
- 如果为 null 或 undefined 则删除属性
- 否则 JSON.stringify(propertyValue) 转换
使用默认转换的例子:
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties() { return { prop1: { type: String, reflect: true }, prop2: { type: Number, reflect: true }, prop3: { type: Boolean, reflect: true }, prop4: { type: Array, reflect: true }, prop5: { type: Object, reflect: true } };} constructor() { super(); this.prop1 = ‘‘; this.prop2 = 0; this.prop3 = false; this.prop4 = []; this.prop5 = { }; } attributeChangedCallback(name, oldVal, newVal) { console.log(‘attribute change: ‘, name, newVal); super.attributeChangedCallback(name, oldVal, newVal); } render() { return html` <p>prop1 ${this.prop1}</p> <p>prop2 ${this.prop2}</p> <p>prop3 ${this.prop3}</p> <p>prop4: ${this.prop4.map((item, index) => html`<span>[${index}]:${item} </span>`)} </p> <p>prop5: ${Object.keys(this.prop5).map(item => html`<span>${item}: ${this.prop5[item]} </span>`)} </p> <button @click="${this.changeProperties}">change properties</button> <button @click="${this.changeAttributes}">change attributes</button> `; } changeAttributes() { let randy = Math.floor(Math.random()*10); let myBool = this.getAttribute(‘prop3‘); this.setAttribute(‘prop1‘, randy.toString()); this.setAttribute(‘prop2‘, randy.toString()); this.setAttribute(‘prop3‘, myBool? ‘‘ : null); this.setAttribute(‘prop4‘, JSON.stringify([...this.prop4, randy])); this.setAttribute(‘prop5‘, JSON.stringify(Object.assign({}, this.prop5, {[randy]: randy}))); this.requestUpdate(); } changeProperties() { let randy = Math.floor(Math.random()*10); let myBool = this.prop3; this.prop1 = randy.toString(); this.prop2 = randy; this.prop3 = !myBool; this.prop4 = [...this.prop4, randy]; this.prop5 = Object.assign({}, this.prop5, {[randy]: randy}); } updated(changedProperties) { changedProperties.forEach((oldValue, propName) => { console.log(`${propName} changed. oldValue: ${oldValue}`); }); } } customElements.define(‘my-element‘, MyElement);
4.1.2 配置自定义转换
您可以使用converter选项在属性声明中指定自定义属性转换器:
myProp: { converter: // Custom property converter }
converter可以是一个对象或函数,如果是一个对象,它有2个关键字 fromAttribute
和 toAttribute
prop1: { converter: { fromAttribute: (value, type) => { // `value` is a string // Convert it to a value of type `type` and return it }, toAttribute: (value, type) => { // `value` is of type `type` // Convert it to a string and return it } } }
如果是一个函数,它被用来替代 fromAttribute:
myProp: { converter: (value, type) => { // `value` is a string // Convert it to a value of type `type` and return it } }
如果没有为反射的属性提供toAttribute函数,则该属性将设置为property 值,而无需进行转换。
在更新期间:
-
If
toAttribute
returnsnull
, the attribute is removed. -
If
toAttribute
returnsundefined
, the attribute is not changed.
自定义转换的例子:
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties() { return { myProp: { reflect: true, converter: { toAttribute(value) { console.log(‘myProp‘s toAttribute.‘); console.log(‘Processing:‘, value, typeof(value)); let retVal = String(value); console.log(‘Returning:‘, retVal, typeof(retVal)); return retVal; }, fromAttribute(value) { console.log(‘myProp‘s fromAttribute.‘); console.log(‘Processing:‘, value, typeof(value)); let retVal = Number(value); console.log(‘Returning:‘, retVal, typeof(retVal)); return retVal; } } }, theProp: { reflect: true, converter(value) { console.log(‘theProp‘s converter.‘); console.log(‘Processing:‘, value, typeof(value)); let retVal = Number(value); console.log(‘Returning:‘, retVal, typeof(retVal)); return retVal; }}, };} constructor() { super(); this.myProp = ‘myProp‘; this.theProp = ‘theProp‘; } attributeChangedCallback(name, oldval, newval) { // console.log(‘attribute change: ‘, name, newval); super.attributeChangedCallback(name, oldval, newval); } render() { return html` <p>myProp ${this.myProp}</p> <p>theProp ${this.theProp}</p> <button @click="${this.changeProperties}">change properties</button> <button @click="${this.changeAttributes}">change attributes</button> `; } changeAttributes() { let randomString = Math.floor(Math.random()*100).toString(); this.setAttribute(‘myprop‘, ‘myprop ‘ + randomString); this.setAttribute(‘theprop‘, ‘theprop ‘ + randomString); this.requestUpdate(); } changeProperties() { let randomString = Math.floor(Math.random()*100).toString(); this.myProp=‘myProp ‘ + randomString; this.theProp=‘theProp ‘ + randomString; } } customElements.define(‘my-element‘, MyElement);
4.1.3 配置观察属性
只要 observed attribute 发生更改,就会触发自定义元素API回调 attributeChangedCallback。默认情况下,每当某个属性触发此回调时,LitElement就会使用属性的 fromAttribute函数从该attribute 设置property值。
默认情况下,LitElement为所有声明的属性创建一个相应的观察属性。被观察属性的名称是属性名称,小写:
// observed attribute name is "myprop" myProp: { type: Number }
要使用其他名称创建观察到的属性,请将attribute设置为字符串:
// Observed attribute will be called my-prop myProp: { attribute: ‘my-prop‘ }
为了防止为从property创建observed attribute,请将attribute设置为false。该property 不会从标记中的attributes 初始化,并且attribute 更改不会对其产生影响。
// No observed attribute for this property myProp: { attribute: false }
配置observed attributes的例子
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties() { return { myProp: { attribute: true }, theProp: { attribute: false }, otherProp: { attribute: ‘other-prop‘ }, };} constructor() { super(); this.myProp = ‘myProp‘; this.theProp = ‘theProp‘; this.otherProp = ‘otherProp‘; } attributeChangedCallback(name, oldval, newval) { console.log(‘attribute change: ‘, name, newval); super.attributeChangedCallback(name, oldval, newval); } render() { return html` <p>myProp ${this.myProp}</p> <p>theProp ${this.theProp}</p> <p>otherProp ${this.otherProp}</p> <button @click="${this.changeAttributes}">change attributes</button> `; } changeAttributes() { let randomString = Math.floor(Math.random()*100).toString(); this.setAttribute(‘myprop‘, ‘myprop ‘ + randomString); this.setAttribute(‘theprop‘, ‘theprop ‘ + randomString); this.setAttribute(‘other-prop‘, ‘other-prop ‘ + randomString); this.requestUpdate(); } updated(changedProperties) { changedProperties.forEach((oldValue, propName) => { console.log(`${propName} changed. oldValue: ${oldValue}`); }); } } customElements.define(‘my-element‘, MyElement);
4.1.4 配置反射属性
您可以配置property ,以便每当property 更改时,其值都会反射到其 observed attribute 中。例如:
// Value of property "myProp" will reflect to attribute "myprop" myProp: { reflect: true }
property更改后,LitElement使用属性转换器中的toAttribute函数从新property值中设置attribute值。
-
If
toAttribute
returnsnull
, the attribute is removed. -
If
toAttribute
returnsundefined
, the attribute is not changed. -
If
toAttribute
itself is undefined, the property value is set to the attribute value without conversion.
LitElement跟踪更新期间的反射状态。LitElement跟踪状态信息,以避免在属性与观察到的反射属性之间创建无限循环的变化。
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties() { return { myProp: { reflect: true } };} constructor() { super(); this.myProp=‘myProp‘; } attributeChangedCallback(name, oldval, newval) { console.log(‘attribute change: ‘, newval); super.attributeChangedCallback(name, oldval, newval); } render() { return html` <p>${this.myProp}</p> <button @click="${this.changeProperty}">change property</button> `; } changeProperty() { let randomString = Math.floor(Math.random()*100).toString(); this.myProp=‘myProp ‘ + randomString; } } customElements.define(‘my-element‘, MyElement);
4.2 配置属性访问器
默认情况下,LitElement为所有声明的属性生成一个属性访问器。每当您设置属性时,都会调用访问器:
// Declare a property static get properties() { return { myProp: { type: String } }; } ... // Later, set the property this.myProp = ‘hi‘; // invokes myProp‘s generated property accessor
生成的访问器会自动调用requestUpdate,如果尚未开始,则启动更新。
4.2.1 创建自己的属性访问器
也就是Java类常用的自定义属性的get set方法
要指定属性的获取和设置的工作方式,可以定义自己的属性访问器。例如:
static get properties() { return { myProp: { type: String } }; } set myProp(value) { const oldValue = this.myProp; // Implement setter logic here... this.requestUpdate(‘myProp‘, oldValue); } get myProp() { ... } ... // Later, set the property this.myProp = ‘hi‘; // Invokes your accessor
如果您的类为属性定义了自己的访问器,则LitElement不会用生成的访问器覆盖它们。如果您的类没有为属性定义访问器,即使超类定义了一个或多个属性,LitElement也会生成它们。
LitElement生成的设置器会自动调用requestUpdate。如果编写自己的设置器,则必须手动调用requestUpdate,并提供属性名称及其旧值。
例子:
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties() { return { prop: { type: Number } }; } set prop(val) { let oldVal = this._prop; this._prop = Math.floor(val); this.requestUpdate(‘prop‘, oldVal); } get prop() { return this._prop; } constructor() { super(); this._prop = 0; } render() { return html` <p>prop: ${this.prop}</p> <button @click="${() => { this.prop = Math.random()*10; }}"> change prop </button> `; } } customElements.define(‘my-element‘, MyElement);
如果要将自己的属性访问器与@property装饰器一起使用,则可以通过将装饰器放在getter上来实现此目的:
_myProp: string = ‘‘; @property({ type: String }) public get myProp(): string { return this._myProp; } public set myProp(value: string) { const oldValue = this.myProp; this._myProp = value; this.requestUpdate(‘myProp‘, oldValue); }
4.2.2 防止LitElement生成属性访问器
在极少数情况下,子类可能需要为其父类上存在的属性更改或添加属性选项
为了防止LitElement生成覆盖父类的已定义访问器的属性访问器(防止覆盖父类属性get set 方法),请在属性声明中将noAccessor设置为true:
static get properties() { return { myProp: { type: Number, noAccessor: true } }; }
定义自己的访问器时,无需设置Accessor。
例如,
父类
import { LitElement, html } from ‘lit-element‘; export class SuperElement extends LitElement { static get properties() { return { prop: { type: Number } }; } set prop(val) { let oldVal = this._prop; this._prop = Math.floor(val); this.requestUpdate(‘prop‘, oldVal); } get prop() { return this._prop; } constructor() { super(); this._prop = 0; } render() { return html` <p>prop: ${this.prop}</p> <button @click="${() => { this.prop = Math.random()*10; }}"> change prop </button> `; } } customElements.define(‘super-element‘, SuperElement);
子类
import { SuperElement } from ‘./super-element.js‘; class SubElement extends SuperElement { static get properties() { return { prop: { reflectToAttribute: true, noAccessor: true } }; } } customElements.define(‘sub-element‘, SubElement);
4.3 配置属性更改
所有声明的属性都有一个函数hasChanged,只要设置了属性,就会调用该函数。
hasChanged比较属性的旧值和新值,并评估属性是否已更改。如果hasChanged返回true,则如果尚未安排元素更新,则LitElement将开始元素更新。有关更新如何工作的更多信息,请参见Element更新生命周期文档。
hasChanged
returnstrue
ifnewVal !== oldVal
.hasChanged
returnsfalse
if both the new and old values areNaN
.
要为属性定制hasChanged,请将其指定为属性选项,也就是自定义属性更改的比较规则,相当于Java重写equals方法
myProp: { hasChanged(newVal, oldVal) { // compare newVal and oldVal // return `true` if an update should proceed }}
例子:
import { LitElement, html } from ‘lit-element‘; class MyElement extends LitElement { static get properties(){ return { myProp: { type: Number, /** * Compare myProp‘s new value with its old value. * * Only consider myProp to have changed if newVal is larger than * oldVal. */ hasChanged(newVal, oldVal) { if (newVal > oldVal) { console.log(`${newVal} > ${oldVal}. hasChanged: true.`); return true; } else { console.log(`${newVal} <= ${oldVal}. hasChanged: false.`); return false; } } }}; } constructor(){ super(); this.myProp = 1; } render(){ return html` <p>${this.myProp}</p> <button @click="${this.getNewVal}">get new value</button> `; } updated(){ console.log(‘updated‘); } getNewVal(){ let newVal = Math.floor(Math.random()*10); this.myProp = newVal; } } customElements.define(‘my-element‘, MyElement);
以上是关于LitElement属性的主要内容,如果未能解决你的问题,请参考以下文章
在Tomcat的安装目录下conf目录下的server.xml文件中增加一个xml代码片段,该代码片段中每个属性的含义与用途