LitElement - базовый JS класс для создания веб-компонентов
Полгода назад я "щупал" Stencil на примере модального окна. Теперь хочется сделать аналогичный компонент, используя LitElement.
В целом, у нативных веб компонентов одна из основных проблем - неудобная работа с DOM. LitElement использует lit-html для рендеринга шаблонов HTML. lit-html перерисовывает только динамические части пользовательского интерфейса, что делает обновления DOM очень быстрыми.
Напишем простое модальное окно на LitElement. Я буду использовать typescript, т.к. хочется использовать декораторы. К моему сожалению, нет офицального starter kit с использованием babel или typescript(.
Начнем
Создаём проект
npm init
Устанавливаем зависимости для разработки
npm install webpack webpack-cli webpack-dev-server typescript ts-loader --save-dev
Создаём webpack конфиг webpack.config.js
который будет транспилировать ts код в js.
var path = require('path');
module.exports = {
entry: './index.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
},
mode: 'development',
resolve: {
extensions: ['.ts', '.js', '.json'],
},
module: {
rules: [{ test: /\.ts/, use: 'ts-loader' }],
},
devServer: {
open: true,
hot: true
}
};
Прописываем script'ы для разработки в package.json
...
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server",
"clean": "rm -rf package-lock.json node_modules/ dist/",
"test": "echo \"Error: no test specified\" && exit 1"
},
...
Для разработки достаточно запустить команду npm start
.
LitElement
Разметка и стили компонента будут позаимствованы у stencil версии компонента.
Сравнительно недавно у LitElement появились декорторы.
@customElement()
- декоратор класса компонента. Задаёт используемый тег веб-компонента/** * Use the customElement decorator to define your class as * a custom element. Registers <my-element> as an HTML tag. */ @customElement('tyapk-modal') export class TyapkModalComponent extends LitElement {}
@property()
- декоратор "входящего" свойства компонента. Способ получить данные извне. В компоненте модального окна заголовок будет задаваться через@property()
/** * Header * Create an observed property. Triggers update on change. */ @property() header = '';
Использование:
<tyapk-modal header="О насущном"></tyapk-modal>
В качестве флага показывать окно или нет используется свойство
@property() show = false;
Шаблоны
Для отображения окна будет использоваться условное выражение:
// Conditional
html`${this.show?html`<p>foo</p>`:html`<p>bar</p>`}`;
В lithtml
существуют привязки:
- Text content:
<p>${...}</p>
(выводится заголовок<h2>${this.header}</h2>
) - Attribute:
<p id="${...}"></p>
- Boolean attribute:
?checked="${...}"
- Property:
.value="${...}"
- Event handler:
@event="${...}"
(обработчики кликов@click="${() => this.handleClick()}"
)
Более подбробно о разнице между Attribute и Property
Несколько функций связаных с открытием/закрытием окна:
private _close(detail: string): void {
// Fire a custom event for others to listen to
this.dispatchEvent(new CustomEvent('close', { detail }));
}
open(): void {
this.show = true;
}
handleClick(): void {
this.show = false;
this._close('ok');
}
closeModal(result: string): void {
this.show = false;
this._close(result);
}
Шаблон компонента модального окна получился следующий
render(): TemplateResult {
/**
* Use JavaScript expressions to include property values in
* the element template.
*/
return html`
${this.show
? html`
<div class="dialog">
<div class="dialog__content">
<header>
<h2>${this.header}</h2>
</header>
<main>
<slot></slot>
</main>
<footer>
<button
class="dialog__ok-btn"
@click="${() => this.handleClick()}"
>
OK
</button>
<button
class="dialog__cancel-btn"
@click="${() => this.closeModal('cancel')}"
>
Отмена
</button>
</footer>
<div
class="dialog__close-btn"
@click="${() => this.closeModal('close')}"
></div>
</div>
</div>
<div class="overlay"></div>
`
: ''}
`;
}
Стили рекомендуется писать в статическом свойстве styles
static get styles(): CSSResult {
return css`
.dialog {
top: 0;
left: 0;
right: 0;
bottom: 0;
position: fixed;
z-index: 1000;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
...
`
}
Исходный код доступен на github
Пример использования модального окна
<tyapk-modal header="Шутка юмора">
<p>Никого работа программы не удивляет так часто, как ее создателя.</p>
</tyapk-modal>
Работает корректно в браузерах с поддержкой slot&template.
Вместо заключения.
Переписать компонент из StencilJS в LitElement у меня заняло 3 часа, 2 из которых ушло на webpack+typescript и поиск starter проектов для LitElement.
- Шпаргала по шаблонам.
- https://lit-html.polymer-project.org/guide
- https://lit-element.polymer-project.org/guide
Никого работа программы не удивляет так часто, как ее создателя.