Em um ponto, tive que me familiarizar com os componentes da Web com urgência e encontrar uma maneira de desenvolver convenientemente usando-os. Eu pretendo escrever uma série de artigos que
de alguma forma, organizar o conhecimento de componentes da web, elemento iluminado e fornecer uma breve introdução a essa tecnologia para outros. Não sou especialista nesta tecnologia e aceitarei com prazer qualquer feedback.
lit-element é um invólucro (modelo básico) para componentes da web nativos. Ele implementa muitos métodos convenientes que não estão na especificação. Devido à sua proximidade com a implementação nativa, o lit-element mostra resultados muito bons em várias referências em relação a outras abordagens (a partir de 06/02/2019).
Bônus que vejo ao usar o lit-element como uma classe base de componentes da web:
- Essa tecnologia já implementa a segunda versão e “adoeceu com doenças da infância”, peculiares aos instrumentos que acabaram de aparecer.
- A montagem pode ser realizada tanto em polímero quanto em webpack, texto datilografado, rollup etc., permitindo incorporar elementos iluminados em qualquer projeto moderno sem problemas.
- O elemento lit tem um sistema muito conveniente de trabalhar com propriedades em termos de digitar, iniciar e converter valores.
- o elemento lit implementa quase a mesma lógica que a reação, ou seja, fornece o mínimo - um modelo único para a construção de componentes e sua renderização e não limita o desenvolvedor a escolher um ecossistema e bibliotecas adicionais.
Crie um componente da web simples no elemento iluminado. Vamos voltar para a documentação. Precisamos do seguinte:
- Adicione pacote npm com elemento lit à nossa montagem
npm install --save lit-element
- Crie nosso componente.
Por exemplo, precisamos criar um componente da web inicializado na tag my-component
. Para fazer isso, crie o arquivo js my-component.js
e defina seu modelo básico:
Primeiro, importamos nosso modelo básico:
import { LitElement, html } from 'lit-element';
Em segundo lugar, crie o próprio componente da Web usando LitElement
E a última coisa é registrar o componente da web no navegador
customElements.define('my-component', MyComponent);
Como resultado, obtemos o seguinte:
import { LitElement, html } from 'lit-element'; class MyComponent extends LitElement { render() { return html`<p>Hello World!</p>` } } customElements.define('my-component', MyComponent);
Se você excluir a necessidade de conectar o my-component.js
ao html, é isso. O componente mais simples está pronto.
Proponho não reinventar a roda e fazer a montagem final do acúmulo de elementos iluminados. Siga as instruções:
git clone https://github.com/PolymerLabs/lit-element-build-rollup.git cd lit-element-build-rollup npm install npm run build npm run start
Após a conclusão de todos os comandos, vamos para a página no navegador http: // localhost: 5000 / .
Se dermos uma olhada em html, veremos que webcomponents-loader.js está na frente da tag de fechamento. Este é um conjunto de polyfills para componentes da web e, para operação entre navegadores do componente da web, é desejável que esse polyfill esteja presente. Vamos dar uma olhada na tabela de navegadores que implementam todos os padrões para trabalhar com componentes da Web. Ele diz que o EDGE ainda não implementa totalmente os padrões (estou em silêncio sobre o IE11, que ainda precisa ser suportado).

Foram implementadas 2 opções para este polyfill:
- webcomponents-bundle.js - esta versão contém todas as opções possíveis para o polyfill, todas iniciadas, mas cada polyfill funcionará apenas com base nos sinais detectados.
- O webcomponents-loader.js é um carregador de inicialização mínimo que, com base nos sintomas detectados, carrega os polyfills necessários
Também peço que você preste atenção em outro polyfill - custom-elements-es5-adapter.js . De acordo com a especificação, apenas as classes ES6 podem ser adicionadas ao customElements.define nativo. Para um melhor desempenho, o código ES6 deve ser passado apenas para os navegadores que o suportam e o ES5 para todos os outros. Isso nem sempre é possível, portanto, para uma melhor compatibilidade entre navegadores, é recomendável que todo o código ES6 seja convertido em ES5. Mas, nesse caso, os componentes da web no ES5 não poderão funcionar nos navegadores. Para resolver esse problema, há custom-elements-es5-adapter.js.
Agora vamos abrir o arquivo ./src/my-element.js
import {html, LitElement, property} from 'lit-element'; class MyElement extends LitElement {
O mecanismo de modelo lit-html pode processar uma sequência de maneira diferente. Vou lhe dar várias opções:
Dicas para otimizar a função render ():
- não deve alterar o estado de um elemento,
- não deve ter efeitos colaterais,
- deve depender apenas das propriedades do elemento,
- deve retornar o mesmo resultado ao transmitir os mesmos valores.
Não atualize o DOM fora da função render ().
O Lit-html é responsável por renderizar o elemento lit - essa é uma maneira declarativa de descrever como o componente da web deve ser exibido. O lit-html garante atualizações rápidas, alterando apenas as partes do DOM que precisam ser alteradas.
Quase todo esse código estava em um exemplo simples, mas o decorador @property
foi adicionado à propriedade myProp
. Esse decorador indica que estamos esperando um atributo chamado myprop
em nosso my-element
. Se nenhum desses atributos estiver definido, o valor da sequência será definido como stuff
por padrão.
<my-element></my-element> <my-element myprop="else"></my-element>
O lit-element fornece duas maneiras de trabalhar com a property
:
- Através do decorador.
- Via
properties
estáticas de um getter.
A primeira opção permite especificar cada propriedade separadamente:
@property({type: String}) prop1 = ''; @property({type: Number}) prop2 = 0; @property({type: Boolean}) prop3 = false; @property({type: Array}) prop4 = []; @property({type: Object}) prop5 = {};
O segundo é especificar tudo em um só lugar, mas, neste caso, se a propriedade tiver um valor padrão, ela deverá ser escrita no método construtor da classe:
static get properties() { return { prop1: {type: String}, prop2: {type: Number}, prop3: {type: Boolean}, prop4: {type: Array}, prop5: {type: Object} }; } constructor() { this.prop1 = ''; this.prop2 = 0; this.prop3 = false; this.prop4 = []; this.prop5 = {}; }
A API para trabalhar com propriedades no elemento lit é bastante extensa:
- atributo : se uma propriedade pode se tornar um atributo observável. Se
false
, o atributo será excluído da observação; nenhum getter será criado para ele. Se true
ou attribute
ausente, a propriedade especificada no getter no formato lowerCamelCase corresponderá ao atributo no formato da sequência. Se uma sequência for especificada, por exemplo, my-prop
, ela corresponderá com o mesmo nome nos atributos. - conversor : contém uma descrição de como converter um valor de / para um atributo / propriedade. O valor pode ser uma função que funciona para serializar e desserializar o valor, ou pode ser um objeto com as teclas
toAttribute
e toAttribute
; essas chaves contêm funções separadas para a conversão dos valores. Por padrão, a propriedade contém uma conversão para os tipos base Boolean
, String
, Number
, Object
e Array
. As regras de conversão estão listadas aqui . - tipo : indica um dos tipos de base que esta propriedade conterá. É usado como uma "dica" para o conversor sobre o tipo que a propriedade deve conter.
- refletir : indica se o atributo deve ser associado à propriedade (
true
) e alterado de acordo com as regras do type
e do converter
. - hasChanged : cada propriedade possui, contém uma função que determina se há uma alteração entre o antigo e o novo valor, respectivamente, retorna um
Boolean
. Se true
, ele começa a atualizar o item. - noAccessor : esta propriedade aceita um
Boolean
e o padrão é false
. Proíbe a geração de getters e setters para cada propriedade para acessá-los da classe. Isso não cancela a conversão.
Vamos fazer um exemplo hipotético: escreveremos um componente da web que contém um parâmetro que contém uma string. Essa palavra deve ser desenhada na tela, na qual cada letra é maior que a anterior.
<ladder-of-letters letters=""></ladder-of-letters>
no final, temos:

quando o botão foi clicado, a propriedade foi alterada, o que causou a verificação primeiro e depois foi enviada para redesenho.

e usando reflect
também podemos ver alterações html

Se você alterar esse atributo com código fora deste componente da web, também causaremos um redesenho do componente da web.
Agora considere o estilo do componente. Temos duas maneiras de estilizar o elemento iluminado:
- Estilo adicionando uma marca de estilo ao método de renderização
render() { return html` <style> p { color: green; } </style> <p>Hello World</p> `; }

- Via
styles
getter estático
import {html, LitElement, css} from 'lit-element'; class MyElement extends LitElement { static get styles() { return [ css` p { color: red; } ` ]; } render() { return html` <p>Hello World</p> `; } } customElements.define('my-element', MyElement);
Como resultado, concluímos que uma tag com estilos não é criada, mas é gravada ( >= Chrome 73
) no Shadow DOM
elemento, de acordo com a especificação . Isso melhora o desempenho com um grande número de elementos, porque ao registrar um novo componente, ele já sabe quais propriedades seus estilos determinam; eles não precisam ser registrados todas as vezes e recontados.

Além disso, se essa especificação não for suportada, uma tag de style
regular será criada no componente.

Além disso, não esqueça que dessa maneira também podemos separar quais estilos serão adicionados e calculados na página. Por exemplo, para usar consultas de mídia não em css, mas em JS e implementar apenas o estilo desejado, por exemplo (isso é selvagem, mas precisa ser):
static get styles() { const mobileStyle = css`p { color: red; }`; const desktopStyle = css`p { color: green; }`; return [ window.matchMedia("(min-width: 400px)").matches ? desktopStyle : mobileStyle ]; }
Dessa forma, veremos isso se o usuário efetuou logon em um dispositivo com uma largura de tela superior a 400px.

E isso é se o usuário visitou o site a partir de um dispositivo com uma largura inferior a 400px.

Minha opinião: praticamente não existe um caso adequado quando um usuário, trabalhando em um dispositivo móvel, de repente enfrenta um monitor completo com uma largura de tela de 1920 px. Adicione a isso o carregamento lento dos componentes. Como resultado, obtemos uma frente muito otimizada com renderização rápida de componentes. O único problema é a dificuldade em apoiar.
Agora, proponho me familiarizar com os métodos do ciclo de vida do elemento iluminado:
- render () : implementa uma descrição do elemento DOM usando
lit-html
. Idealmente, a função render
é uma função pura que usa apenas as propriedades atuais do elemento. O método render()
é chamado pela função update()
. - shouldUpdate (modifiedProperties) : implementado se for necessário controlar a atualização e a renderização, quando as propriedades foram alteradas ou
requestUpdate()
chamado. O argumento para a função changedProperties
é um Map
contém chaves para as propriedades alteradas. Por padrão, esse método sempre retorna true
, mas a lógica do método pode ser alterada para controlar a atualização do componente. - performUpdate () : implementado para controlar o tempo de atualização, por exemplo, para integrar-se ao planejador.
- update (modifiedProperties) : esse método chama
render()
. Também atualiza os atributos de um elemento de acordo com o valor da propriedade. Definir propriedades dentro deste método não causará outra atualização. - firstUpdated (modifiedProperties) : chamado após a primeira atualização do elemento DOM imediatamente antes da chamada
updated()
. Esse método pode ser útil para capturar links para nós estáticos visualizados com os quais você precisa trabalhar diretamente, por exemplo, em updated()
. - updated (modifiedProperties) : chamado sempre que o DOM de um item é atualizado e exibido. Uma implementação para executar tarefas após a atualização por meio da API DOM, por exemplo, com foco em um elemento.
- requestUpdate (name, oldValue) : chama uma solicitação de atualização assíncrona para um item. Isso deve ser chamado quando o item precisar ser atualizado com base em algum estado não causado pela configuração da propriedade.
- createRenderRoot () : por padrão, cria uma raiz de sombra para o item. Se o uso do Shadow DOM não for necessário, o método deve retornar
this
.
Como o elemento é atualizado:
- A propriedade recebe um novo valor.
- Se a propriedade
hasChanged(value, oldValue)
retornar false
, o item não será atualizado. Caso contrário, uma atualização é planejada chamando requestUpdate()
. - requestUpdate () : atualiza o elemento após microtask (no final do loop de eventos e antes do próximo redesenho).
- performUpdate () : a atualização está em andamento e continua com o restante da API de atualização.
- shouldUpdate (modifiedProperties) : a atualização continuará se
true
retornado. - firstUpdated (modifiedProperties) : chamado quando o item é atualizado pela primeira vez, imediatamente antes de chamar
updated()
. - update (modifiedProperties) : atualiza o item. Alterar propriedades neste método não causa outra atualização.
- render () : retorna um modelo
lit-html
para renderizar um elemento no DOM. Alterar propriedades neste método não causa outra atualização.
- updated (modifiedProperties) : chamado sempre que um item é atualizado.
Para entender todas as nuances do ciclo de vida do componente, aconselho a consultar a documentação .
No trabalho, tenho um projeto no Adobe Experience Manager (AEM), na sua criação, o usuário pode arrastar e soltar componentes na página e, de acordo com a ideologia do AEM, esse componente contém uma tag de script
que contém tudo o que é necessário para implementar a lógica desse componente. Mas, de fato, essa abordagem deu origem a muitos recursos e dificuldades de bloqueio na implementação da frente nesse sistema. Para implementar a frente, os componentes da web foram escolhidos como uma maneira de não alterar a renderização do servidor (o que ele fez muito bem), além de enriquecer a implementação antiga com uma nova abordagem suavemente, bit a bit. Na minha opinião, existem várias opções para implementar o carregamento de componentes da Web para esse sistema: coletar um pacote (que pode se tornar muito grande) ou dividi-lo em pedaços (são necessários muitos arquivos pequenos, é necessário carregamento dinâmico) ou usar a abordagem atual para incorporar um script em cada um componente que é renderizado no lado do servidor (eu realmente não quero voltar a isso). Na minha opinião, a primeira e a terceira opção não são uma opção. Para o segundo, você precisa de um carregador de inicialização dinâmico, como no estêncil. Mas para o elemento iluminado na "caixa" isso não é fornecido. Houve uma tentativa dos desenvolvedores de elementos iluminados de criar um carregador dinâmico , mas é um experimento e não é recomendável usá-lo na produção. Também dos desenvolvedores de elementos iluminados, há um problema no repositório de especificações de componentes da Web com uma proposta para adicionar à especificação a capacidade de carregar dinamicamente os js necessários para o componente da Web com base na marcação html na página. E, na minha opinião, essa ferramenta nativa é uma ideia muito boa que permitirá criar um ponto de inicialização de componentes da Web e simplesmente adicioná-lo a todas as páginas do site.
Para carregar dinamicamente componentes da Web de elementos iluminados dinamicamente com os caras do PolymerLabs, foi desenvolvido um elemento de divisão . Esta é uma solução experimental. Funciona da seguinte maneira:
- Para criar um SplitElement, você escreve duas definições de elementos em dois módulos.
- Um deles é um esboço, que define as partes carregadas de um elemento: geralmente esse é o nome e as propriedades. As propriedades devem ser definidas com um stub para que o elemento lit possa gerar atributos observáveis em tempo hábil para chamar
customElements.define()
. - O stub também deve ter um método de carregamento assíncrono estático que retorna uma classe de implementação.
- Outra classe é a "implementação", que contém todo o resto.
- O construtor
SplitElement
carrega a classe de implementação e executa upgrade()
.
Exemplo de stub:
import {SplitElement, property} from '../split-element.js'; export class MyElement extends SplitElement {
Exemplo de implementação:
import {MyElement} from './my-element.js'; import {html} from '../split-element.js';
Exemplo de SplitElement no ES6:
import {LitElement, html} from 'lit-element'; export * from 'lit-element';
Se você ainda estiver usando a montagem sugerida acima no Rollup, certifique-se de definir o babel para poder manipular importações dinâmicas
npm install @babel/plugin-syntax-dynamic-import
E nas configurações .babelrc adicione
{ "plugins": ["@babel/plugin-syntax-dynamic-import"] }
Aqui fiz um pequeno exemplo da implementação de componentes da Web com carregamento atrasado: https://github.com/malay76a/elbrus-split-litelement-web-components
Tentei aplicar a abordagem de carregamento dinâmico de componentes da web, cheguei à seguinte conclusão: a ferramenta está funcionando bem, é necessário coletar todas as definições de componentes da web em um arquivo e conectar a descrição do próprio componente através de pedaços separadamente. Sem o http2, essa abordagem não funciona, porque Um conjunto muito grande de arquivos pequenos que descrevem os componentes é formado. Com base no princípio do design atômico , a importação de átomos deve ser determinada no corpo, mas o corpo já deve estar conectado como um componente separado. Um dos gargalos é que o usuário receberá muitas definições de elementos do usuário no navegador que serão inicializadas de uma maneira ou de outra no navegador e o estado inicial será determinado. Essa solução é redundante. Uma das opções para uma solução simples para o carregador de componentes é o seguinte algoritmo:
- carregar os utilitários necessários,
- carregar polyfills,
- montar elementos personalizados a partir do light DOM:
- todos os elementos DOM que contêm um hífen no nome da tag são selecionados
- a lista é filtrada e é formada uma lista dos primeiros elementos.
- :
- Intersection Observer,
- +- 100px import.
- 3 shadowDOM,
- , shadowDOM , , import JS.
- lit-element open-wc.org . webpack rollup, - storybook, IDE.
:
- Let's Build Web Components! Part 5: LitElement
- Web Component Essentials
- A night experimenting with Lit-HTML…
- LitElement To Do App
- LitElement app tutorial part 1: Getting started
- LitElement tutorial part 2: Templating, properties, and events
- LitElement tutorial part 3: State management with Redux
- LitElement tutorial part 4: Navigation and code splitting
- LitElement tutorial part 5: PWA and offline
- Lit-html workshop
- Awesome lit-html