CSSを動的にインポートする


私たちは最近、あなたのプレゼンテーションのコードをショーケースにします.DeckDeckGo .
あなたが時々私のポストを読むならば、あなたはすでに私がパフォーマンスについて気にかけるということを知っているかもしれません、そして、私は私がそうすることができるのと同じくらい怠惰な装荷概念を使用する傾向があります.だからこそ、この素晴らしいセットのテーマを追加するには、すでに魅力のように働いていたPRを提供するときに、私は怠惰なこれらの新しいCSS値を読み込む可能性を試して熱望していた.たとえ私が2、3バイトだけを惜しまないとしても、それが良い実験とゴールであると思いました😉.

導入


ソリューションの目標は、需要に応じてCSSを読み込みます.そのような目的を達成するために、我々はJavaScriptimport() . スタティックビルドスタイルを処理する代わりに、スタイルをJavaScriptコードとして統合することによって読み込みを延期します.
簡単に言うと、私たちはその場でJavascriptを通してCSSを注入します.

ダイナミックインポート


ダイナミックimport() , スクリプトモジュールの非同期ロードと実行を許可するofficial TC39 提案と標準化ECMAScript 2020 . また、それは既にトランスポーターのようにサポートされていますWebpack or Typescript .

セットアップ


解決策に直進する前に、プロジェクトを始めましょうStencil コマンドラインでnpm init stencil .
このコンポーネントは、我々はデモンストレーション目的のために開発しようとしている、どちらかの“緑”または“赤”の背景を持つテキストをレンダリングする目標を持っています.だからこそ、このようなプロパティを追加できます./src/components/my-component/my-component.tsx .
import { Component, Prop, h } from '@stencil/core';

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true
})
export class MyComponent {

  @Prop() theme: 'green' | 'red' = 'green'

  render() {
    return <div class={this.theme}>Hello, World!</div>;
  }
}
クラス名としてプロパティを適用しているので、関連するCSSを./src/components/my-component/my-component.css . 我々は現在、デモプロジェクトを設定していることに注意してください、私たちはまだソリューションを実装していないので、我々はスタイルをCSSファイルに追加する理由です.
:host {
  display: block;
}

.red {
  background: red;
}

.green {
  background: green;
}
最後に、コンポーネントに加えて、我々も追加する<select/> フィールドには、これらの色を切り替えることができます./src/index.html テスト用.
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
  <title>Stencil Component Starter</title>

  <script type="module" src="/build/lazy-css.esm.js"></script>
  <script nomodule src="/build/lazy-css.js"></script>

</head>
<body>

  <my-component></my-component>

  <select id="themeToggler" onchange="updateTheme()">
    <option value="green" selected="selected">green</option>
    <option value="red">red</option>
  </select>

  <script type="text/javascript">
    function updateTheme() {
      const toggler = document.getElementById('themeToggler');
      const elem = document.querySelector('my-component');

      elem.theme  = toggler.value;
    }
  </script>
</body>
</html>
ローカルサーバーを走らせるなら、npm run start , お気に入りのブラウザでコンポーネントをテストするには、バックグラウンドを切り替えることができます.

もっと重要なことは、デバッガを開くなら、私たちのスタイルも.green and .red が読み込まれる.これは、クライアント側がこれらの2つの色の例の1つを使用していない場合でも、これらの2つのスタイルを取得することを意味します.

解決策


楽しみましょう😜.

スタイル


まず最初に、私たちはスタイルを./src/components/my-component/my-component.css , コンポーネントの関連CSSから.
:host {
  display: block;
}

機能性成分


静的なスタイルを取り除いたので、我々は現在彼らをオンザフライで適用する方法を必要とします.そういうわけで、私たちは、目標を求めている機能的なコンポーネントをつくります<style/> ノードを遮光されたWebコンポーネントに入力します.
によるとtheme プロパティこのプロパティは、「緑」または「赤い」背景を適用します.
単純な理由のために、我々はそれを我々の構成要素メインスクリプトに宣言します./src/components/my-component/my-component.tsx .
import {Component, Prop, h, FunctionalComponent, Host, State} from '@stencil/core';

const ThemeStyle: FunctionalComponent<{style: string}> =
  ({style}) => {
    return (
      <style>{`
        :host ${style};
      `}</style>
    );
};

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true
})
export class MyComponent {

  @Prop() theme: 'green' | 'red' = 'green'

  @State()
  private style: string;

  // TODO: Dynamically import style

  render() {
    return <Host>
      <ThemeStyle style={this.style}></ThemeStyle>
      <div class={this.theme}>Hello, World!</div>
    </Host>;
  }
}

ダイナミックインポート


コンポーネントは動的に私たちのテーマをレンダリングするように設定されますが、我々はまだ怠惰なこれらをロードしません.また、我々のCSSのコンテンツが削除されている.だからこそ、実行時に取得するスタイルごとにJavaScript定数を1つ作成します.具体的には、プロジェクトでは、ファイルを作成します./src/components/my-component/red.ts “赤”のテーマ.
const theme: string = `{
  background: red;
}`;

export {theme};
もう一つ./src/components/my-component/green.ts 緑のスタイルのために.
const theme: string = `{
  background: green;
}`;

export {theme};
これらは動的な助けを借りて実行される予定ですimport() 最後にコンポーネントを追加します./src/components/my-component/my-component.tsx .
private async importTheme(): Promise<{theme}> {
  if (this.theme === 'red') {
    return import('./red');
  } else  {
    return import('./green');
  }
}
残念なことに、現在動的に使用することはできませんimport() を指定します.私が理解している限りでは、WebpackのようなバンドルがRollup , スクリプトが実行時に注入されようとしても、どのコードが使用されているかを知る必要があります.そういうわけでreturn import( $このテーマ); は準拠しない.

読み込み


我々は、我々のテーマを宣言してimport() しかし、我々はまだ結果を、コンポーネントがマウントされようとするとき、値をロードすることによって、そして、テーマプロパティがaを宣言することによって修飾されるときに、私たちがするレンダリングに適用する必要があります@Watch() .
import {Component, Prop, h, FunctionalComponent, Host, State, Watch} from '@stencil/core';

const ThemeStyle: FunctionalComponent<{style: string}> =
  ({style}) => {
    return (
      <style>{`
        :host ${style};
      `}</style>
    );
};

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true
})
export class MyComponent {

  @Prop() theme: 'green' | 'red' = 'green'

  @State()
  private style: string;

  async componentWillLoad() {
    await this.loadTheme();
  }

  @Watch('theme')
  private async loadTheme() {
    const {theme} = await this.importTheme();
    this.style = theme;
  }

  private async importTheme(): Promise<{theme}> {
    if (this.theme === 'red') {
      return import('./red');
    } else  {
      return import('./green');
    }
  }

  render() {
    return <Host>
      <ThemeStyle style={this.style}></ThemeStyle>
      <div class={this.theme}>Hello, World!</div>
    </Host>;
  }
}
ETのVILは、私たちは怠惰な負荷CSSを動的に使用することができますimport() 🎉.
開発サーバーを使用してブラウザでコンポーネントを再度テストする場合npm run start ), 我々はまだそれがまだ我々の選択に応じて異なる背景をレンダリングすることに注意してください.

さらに重要なことは、デバッガを観察するならば、我々のテーマがその場で読み込まれることにも気づくべきです.

同様に、影のある要素を見るならば、我々は関係するだけであると気がつくべきです<style/> ノードを含める必要があります.

概要


私がダイナミックに使ったのは初めてimport() WebコンポーネントのLast Load CSSには、結果に本当に満足していることを認めなければなりません.さらに、これらのテーマを加えたスライドで表示されるコードに加えますDeckDeckGo 本当に良い改善だと思います.あなたの次の話をしようとする😁.

無限に、そして、向こうに!
ダビデ
カバー写真Joshua Eckstein on Unsplash