1、概述

基于LitElement的组件通过响应观察到的属性更改而异步更新。

属性更改是分批进行的,如果在请求更新后,更新开始之前,发生更多属性更改,则所有更改都将捕获在同一次更新中。

在较高级别上,更新生命周期为:

  1. 一个属性被设置
  2. 检查是否需要更新。如果需要更新,发出请求。
  3. 执行更新:
    • 通过properties 和 attributes.
    • 渲染元素
  4. resolve一个promise,表示更新已完成

1.1 LitElement和浏览器事件循环

浏览器通过处理事件循环中的任务队列来执行JavaScript代码。在事件循环的每次迭代中,浏览器都从队列中提取任务并将其运行到完成。

任务完成后,在从队列中执行下一个任务之前,浏览器会分配时间来从其他来源(包括DOM更新,用户交互和微任务队列)执行工作。

默认情况下,LitElement更新是异步请求的,并作为微任务排队。这意味着上面的步骤3(执行更新)在事件循环的下一次迭代结束时执行。

您可以更改此行为,以便第3步在执行更新之前等待Promise。有关更多信息,请参见 performUpdate。

有关浏览器事件循环的更详细说明,请参阅Jake Archibald的文章

1.2 生命周期回调

LitElement还从Web组件标准继承默认的生命周期回调

  • connectedCallback: 当组件被添加到document 的 DOM中时调用
  • disconnectedCallback: 当组件被从document 的 DOM中删除时调用
  • adoptedCallback: 当组件被移动到一个新的document中时调用
  • attributeChangedCallback: 当组件属性更改时调用

请注意,adoptedCallback 不会被 polyfilled

所有的生命周期回调中都需要调用其父类的回调,例如

connectedCallback() {
super.connectedCallback() console.log('connected')
}

1.3 Promises和异步函数

LitElement使用Promise对象计划和响应元素更新。

使用async和await使得使用Promises变得容易。例如,您可以等待updateComplete承诺:

// `async` makes the function return a Promise & lets you use `await`
async myFunc(data) {
// Set a property, triggering an update
this.myProp = data; // Wait for the updateComplete promise to resolve
await this.updateComplete;
// ...do stuff...
return 'done';
}

由于异步函数返回Promise,因此您也可以等待它们:

let result = await myFunc('stuff');
// `result` is resolved! You can do something with it

2、函数和属性

按照调用顺序,更新生命周期中的方法和属性为:

  1. someProperty.hasChanged 调用属性更改
  2. requestUpdate 请求更新
  3. performUpdate 执行更新
  4. shouldUpdate 判断应该更新
  5. update 更新
  6. render 渲染
  7. firstUpdate 首次更新
  8. update 更新
  9. updateComplete 完成更新

2.1 someProperty.hasChanged

所有的声明属性都有一个hasChanged函数,只要属性被设置,它就会被调用,然后hasChanged函数返回true,才会执行更新,具体操作见前一节自定义属性更改部分

2.2 requestUpdate

//手动调用更新
this.requestUpdate(); // 从一个自定义属性的setter函数中调用
this.requestUpdate(propertyName, oldValue);
Params

propertyName

oldValue

要更新的属性名

旧的属性值

Return Promise

返回updateComplete Promise,该Promise在更新完成时解决。

此方法内部的属性更改不会触发元素更新。

Update? No

此方法内部的属性更改不会触发元素更新。

如果hasChanged返回true,则将触发requestUpdate,更新将继续进行。

要手动启动元素更新,请不带任何参数调用requestUpdate。

要实现支持属性选项的自定义属性设置器,请将属性名称及其旧的的值作为参数传递。

手动属性更新的例子:

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
constructor() {
super(); // Request an update in response to an event
this.addEventListener('load-complete', async (e) => {
console.log(e.detail.message);
console.log(await this.requestUpdate());
});
}
render() {
return html`
<button @click="${this.fire}">Fire a "load-complete" event</button>
`;
}
fire() {
let newMessage = new CustomEvent('load-complete', {
detail: { message: 'hello. a load-complete happened.' }
});
this.dispatchEvent(newMessage);
}
}
customElements.define('my-element', MyElement);

在构造函数中添加自定义事件load-complete的监听器,并在点击事件中手动触发该自定义事件,调用requestUpdate函数,返回值为true

在自定义属性的setter方法中调用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);

在点击事件中给this.prop属性赋值,由于重写了prop属性的setter函数,所以会执行它,然后在setter函数中点给属性赋新的值,然后调用requestUpdate()函数,传入属性名prop和旧的值,完成属性更新

2.3 performUpdate

/**
* 重写以覆盖默认行为
*/
performUpdate() { ... }
Returns   void or Promise 执行更新
Update? No 此方法内部的属性更改不会触发元素更新。

默认情况下,performUpdate在执行下一次浏览器事件循环之前被调用,要调用performUpdate,请将它实现为一个异步任务,并在调用super.performUpdate()之前等待一些状态

例如:

async performUpdate() {
await new Promise((resolve) => requestAnimationFrame(() => resolve()));
super.performUpdate();
}
import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() { return { prop1: { type: Number } }; } constructor() {
super();
this.prop1 = 0;
} render() {
return html`
<p>prop1: ${this.prop1}</p>
<button @click="${() => this.prop1=this.change()}">Change prop1</button>
`;
} async performUpdate() {
console.log('Requesting animation frame...');
await new Promise((resolve) => requestAnimationFrame(() => resolve()));
console.log('Got animation frame. Performing update');
super.performUpdate();
} change() {
return Math.floor(Math.random()*10);
}
}
customElements.define('my-element', MyElement);

2.4 shouldUpdate

/**
* 重写以覆盖默认行为
*/
shouldUpdate(changedProperties) { ... }
Params changedProperties map的键是已更改属性的名称;值是对应的先前值。
Returns Boolean 如果为true,则继续更新。默认返回值为true。
Updates? Yes 此方法内部的属性更改将触发元素更新。

控制是否应继续进行更新。实现shouldUpdate以指定哪些属性更改应引起更新。默认情况下,此方法始终返回true。

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
prop1: { type: Number },
prop2: { type: Number }
};
}
constructor() {
super();
this.prop1 = 0;
this.prop2 = 0;
} render() {
return html`
<p>prop1: ${this.prop1}</p>
<p>prop2: ${this.prop2}</p>
<button @click="${() => this.prop1=this.change()}">Change prop1</button>
<button @click="${() => this.prop2=this.change()}">Change prop2</button>
`;
} /**
* Only update element if prop1 changed.
*/
shouldUpdate(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
return changedProperties.has('prop1');
} change() {
return Math.floor(Math.random()*10);
}
}
customElements.define('my-element', MyElement);

由于在shouldUpdate方法中总是判断键是否为prop1,所以prop2的属性更改总是在最后一步被终止

2.5 update

Params changedProperties map的键是已更改属性的名称;值是对应的先前值。
Updates? No 此方法内部的属性更改不会触发元素更新。

将property值反射为attributes属性,并通过lit-html调用render来渲染DOM。在此提供参考。您无需覆盖或调用此方法。

2.6 render

/**
* 重写render以覆盖默认行为
*/
render() { ... }
Returns TemplateResult 必须返回lit-html TemplateResult
Updates? No 此方法内部的属性更改不会触发元素更新。

使用lit-html渲染元素模板。您必须为扩展LitElement基类的任何组件实现render函数

2.7 firstUpdated

/**
* 重写
*/
firstUpdated(changedProperties) { ... }
Params changedProperties map的键是已更改属性的名称;值是相应的先前值。
Updates? Yes 此方法内部的属性更改将触发元素更新。

在元素的DOM第一次更新之后,即在调用更新之前立即调用

创建元素模板后,请重写firstUpdated以执行一次性工作。

Example:在第一次输入时将光标焦点指向输入框

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
textAreaId: { type: String },
startingText: { type: String }
};
}
constructor() {
super();
this.textAreaId = 'myText';
this.startingText = 'Focus me on first update';
}
render() {
return html`
<textarea id="${this.textAreaId}">${this.startingText}</textarea>
`;
}
firstUpdated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
const textArea = this.shadowRoot.getElementById(this.textAreaId);
textArea.focus();
}
}
customElements.define('my-element', MyElement);

2.8 updated

/**
* 重写...
*/
updated(changedProperties) { ... }
Params changedProperties map的键是已更改属性的名称;值是对应的先前值。
Updates? Yes 此方法内部的属性更改将触发元素更新。

在元素的DOM已更新和呈现时调用。重写以在更新后执行某些任务。

example:在更新后将焦点聚集到元素上

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
prop1: { type: Number },
prop2: { type: Number }
};
}
constructor() {
super();
this.prop1 = 0;
this.prop2 = 0;
}
render() {
return html`
<style>button:focus { background-color: aliceblue; }</style> <p>prop1: ${this.prop1}</p>
<p>prop2: ${this.prop2}</p> <button id="a" @click="${() => this.prop1=Math.random()}">prop1</button>
<button id="b" @click="${() => this.prop2=Math.random()}">prop2</button>
`;
}
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
let b = this.shadowRoot.getElementById('b');
b.focus();
}
}
customElements.define('my-element', MyElement);

每次更新后都会聚焦于#b元素

2.9 updateComplete

// Await Promise property.
await this.updateComplete;
Type Promise 当元素完成更新时返回一个布尔值
Resolves

如果没有其他待处理的更新则返回true

如果此更新周期触发了另一个更新则返回false

 

当元素完成更新时updateComplete  Promise resolves,使用updateComplete等待更新:

await this.updateComplete;
// do stuff
this.updateComplete.then(() => { /* do stuff */ });

example:

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
prop1: { type: Number }
};
} constructor() {
super();
this.prop1 = 0;
} render() {
return html`
<p>prop1: ${this.prop1}</p>
<button @click="${this.changeProp}">prop1</button>
`;
} async getMoreState() {
return;
} async changeProp() {
this.prop1 = Math.random();
await Promise.all(this.updateComplete, this.getMoreState());
console.log('Update complete. Other state completed.');
}
} customElements.define('my-element', MyElement);

2.10 重写updateComplete

要在实现updateComplete承诺之前等待其他状态,请重写_getUpdateComplete方法。例如,在这里等待子元素的更新可能很有用。首先等待super._getUpdateComplete(),然后等待任何后续状态。

建议覆盖_getUpdateComplete方法而不是updateComplete getter,以确保与使用TypeScript的ES5输出的用户兼容(请参阅TypeScript#338)。

class MyElement extends LitElement {
async _getUpdateComplete() {
await super._getUpdateComplete();
await this._myChild.updateComplete;
}
}

3、Examples

3.1 控制何时处理更新

重写 performUpdate方法

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() { return { prop1: { type: Number } }; } constructor() {
super();
this.prop1 = 0;
} render() {
return html`
<p>prop1: ${this.prop1}</p>
<button @click="${() => this.prop1=this.change()}">Change prop1</button>
`;
} async performUpdate() {
console.log('Requesting animation frame...');
await new Promise((resolve) => requestAnimationFrame(() => resolve()));
console.log('Got animation frame. Performing update');
super.performUpdate();
} change() {
return Math.floor(Math.random()*10);
}
}
customElements.define('my-element', MyElement);

3.2 自定义哪些属性更改应引起更新

重写shouldUpdate方法

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
prop1: { type: Number },
prop2: { type: Number }
};
}
constructor() {
super();
this.prop1 = 0;
this.prop2 = 0;
} render() {
return html`
<p>prop1: ${this.prop1}</p>
<p>prop2: ${this.prop2}</p>
<button @click="${() => this.prop1=this.change()}">Change prop1</button>
<button @click="${() => this.prop2=this.change()}">Change prop2</button>
`;
} /**
* Only update element if prop1 changed.
*/
shouldUpdate(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
return changedProperties.has('prop1');
} change() {
return Math.floor(Math.random()*10);
}
}
customElements.define('my-element', MyElement);

3.3 自定义一组属性更改

为属性指定hasChanged。请参阅属性文档。

3.4 管理对象子属性的属性更改和更新

不能观察到变动(对对象子属性和数组项的更改)。而是重写整个对象,或在发生变动后调用requestUpdate。

// Option 1: Rewrite whole object, triggering an update
this.prop1 = Object.assign({}, this.prop1, { subProp: 'data' }); // Option 2: Mutate a subproperty, then call requestUpdate
this.prop1.subProp = 'data';
this.requestUpdate();
import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() { return { prop1: { type: Object } }; }
constructor() {
super();
this.prop1 = { subProp: 0 }
}
render() {
return html`
<p>prop1.subProp: ${this.prop1.subProp}</p>
<button @click="${this.change}">change</button>
`;
}
change() {
let newVal = Math.random();
/**
* Changes to object subproperties and array items are not observable.
* Instead:
*/ // Option 1: Rewrite the whole object, triggering an update
// this.prop1 = Object.assign({}, this.prop1, { subProp: newVal }); // Option 2: Mutate a subproperty, then call requestUpdate
this.prop1.subProp = newVal;
this.requestUpdate();
}
}
customElements.define('my-element', MyElement);

3.5 在响应中更新非属性更改

调用requestUpdate:

// Request an update in response to an event
this.addEventListener('load-complete', async (e) => {
console.log(e.detail.message);
console.log(await this.requestUpdate());
});
import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
constructor() {
super(); // Request an update in response to an event
this.addEventListener('load-complete', async (e) => {
console.log(e.detail.message);
console.log(await this.requestUpdate());
});
}
render() {
return html`
<button @click="${this.fire}">Fire a "load-complete" event</button>
`;
}
fire() {
let newMessage = new CustomEvent('load-complete', {
detail: { message: 'hello. a load-complete happened.' }
});
this.dispatchEvent(newMessage);
}
}
customElements.define('my-element', MyElement);

3.6 无论属性更改如何,都请求更新

调用requestUpdate:

this.requestUpdate();

3.7 请求更正指定的属性

调用requestUpdate(propName, oldValue):

let oldValue = this.prop1;
this.prop1 = 'new value';
this.requestUpdate('prop1', oldValue);
import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
constructor() {
super(); // Request an update in response to an event
this.addEventListener('load-complete', async (e) => {
console.log(e.detail.message);
console.log(await this.requestUpdate());
});
}
render() {
return html`
<button @click="${this.fire}">Fire a "load-complete" event</button>
`;
}
fire() {
let newMessage = new CustomEvent('load-complete', {
detail: { message: 'hello. a load-complete happened.' }
});
this.dispatchEvent(newMessage);
}
}
customElements.define('my-element', MyElement);

3.8 第一次更新后做点什么

重写 firstUpdated:

firstUpdated(changedProps) {
console.log(changedProps.get('prop1'));
}
import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
textAreaId: { type: String },
startingText: { type: String }
};
}
constructor() {
super();
this.textAreaId = 'myText';
this.startingText = 'Focus me on first update';
}
render() {
return html`
<textarea id="${this.textAreaId}">${this.startingText}</textarea>
`;
}
firstUpdated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
const textArea = this.shadowRoot.getElementById(this.textAreaId);
textArea.focus();
}
}
customElements.define('my-element', MyElement);

3.9 每次更新后都做些事情

重写 updated:

updated(changedProps) {
console.log(changedProps.get('prop1'));
}
import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
prop1: { type: Number },
prop2: { type: Number }
};
}
constructor() {
super();
this.prop1 = 0;
this.prop2 = 0;
}
render() {
return html`
<style>button:focus { background-color: aliceblue; }</style> <p>prop1: ${this.prop1}</p>
<p>prop2: ${this.prop2}</p> <button id="a" @click="${() => this.prop1=Math.random()}">prop1</button>
<button id="b" @click="${() => this.prop2=Math.random()}">prop2</button>
`;
}
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
let b = this.shadowRoot.getElementById('b');
b.focus();
}
}
customElements.define('my-element', MyElement);

3.10 元素下次更新时执行一些操作

等待updateComplete承诺:

await this.updateComplete;
// do stuff
this.updateComplete.then(() => {
// do stuff
});

3.11 等待元素完成更新

等待updateComplete承诺:

let done = await updateComplete;
updateComplete.then(() => {
// finished updating
});
import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
static get properties() {
return {
prop1: { type: Number }
};
} constructor() {
super();
this.prop1 = 0;
} render() {
return html`
<p>prop1: ${this.prop1}</p>
<button @click="${this.changeProp}">prop1</button>
`;
} async getMoreState() {
return;
} async changeProp() {
this.prop1 = Math.random();
await Promise.all(this.updateComplete, this.getMoreState());
console.log('Update complete. Other state completed.');
}
} customElements.define('my-element', MyElement);

Lit-Element基本用法完

LitElement(六)生命周期的更多相关文章

  1. how tomcat works 六 生命周期

    我觉得这一章叫tomcat中的观察者模式,比较好! 首先,不要看本章,请查看一下关于观察者模式的资料比较好. 推荐以下知识点 基础篇 设计模式之禅----观察者模式 大家可以找到pdf阅读一下 另外本 ...

  2. 【微信小程序开发•系列文章六】生命周期和路由

    这篇文章理论的知识比较多一些,都是个人观点,描述有失妥当的地方希望读者指出. [微信小程序开发•系列文章一]入门 [微信小程序开发•系列文章二]视图层 [微信小程序开发•系列文章三]数据层 [微信小程 ...

  3. 【Microsoft Azure 的1024种玩法】六、使用Azure Cloud Shell对Linux VirtualMachines 进行生命周期管理

    [文章简介] Azure Cloud Shell 是一个用于管理 Azure 资源的.可通过浏览器访问的交互式经验证 shell. 它使用户能够灵活选择最适合自己工作方式的 shell 体验,本篇文章 ...

  4. Asp.Net生命周期系列六

    上篇说到当一个Http请求流到HttpHandler这里时才开始对它的处理,那么一个请求经过HttpHandler之后, 到底怎么对它处理呢,也就是说HttpHandler会触发哪些事件,触发的顺序如 ...

  5. [译] ASP.NET 生命周期 – ASP.NET 上下文对象(六)

    使用 HttpApplication 对象 ASP.NET 框架中的许多类都提供了许多很方便的属性可以直接映射到 HttpContext 类中定义的属性.这种交叠有一个很好的例子就是 HttpAppl ...

  6. android Fragments详解六:处理fragement的生命周期

    把条目添加到动作栏 你的fragment们可以向activity的菜单(按Manu键时出现的东西)添加项,同时也可向动作栏(界面中顶部的那个区域)添加条目,这都需通过实现方法onCreateOptio ...

  7. Android学习笔记(六)Fragment的生命周期

    在上一篇博文中对Fragment做了简单的介绍,现在再来探讨一下Fragment的生命周期. 一.Fragment的几种状态: 与Activity类似,Fragment也有一下几种状态: · 活动状态 ...

  8. C++服务器设计(六):设备连接的生命周期管理

    生命周期介绍 每一个服务器系统的新连接从建立开始时,均会经历多个阶段.比如连接的建立,登录的验证,退出前的资源释放等.同时在具体的消息处理中,还会遇到不可识别的消息事件,或者消息处理时出现数据错误等. ...

  9. Maven详解(六)------ 生命周期

    1.什么是 生命周期? Maven 强大的原因是有一个十分完善的生命周期,生命周期可以理解为项目构建步骤的集合,它定义了各个构建环节的执行顺序,有了这个顺序,Maven 就可以自动化的执行构建命令. ...

随机推荐

  1. centos7虚拟机分配静态IP但是得不到IP、不能上网一种可能的原因和解决办法

    1.首先通过ifconfig查看网卡,发现网卡名称为ens33 2. 在/etc/sysconfig/network-scripts/目录下查看网络配置文件 3. 发现有ifcfg-eth0的配置文件 ...

  2. Pikachu-Unsafe Filedownload(不安全的文件下载)

    不安全的文件下载概述 文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,便会向后台发送一个下载请求,一般这个请求会包含一个需要下载的文件名称,后台在收到请求后 会开始执行下载代码,将该文 ...

  3. Java出现次数最多的整数

    描述 编写一个程序,读入一组整数,这组整数是按照从小到大的顺序排列的,它们的个数N也是由用户输入的,最多不会超过20.然后程序将对这个数组进行统计,把出现次数最多的那个数组元素值打印出来.如果有两个元 ...

  4. Java输入和输出数组(加逗号)

    输入示例 61,2,3,4,5,6 输出示例 1,2,3,4,5,61,2,3,4,5,6 import java.util.Scanner; public class Demo01 { public ...

  5. python之三目运算符的替代品?

    # 不知曾几何时,你是否也觉得Python的三目运算写起来很麻烦呢?(没有过) # 比如: a, b = 3, 4 c = a if a > b else b d = a if a < b ...

  6. Spring Boot源码(一):去除web.xml

    访问https://spring.io/ spring boot中: public class MyWebApplicationInitializer implements WebApplicatio ...

  7. python3练习100题——039

    原题链接:http://www.runoob.com/python/python-exercise-example39.html 题目:有一个已经排好序的数组.现输入一个数,要求按原来的规律将它插入数 ...

  8. 最大流算法之Ford-Fulkerson算法与Edmonds–Karp算法

    引子 曾经很多次看过最大流的模板,基础概念什么的也看了很多遍.也曾经用过强者同学的板子,然而却一直不会网络流.虽然曾经尝试过写,然而即使最简单的一种算法也没有写成功过,然后对着强者大神的代码一点一点的 ...

  9. springboot里面的缓存注解

    https://blog.csdn.net/u012240455/article/details/80844361 https://lfvepclr.gitbooks.io/spring-framew ...

  10. 牛客练习赛53 B题调和级数

    https://ac.nowcoder.com/acm/contest/1114/B 这题时间卡的比较死,多了一个快速幂的logn就过不了这题. #include<bits/stdc++.h&g ...