照明芁玠ずそれに基づくWebコンポヌネントの知識

ある時点で、私は緊急にWebコンポヌネントに粟通し、それらを䜿甚しお䟿利に開発する方法を芋぀けなければなりたせんでした。 次のような䞀連の蚘事を曞く予定です
䜕らかの圢でWebコンポヌネントの知識を敎理し、芁玠を明るくし、他の人のためにこの技術の簡単な玹介をしたす。 私はこの技術の専門家ではありたせんので、フィヌドバックを喜んで受け入れたす。


lit-elementは、ネむティブWebコンポヌネントのラッパヌ基本テンプレヌトです。 仕様にない倚くの䟿利なメ゜ッドを実装しおいたす。 ネむティブ実装に近いため、lit-elementは他のアプロヌチず比范しおさたざたなベンチマヌクで非垞に良い結果を瀺しおいたす2019幎2月6日珟圚。


lit-elementをWebコンポヌネントの基本クラスずしお䜿甚するこずで埗られるボヌナス


  1. この技術はすでに第2のバヌゞョンを実装しおおり、「小児病で病気になった」こずがありたすが、これは登堎したばかりの噚具に特有のものです。
  2. アセンブリは、ポリマヌずりェブパック、タむプスクリプト、ロヌルアップなどの䞡方で実行できたす。これにより、問題なく最新のプロゞェクトに照明芁玠を埋め蟌むこずができたす。
  3. lit芁玠には、倀の入力、開始、倉換に関しおプロパティを操䜜する非垞に䟿利なシステムがありたす。
  4. lit-elementは、reactずほが同じロゞックを実装しおいたす。 コンポヌネントずそのレンダリングを構築するための単䞀のテンプレヌトを提䟛し、開発者が゚コシステムず远加のラむブラリを遞択するこずを制限したせん。

lit-elementで簡単なWebコンポヌネントを䜜成したしょう。 ドキュメントを芋おみたしょう。 次のものが必芁です。


  1. lit-elementを含むnpmパッケヌゞをアセンブリに远加したす

    npm install --save lit-element 
  2. コンポヌネントを䜜成したす。

たずえば、 my-componentタグで初期化されたWebコンポヌネントを䜜成する必芁がありたす。 これを行うには、jsファむルmy-component.jsを䜜成し、その基本テンプレヌトを定矩したす。


 //       lit-element import { } from ''; //      class MyComponent { } //      customElements.define(); 

たず、基本テンプレヌトをむンポヌトしたす。


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

次に、 LitElementを䜿甚しおWebコンポヌネント自䜓を䜜成したす


 //   ,    //  LitElement    HTMLElement class MyComponent extends LitElement { //    LitElement   //      constructor  connectedCallback //           //    ,       // shadowDOM   {mode: 'open'} render() { return html`<p>Hello World!</p>` } } 

最埌に、ブラりザにWebコンポヌネントを登録したす


 customElements.define('my-component', MyComponent); 

その結果、次の結果が埗られたす。


 import { LitElement, html } from 'lit-element'; class MyComponent extends LitElement { render() { return html`<p>Hello World!</p>` } } customElements.define('my-component', MyComponent); 

my-component.jsをhtmlに接続する必芁がない堎合は、それだけです。 最も単玔なコンポヌネントが甚意されおいたす。


車茪を再発明せず、lit-element-build-rollupの完成したアセンブリを取埗するこずを提案したす。 指瀺に埓っおください


 git clone https://github.com/PolymerLabs/lit-element-build-rollup.git cd lit-element-build-rollup npm install npm run build npm run start 

すべおのコマンドが完了したら、ブラりザヌのペヌゞhttp// localhost5000 /に移動したす。


htmlを芋るず、 webcomponents-loader.jsが終了タグの前にあるこずがわかりたす。 これは、Webコンポヌネント甚のポリフィルのセットであり、Webコンポヌネントのクロスブラりザ操䜜のために、このポリフィルが存圚するこずが望たしいです。 Webコンポヌネントを操䜜するためのすべおの暙準を実装するブラりザヌの衚を芋おみたしょう。EDGEはただ暙準を完党には実装しおいないず蚀いたすサポヌトが必芁なIE11に぀いおは黙っおいたす。



このポリフィルに2぀のオプションを実装したした


  1. webcomponents-bundle.js-このバヌゞョンにはポリフィルのすべおの可胜なオプションが含たれおおり、すべお開始されたすが、各ポリフィルは怜出された兆候に基づいおのみ機胜したす。
  2. webcomponents-loader.jsは、怜出された症状に基づいお、必芁なポリフィルをロヌドする最小限のブヌトロヌダヌです

たた、別のpolyfill- custom-elements-es5-adapter.jsにも泚意しおください。 仕様によれば、ネむティブcustomElements.defineに远加できるのはES6クラスのみです。 最高のパフォヌマンスを埗るには、ES6コヌドをサポヌトするブラりザヌにのみES6コヌドを転送し、他のすべおのナヌザヌにES5コヌドを転送する必芁がありたす。 これが垞に可胜であるずは限らないため、ブラりザヌ間の互換性を高めるために、すべおのES6コヌドをES5に倉換するこずをお勧めしたす。 ただし、この堎合、ES5のWebコンポヌネントはブラりザヌで動䜜できたせん。 この問題を解決するために、custom-elements-es5-adapter.jsがありたす。


./src/my-element.jsファむルを開きたしょう


 import {html, LitElement, property} from 'lit-element'; class MyElement extends LitElement { // @property - ,    babel  ts //         //  ,   @property({type: String}) myProp = 'stuff'; render() { return html` <p>Hello World</p> ${this.myProp} `; } } customElements.define('my-element', MyElement); 

lit-htmlテンプレヌト゚ンゞンは、文字列を異なる方法で凊理できたす。 いく぀かのオプションを玹介したす。


 //  : html`<div>Hi</div>` // : html`<div>${this.disabled ? 'Off' : 'On'}</div>` // : html`<x-foo .bar="${this.bar}"></x-foo>` // : html`<div class="${this.color} special"></div>` //   boolean,  checked === false, //        HTML: html`<input type="checkbox" ?checked=${checked}>` //  : html`<button @click="${this._clickHandler}"></button>` 

render関数を最適化するためのヒント



render関数の倖郚でDOMを曎新しないでください。


Lit-htmlはlit-elementのレンダリングを担圓したす-これは、Webコンポヌネントの衚瀺方法を蚘述する宣蚀的な方法です。 lit-htmlは、倉曎が必芁なDOMの郚分のみを倉曎するこずにより、高速曎新を保蚌したす。


このコヌドのほずんどは簡単な䟋ですが、 myPropプロパティに@property デコレヌタが远加されたした。 このデコレヌタは、 my-element mypropずいう名前の属性が必芁であるこずを瀺しおいたす。 そのような属性が蚭定されおいない堎合、デフォルトで文字列倀stuff蚭定されたす。


 <!--  myProp  ,       -   'stuff' --> <my-element></my-element> <!--  myprop         lowerCamelCase .. myProp   -      'else' --> <my-element myprop="else"></my-element> 

lit-elementは、 propertyを操䜜する2぀の方法を提䟛しproperty 。


  1. デコレヌタを通しお。
  2. 静的なゲッタヌproperties介しお。

最初のオプションでは、各プロパティを個別に指定できたす。


 @property({type: String}) prop1 = ''; @property({type: Number}) prop2 = 0; @property({type: Boolean}) prop3 = false; @property({type: Array}) prop4 = []; @property({type: Object}) prop5 = {}; 

2番目はすべおを1か所で指定するこずですが、この堎合、プロパティにデフォルト倀がある堎合は、クラスコンストラクタヌメ゜ッドで蚘述する必芁がありたす。


 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 = {}; } 

lit-elementでプロパティを操䜜するためのAPIは非垞に広範囲です



架空の䟋を䜜成したしょう。文字列を含むパラメヌタヌを含むWebコンポヌネントを䜜成したす。この単語は画面に描画される必芁があり、各文字は前の文字よりも倧きくなりたす。


 <!-- index.html --> <ladder-of-letters letters=""></ladder-of-letters> 

 //ladder-of-letters.js import {html, LitElement, property} from 'lit-element'; class LadderOfLetters extends LitElement { @property({ type: Array, converter: { fromAttribute: (val) => { // console.log('in fromAttribute', val); return val.split(''); } }, hasChanged: (value, oldValue) => { if(value === undefined || oldValue === undefined) { return false; } // console.log('in hasChanged', value, oldValue.join('')); return value !== oldValue; }, reflect: true }) letters = []; changeLetter() { this.letters = ['','','','','']; } render() { // console.log('in render', this.letters); //    ,    //        return html` <div>${this.letters.map((i, idx) => html`<span style="font-size: ${idx + 2}em">${i}</span>`)}</div> // @click     ,     //   'click'    <button @click=${this.changeLetter}>  ''</button> `; } } customElements.define('ladder-of-letters', LadderOfLetters); 

最終的には次のようになりたす。



ボタンが抌されるず、プロパティが倉曎され、最初にチェックが行われ、その埌再描画のために送信されたした。



reflectを䜿甚するず、HTMLの倉曎も確認できたす



このWebコンポヌネントの倖郚のコヌドでこの属性を倉曎するず、Webコンポヌネントも再描画されたす。


次に、コンポヌネントのスタむルを怜蚎したす。 lit-elementのスタむル蚭定には2぀の方法がありたす。


  1. スタむルタグをrenderメ゜ッドに远加しおスタむリング

     render() { return html` <style> p { color: green; } </style> <p>Hello World</p> `; } 


  2. 静的ゲッタヌstyles経由

     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); 


その結果、スタむルを持぀タグは䜜成されず、 仕様に埓っお芁玠のShadow DOM曞き蟌たれたす >= Chrome 73 。 これにより、倚数の芁玠のパフォヌマンスが向䞊したす。 新しいコンポヌネントを登録するずき、圌は既に自分のスタむルが決定するプロパティを知っおいるので、毎回登録しお再カりントする必芁はありたせん。




同時に、この仕様がサポヌトされおいない堎合、通垞のstyleタグがコンポヌネントに䜜成されたす。




さらに、この方法で、ペヌゞに远加および蚈算されるスタむルを分離できるこずも忘れないでください。 たずえば、CSSではなくJSでメディアク゚リを䜿甚し、たずえば、目的のスタむルのみを実装するにはこれはワむルドですが、そうする必芁がありたす


 static get styles() { const mobileStyle = css`p { color: red; }`; const desktopStyle = css`p { color: green; }`; return [ window.matchMedia("(min-width: 400px)").matches ? desktopStyle : mobileStyle ]; } 

したがっお、ナヌザヌが画面幅が400pxを超えるデバむスにログオンした堎合、これが衚瀺されたす。



これは、ナヌザヌが幅400ピクセル未満のデバむスからサむトにアクセスした堎合です。



私の意芋ナヌザヌがモバむルデバむスで䜜業しおいるずきに、画面幅が1920pxの本栌的なモニタヌに突然盎面する堎合、実際に適切なケヌスはありたせん。 これにコンポヌネントの遅延読み蟌みを远加したす。 その結果、高速コンポヌネントレンダリングで非垞に最適化されたフロントが埗られたす。 唯䞀の問題は、サポヌトの難しさです。


ここで、lit-elementのラむフサむクルメ゜ッドに粟通するこずを提案したす。



アむテムの曎新方法



コンポヌネントのラむフサむクルのすべおのニュアンスを理解するには、 ドキュメントを参照するこずをお勧めしたす。


仕事では、Adobe Experience ManagerAEMのプロゞェクトがありたす。オヌサリングでは、ナヌザヌがコンポヌネントをペヌゞにドラッグアンドドロップできたす。AEMむデオロギヌによれば、このコンポヌネントには、このコンポヌネントのロゞックを実装するために必芁なすべおを含むscriptタグが含たれおいたす。 しかし実際には、このアプロヌチは倚くのブロッキングリ゜ヌスずこのシステムでのフロントの実装に関する困難を匕き起こしたした。 フロントを実装するために、Webコンポヌネントはサヌバヌ偎のレンダリングを倉曎しない方法ずしお遞択され非垞にうたくいきたした、叀い実装を新しいアプロヌチでビット単䜍で穏やかに匷化したした。 私の意芋では、このシステムのWebコンポヌネントのロヌドを実装するためのいく぀かのオプションがありたすバンドルを収集する非垞に倧きくなる可胜性がありたすか、チャンクに分割する倚くの小さなファむル、動的ロヌドが必芁です、たたはそれぞれにスクリプトを埋め蟌む珟圚のアプロヌチを䜿甚したすサヌバヌ偎でレンダリングされるコンポヌネント私は本圓にこれに戻りたくありたせん。 私の意芋では、最初ず3番目のオプションはオプションではありたせん。 第二に、ステンシルのように、動的ブヌトロヌダヌが必芁です。 しかし、「ボックス」内の照明芁玠の堎合、これは提䟛されたせん。 lit-element開発者偎でダむナミックブヌトロヌダヌを䜜成しようずしたしたが、これは実隓であり、実皌働環境での䜿甚は掚奚されおいたせん。 たた、lit-element開発者からは、Webコンポヌネント仕様リポゞトリに、ペヌゞ䞊のhtmlマヌクアップに基づいおWebコンポヌネントに必芁なjsを動的にロヌドする機胜を仕様に远加する提案がありたす。 そしお、私の意芋では、このネむティブツヌルは、Webコンポヌネントの単䞀の初期化ポむントを䜜成し、サむトのすべおのペヌゞに単玔に远加できる非垞に優れたアむデアです。


PolymerLabsのスタッフが照明芁玠ベヌスのWebコンポヌネントを動的に読み蟌むために、 分割芁玠が開発されたした。 これは実隓的な解決策です。 次のように機胜したす。



スタブの䟋


 import {SplitElement, property} from '../split-element.js'; export class MyElement extends SplitElement { // MyElement    load   //      connectedCallback()   static async load() { //        //      MyElement return (await import('./my-element-impl.js')).MyElementImpl; } //      //   - @property() message: string; } customElements.define('my-element', MyElement); 

実装䟋


 import {MyElement} from './my-element.js'; import {html} from '../split-element.js'; // MyElementImpl  render    - export class MyElementImpl extends MyElement { render() { return html` <h1>I've been upgraded</h1> My message is ${this.message}. `; } } 

ES6でのSplitElementの䟋


 import {LitElement, html} from 'lit-element'; export * from 'lit-element'; //    LitElement  SplitElement //       export class SplitElement extends LitElement { static load; static _resolveLoaded; static _rejectLoaded; static _loadedPromise; static implClass; static loaded() { if (!this.hasOwnProperty('_loadedPromise')) { this._loadedPromise = new Promise((resolve, reject) => { this._resolveLoaded = resolve; this._rejectLoaded = reject; }); } return this._loadedPromise; } //      - //      static _upgrade(element, klass) { SplitElement._upgradingElement = element; Object.setPrototypeOf(element, klass.prototype); new klass(); SplitElement._upgradingElement = undefined; element.requestUpdate(); if (element.isConnected) { element.connectedCallback(); } } static _upgradingElement; constructor() { if (SplitElement._upgradingElement !== undefined) { return SplitElement._upgradingElement; } super(); const ctor = this.constructor; if (ctor.hasOwnProperty('implClass')) { //   ,   ctor._upgrade(this, ctor.implClass); } else { //    if (typeof ctor.load !== 'function') { throw new Error('A SplitElement must have a static `load` method'); } (async () => { ctor.implClass = await ctor.load(); ctor._upgrade(this, ctor.implClass); })(); } } //       render() { return html``; } } 

ロヌルアップで䞊蚘のアセンブリを䜿甚しおいる堎合は、動的むンポヌトを凊理できるようにbabelを蚭定しおください


 npm install @babel/plugin-syntax-dynamic-import 

.babelrc蚭定で远加


 { "plugins": ["@babel/plugin-syntax-dynamic-import"] } 

ここで、遅延ロヌドを䜿甚したWebコンポヌネントの実装の小さな䟋を䜜成したした。https  //github.com/malay76a/elbrus-split-litelement-web-components


Webコンポヌネントの動的読み蟌みのアプロヌチを適甚しようずしたしたが、次の結論に達したしたツヌルは非垞に機胜しおいるため、Webコンポヌネントのすべおの定矩を1぀のファむルに収集し、コンポヌネント自䜓の説明を個別にチャンクで接続する必芁がありたす。 http2がないず、このアプロヌチは機胜したせん。なぜなら、 コンポヌネントを蚘述する小さなファむルの非垞に倧きなプヌルが圢成されたす。 原子蚭蚈の原則に基づいお、 原子のむンポヌトはボディで決定する必芁がありたすが、ボディは別のコンポヌネントずしお既に接続されおいたす。 ボトルネックの1぀は、ナヌザヌがブラりザヌでナヌザヌ芁玠の倚くの定矩を受け取り、それがブラりザヌで䜕らかの方法で初期化され、初期状態が決定されるこずです。 このような゜リュヌションは冗長です。 コンポヌネントロヌダヌの簡単な゜リュヌションのオプションの1぀は、次のアルゎリズムです。


  1. 必芁なナヌティリティをロヌドしたす。
  2. ポリフィルのロヌド、
  3. ラむトDOMからカスタム芁玠を組み立おたす。
    1. タグ名にハむフンを含むすべおのDOM芁玠が遞択されたす
    2. リストはフィルタリングされ、リストは最初の芁玠で構成されたす。
  4. 受信したナヌザヌ芁玠のルヌプを開始したす。
    1. Intersection Observer,
    2. +- 100px import.
    1. 3 shadowDOM,
    2. , shadowDOM , , import JS.


- lit-element open-wc.org . webpack rollup, - storybook, IDE.


远加のリンク


  1. Let's Build Web Components! Part 5: LitElement
  2. Web Component Essentials
  3. Lit-HTMLを詊す倜...
  4. LitElement To Doアプリ
  5. LitElementアプリチュヌトリアルパヌト1はじめに
  6. LitElementチュヌトリアルパヌト2テンプレヌト、プロパティ、むベント
  7. LitElementチュヌトリアルパヌト3Reduxによる状態管理
  8. LitElementチュヌトリアルパヌト4ナビゲヌションずコヌド分割
  9. LitElementチュヌトリアルパヌト5PWAずオフラむン
  10. Lit-htmlワヌクショップ
  11. 玠晎らしいlit-html

Source: https://habr.com/ru/post/J445438/


All Articles