使用LIT-HTML创建Web组件的基类
可以通过LIT-HTML元素NPM软件包安装lit-element 。
lit-element使您可以使用用JavaScript模板文字表示的HTML模板创建Web组件,并有效地渲染并将这些模板重新呈现为DOM。
通过集成LIT-HTML, lit-element可以实现这一目标,并具有以下功能:
render()方法易于渲染renderCallback可能会预/填充挂钩invalidate()来手动触发重新渲染this或破坏的访问属性和方法id的ID元素this.$(...)演示可以在这里找到。
简单地使用lit-html创建render()方法来编写HTML代码。
import { LitElement , html } from '/src/lit-element.js' ;
class HelloWorld extends LitElement {
render ( ) {
return html `
< div style =" font-weight: bold " > Hello World </ div >
` ;
}
}
customElements . define ( 'hello-world' , HelloWorld ) < hello-world > </ hello-world >id查询元素在第一次渲染内容(即connectedCallback() fires)之后,您可以使用id访问Shadow root中的元素this.$(...) 。
在下面的示例中,每当按下按钮时,我们将调用this.changeColor() ,结果使用this.$("wrapper")并修改其背景颜色。
class ColorMarker extends LitElement {
changeColor ( ) {
const color = Math . random ( ) . toString ( 16 ) . substr ( 2 , 6 ) ;
// Easily query the element by id:
this . $ ( "wrapper" ) . style . backgroundColor = `# ${ color } ` ;
}
render ( ) {
return html `
< style >
div {
background-color: yellow;
}
</ style >
< button on-click = ${ ( ) => this . changeColor ( ) } >
Change background color
</ button >
< div id =" wrapper " > < slot > </ slot > </ div >
` ;
}
}
customElements . define ( 'color-marker' , ColorMarker ) ; < color-marker > Horse </ color-marker >在此示例中,我们将使用属性。静态getter properties()中定义的每个属性都将确保在修改时在正确的时间重新渲染内容。
属性可以具有默认值,甚至可以通过属性反映(更改是双向的)。与其在诸如upper-case与upperCase的特殊规则之后进行魔术和转换案例,而是定义了该属性应反映的属性名称的示例,从而避免任何歧义。
注意,使用属性时,必须在使用元素之前调用this.withProperties 。随着该方法返回类本身,可以作为customElements.define(...)
注意,属性默认值是从元素属性本身(现在或丢失)设置的,因此忽略了通过“值”设置的默认值。
import { LitElement , html } from '/src/lit-element.js' ;
class HelloWorld extends LitElement {
static get properties ( ) {
return {
uppercase : {
type : Boolean ,
attrName : "uppercase"
}
}
}
render ( ) {
return html `
< style >
.uppercase {
text-transform: uppercase;
}
</ style >
< div id =" box " class$ =" ${ this . uppercase ? 'uppercase' : '' } " >
< slot > Hello World </ slot >
</ div >
` ;
}
}
customElements . define ( 'hello-world' , HelloWorld . withProperties ( ) ) ; < hello-world > </ hello-world >
< hello-world uppercase > ¡Hola, mundo! </ hello-world > 创建自定义元素时,一个好的模式是使用属性代替方法或属性。这允许声明使用元素,就像<my-dialog opened>一样。
对于仅在其他自定义元素中消耗的自定义元素,它通常仅依靠属性更快。如果您需要传递复杂数据,例如数组或对象,也是如此。
为了使使用属性易于使用, lit-html-element仅通过定义属性的名称应使用attrName: 。
属性是否存在(在元素上)导致实际值,即。布尔属性的缺失属性意味着该属性将是false ,并且对于所有其他属性类型undefined 。这意味着,当将属性映射到属性时,没有默认值始终根据属性的存在而定义为默认值。这意味着设置value:当attrName:存在时被忽略。
使用其类型构造函数,即String String(attributeValue) , Number(attributeValue) Number ,等等。
Boolean具有特殊的处理,以遵循Web平台的模式。
根据HTML标准:
元素上布尔属性的存在代表真实值,而属性的不存在代表假值。
如果存在属性,则其值必须是空的字符串或值是ASCII案例不敏感的属性匹配的属性属性名称,而没有领先或尾随的空格。
Array和Object用于属性,没有特殊的处理,因此使用其构造函数作为其他任何值类型进行转换,除了布尔。
this将传递给您的(),这更干净。特别是在破坏时。但是,您仍然可以手动参考它们。
class RenderShorthand extends LitElement {
static get properties ( ) {
return {
greeting : {
type : String ,
value : "Hello"
}
}
}
render ( { greeting } ) {
return html ` ${ greeting } World!` ;
}
}
customElements . define ( 'render-shorthand' , RenderShorthand . withProperties ( ) ) ; 当properties()中的任何属性都会更改时, lit-element将自动重新渲染。通过attrName映射到属性的属性也是如此。
如果您需要手动重新渲染,则可以通过调用invalidate()触发重新渲染。这将安排一个微型掩体,该微型掩饰将在下一个requestAnimationFrame之前渲染内容。
自定义元素在工作之前需要升级。当浏览器拥有所需的所有资源时,这会自动发生。
这意味着,如果您执行取决于其他自定义元素并将属性用于数据流的自定义元素,则在升级该元素之前设置这些属性,这意味着您最终会遮盖lit-html-element属性,这意味着属性更新和属性反射无法正常工作。
当允许等待所有依赖项升级时,有一个API whenAllDefined(result, container) 。使用它的一种方法是覆盖renderCallback() :
renderCallback ( ) {
if ( "resolved" in this ) {
super . renderCallback ( ) ;
} else {
whenAllDefined ( this . render ( this ) ) . then ( ( ) => {
this . resolved = true ;
this . renderCallback ( ) ;
} ) ;
}
}但是,如果您在升级之前手动设置值,则可能仍然设法蒙上阴影属性
document . getElementById ( 'ninja' ) . firstName = "Ninja" ;因此,以下方式守护这些:
customElements . whenDefined ( 'computed-world' ) . then ( ( ) => {
document . getElementById ( 'ninja' ) . firstName = "Ninja" ;
} ) ;如果您需要一些计算的属性并根据其他属性进行更新,则可以使用“计算”值,该值将用参数定义为字符串的对象方法。
仅当定义所有因属性时,才能更新计算属性。可以使用value:
注意,计算的属性不能反映到属性。
例如。
import { LitElement , html } from '/node_modules/lit-html-element/lit-element.js' ;
class ComputedWorld extends LitElement {
static get properties ( ) {
return {
firstName : {
type : String ,
attrName : "first-name"
} ,
doubleMessage : {
type : String ,
computed : 'computeDoubleMessage(message)'
} ,
message : {
type : String ,
computed : 'computeMessage(firstName)' ,
value : 'Hej Verden'
}
}
}
computeDoubleMessage ( message ) {
return message + " " + message ;
}
computeMessage ( firstName ) {
return `Konichiwa ${ firstName } ` ;
}
render ( ) {
return html `
< div style =" font-weight: bold " > ${ this . doubleMessage } </ div >
` ;
}
}
customElements . define ( 'computed-world' , ComputedWorld . withProperties ( ) ) < computed-world > </ computed-world >
< computed-world first-name =" Kenneth " > </ computed-world > 可以使用Typescript而不是JavaScript使用lit-html-element 。使用TypeScript时,您可以选择使用装饰器,而不是定义静态属性访问器static get properties() 。
使用属性装饰器时,任何此类静态属性登录器都将被忽略,您也无需致电.withProperties() 。
import {
LitElement ,
html ,
TemplateResult ,
customElement ,
property ,
attribute ,
computed
} from '../../src/lit-element.js' ;
@ customElement ( 'test-element' )
export class TestElement extends LitElement {
@ computed ( 'firstName' , 'lastName' )
get fullName ( ) : string {
return ` ${ this . firstName } ${ this . lastName } ` ;
}
@ property ( ) firstName : string = 'John' ;
@ property ( ) lastName : string = 'Doe' ;
@ property ( ) human : boolean = true ;
@ property ( ) favorite : any = { fruit : 'pineapple' } ;
@ property ( ) kids : Array < string > = [ 'Peter' , 'Anna' ] ;
@ attribute ( 'mother' ) mother : string ;
@ attribute ( 'super-star' ) superStar : boolean ;
render ( ) : TemplateResult {
return html `
< h2 > Name: ${ this . fullName } </ h2 >
< h2 > Is human?: ${ human ? "yup" : "nope" } </ h2 >
< h2 > Favorites: ${ JSON . stringify ( this . favorite ) } </ h2 >
< h2 > Kids: ${ JSON . stringify ( this . kids ) } </ h2 >
< h2 > Mother: ' ${ this . mother } ' </ h2 >
< h2 > Superstar?: ' ${ this . superStar } ' </ h2 >
` ;
}
} < test-element super-star mother =" Jennifer " > </ test-element >为了使用TypeScript中的装饰器,您需要在tsconfig.json中启用experimentalDecorators编译器设置或使用--experimentalDecorators用户标志。
{
"compilerOptions" : {
"experimentalDecorators" : true
}
}启用了上述功能,您可以开始使用装饰器,但必须手动指定类型信息:
@ property ( { type : String } )
myProperty: string ;由于通常可以从属性派生,尤其是在定义类型的打字稿中,这感觉就像是双重工作。幸运的是,有一个新的规范提案称为元数据反射,旨在解决此问题。该提案尚未正式向TC39工作组正式提出(定义JavaScript标准),但是在打字稿中已经有一个可用的多填充和实验支持。
启用了元数据反射,可以更简单地定义属性类型:
@ property ( ) myProperty: string ;为了使用Typescript的装饰器,请按照以下步骤进行操作。
tsconfig.json中启用emitDecoratorMetadata编译器设置,或使用--emitDecoratorMetadata标志。 {
"compilerOptions" : {
"emitDecoratorMetadata" : true
}
}$ npm install --save-dev rbuckton/reflect-metadata < script src =" /node_modules/reflect-metadata/Reflect.js " > </ script >以下API文档使用Web IDL。
PropertyOptions PropertyOptions用于配置自定义元素的属性。在JavaScript中,您需要实现一个称为properties的静态属性访问器,该属性返回一个对象,该对象的每个属性都具有关联的PropertyOptions :
class {
static get properties ( ) {
return { selfDefinedObjectProperty : ... }
}
} PropertyOptions词典具有4个可选属性,以下以Web IDL格式显示。
typedef (BooleanConstructor or DateConstructor or NumberConstructor or StringConstructor or ArrayConstructor or ObjectConstructor) PropertyType ;
dictionary PropertyOptions {
attribute PropertyType type ;
attribute any value ;
attribute USVString attrName ;
attribute USVString computed ;
}type属性type属性仅在使用装饰器和元数据反射时是可选的。
value属性value属性定义了属性的默认值。如果通过attrName进行属性 /属性映射(请参见下文),则忽略了value 。使用装饰器时,该值是从属性定义本身中获取的:
@ property ( ) myProperty: string = "Hello World" ; attrName属性attrName定义了属性的名称,该名称应用属性反映出,另一方面应反映。使用attrName ,默认值将被忽略并从自定义元素,即。取决于属性是否存在。
属性名称,很大程度上是拉丁字母(AZ),包括“ - ”(连字符)。 HTML文档中HTML元素上的所有属性会自动获得ASCII-LOWERCALCOLCATION,并且初始连字符(' - ')被忽略。
请注意,数据属性,即。以data-开头的属性可以通过element.dataset自动作为属性访问。
当映射的属性在元素上设置时,属性将使用新值的字符串表示形式进行更新,除非新值undefined ,在该值中删除了属性。
有一个例外,因为布尔的属性反映了不同。将属性设置为true ,属性(SAI attr )设置为“空字符串'' (含义存在属性,即<div attr> )。将属性设置为false ,并删除属性,即。 <div> 。
设置属性时,使用其类型构造函数转换值,即String(attributeValue) , Number(attributeValue)的String , Number ,等等。
Boolean具有特殊的处理,以遵循Web平台的模式。
根据HTML标准:
元素上布尔属性的存在代表真实值,而属性的不存在代表假值。
如果存在属性,则其值必须是空的字符串或值是ASCII案例不敏感的属性匹配的属性属性名称,而没有领先或尾随的空格。
在上面的“属性反射”部分中阅读更多内容。
computed属性可以使用computed的其他属性来计算属性,它采用诸如'methodName(property1, property2)'之类的字符串,其中methodName是元素上的方法,而property1和property2定义。
仅当定义所有因属性时,才能更新计算属性。可以使用value:
注意,计算的属性不能反映到属性。
renderCallback renderCallback允许在渲染之前和之后进行自定义钩。
如果您需要在渲染之前进行额外的工作,例如基于另一个属性设置属性,则子类可以覆盖renderCallback()以在基类调用render()之前或之后进行工作,包括在render()之前设置依赖性属性。
withProperties()托多:
render(HTMLElement this)托多:在这里移动文档
async invalidate()托多:在这里移动文档
$(DOMString id)托多:在这里移动文档
whenAllDefined(TemplateResult result)托多:在这里移动文档
@customElement(USVString tagname)用于注册自定义元素的课堂装饰器
@ customElement ( 'my-element' )
class extends HTMLElement {
...
}@property(optional PropertyOptions options)用于连接到lit-html-element属性系统的物业装饰器。
使用属性装饰器时,您不需要定义静态属性访问器static get properties() 。
使用属性装饰器时,任何此类静态属性登录器都将被忽略,您也无需致电.withProperties() 。
@ property ( { type : String } )
myProperty: string ;检查扩展名以获取打字稿以获取更多信息。
@attribute(USVString attrName)用于连接到lit-html-element属性系统并将属性与自定义元素属性相关联的属性装饰器。
检查attrName属性以获取更多信息。
@computed(any dependency1, any dependency2, ...)用于连接到lit-html-element属性系统的属性装饰器,并创建自动计算的属性。
检查computed属性以获取更多信息。
@listen(USVString eventName, (USVString or EventTarget) target)用于添加活动听众的方法装饰器。您可以将字符串用于目标,它将使用该id搜索Shadowroot中的元素。
第一个渲染后,添加了事件侦听器,从而创建了阴影DOM。