Web労働者と画像を読み込む


Web労働者は不思議です.彼らはJavascriptのマルチスレッドを可能にします.それらの実用的なアプリケーションは、重数のクランチから非同期データの検索と配布を管理するために、イメージをロードする(この記事で論じるように).
私は実際にウェブワーカーについての今後の話を準備していると私の良い友人によると.

Of course Trezy's talk is about web workers. If he wasn't already married to Meg, the man would marry a web worker.
— @xlexious


つまり、私は彼らが少し好きだと思います.なんということか

なぜ我々はWeb労働者と画像をロードしたいですか?
UIのスレッドと労働者に移動イメージの読み込みは、画像重いサイトと私のお気に入りのWebワーカーの実装の1つの本当に素晴らしい機会です.これは、レンダリングをブロックから画像のダウンロードを防止し、それはあなたのサイトを大幅にスピードアップすることができます.
楽しい事実:<img> タグは実際にアプリケーション負荷をブロックします.あなたのページに100の画像がある場合は、ブラウザはあなたのページをレンダリングする前に、それらのすべてをダウンロードします.
いくつかの異なる環境でウェブワーカーを実装することについて少し話しましょう.

標準的なバニラ実装
あなたの平均JavaScriptアプリケーションでWebワーカーを起動するには、独自のファイルにそれを持っている必要があります.我々が私のウェブサイトに取り組んでいると仮定しましょう.https://trezy.com . 労働者ファイルに名前を付けますimage-loader.worker.js そして、それはで利用できるでしょうhttps://trezy.com/workers/image-loader.worker.js .
我々は非常に単純なウェブワーカーから始めます.
/*
 * image-loader.worker.js
 */

// The `message` event is fired in a web worker any time `worker.postMessage(<data>)` is called.
// `event.data` represents the data being passed into a worker via `worker.postMessage(<data>)`.
self.addEventListener('message', event => {
  console.log('Worker received:', event.data)
})
メインのJavaScriptファイルで使用を開始するには、次のようにします.
/*
 * main.js
 */

const ImageLoaderWorker = new Worker('/workers/image-loader.worker.js')

ImageLoaderWorker.postMessage('Hello world!')
私たちがこれをすべて積むならば、我々は見なければなりませんHello world! コンソールで.
🎉 ウッ!🥳

それに入りましょう

ステップ1:マークアップを更新
すべてのあなたの労働者の実装では、我々は現在イメージローダの実装を開始することができます.HTMLから始めましょう.
<body>
  <img data-src="/images/image1.png">
  <img data-src="/images/image2.png">
  <img data-src="/images/image3.png">
  <img data-src="/images/image4.png">
  <img data-src="/images/image5.png">
</body>

Uuuuuuuh, hang on. That's not what <img> tags look like!
— You, just now.


非常にastute観測、あなた!通常、あなたはsrc 資産<img> どこからイメージファイルをダウンロードするかを指示する要素ですが、ここではdata-src . これはブラウザが<img> による要素src 属性は、すぐに画像をダウンロードを開始します.私たちは、その仕事を私たちのウェブワーカーに対して無効にすることを望んでいますdata-src ブラウザがUIスレッドでダウンロードを処理するのを防ぐために.

ステップ2:我々のウェブワーカーにイメージURLを渡します
我々の中でmain.js ファイルは、関連のすべてを取得する必要があります<img> 我々のウェブワーカーに彼らのURLを渡すことができるように、要素:
/*
 * main.js
 */

// Load up the web worker
const ImageLoaderWorker = new Worker('/workers/image-loader.worker.js')

// Get all of the `<img>` elements that have a `data-src` property
const imgElements = document.querySelectorAll('img[data-src]')

// Loop over the image elements and pass their URLs to the web worker
imgElements.forEach(imageElement => {
  const imageURL = imageElement.getAttribute('data-src')
  ImageLoaderWorker.postMessage(imageURL)
})

ステップ3:ダウンロード画像
優れた!我々のウェブワーカーがイメージURLの束を受け取った今、彼らを処理する方法を理解しましょう.これは、いくつかの理由でWebワーカーで少し複雑になります.
  • DOM APIへのアクセスはありません.非Webワーカーイメージダウンローダの実装の多くは、新しいイメージ要素を作成し、src それの属性、ダウンロードを開始して<img> 新しいもので.これはWebワーカーの中にDOM要素を作成する方法がないからです.
  • イメージはネイティブのJavaScript形式を持っていません.画像はバイナリデータで構成されているので、JavaScriptで使用できるものに変換する必要があります.
  • 文字列を使用してUIスレッドと通信することができます.訂正されました.これはYoreの日のケースであったが、もはやありませんでした!😁
  • それで、どのように我々はイメージをダウンロードすることができますか、バイナリ形式からJavaScriptが使うことができる何かに変換されて、それから、UI糸に戻りました?ここはどこですfetchFileReader API来る.
    fetch JSON以上のもの
    あなたはおそらく見るのに慣れているfetch いくつかのAPIからデータを取得するために使用しresponse.json() レスポンスのJSON本体をオブジェクトとして取得します.しかし.json() ここで唯一のオプションではありません.また.text() , .formData() , .arrayBuffer() , そして、この運動のために我々に重要なもの..blob() .
    エー Blob 画像のようなネイティブのJavaScript形式を持っていないデータを含む、実質的に何かを表現するために使用することができます!彼らは、我々がここでしようとしていることに絶好です.それを念頭に置いて、私たちのウェブワーカーを更新しましょうBlob s
    /*
     * image-loader.worker.js
     */
    
    // I'm making the event handler `async` to make my life easier. If
    // you're not compiling your code, you may want to use the Promise-based
    // API of `fetch`
    self.addEventListener('message', async event => {
      // Grab the imageURL from the event - we'll use this both to download
      // the image and to identify which image elements to update back in the
      // UI thread
      const imageURL = event.data
    
      // First, we'll fetch the image file
      const response = await fetch(imageURL)
    
      // Once the file has been fetched, we'll convert it to a `Blob`
      const fileBlob = await response.blob()
    })
    
    大丈夫、我々は進歩している!我々は彼らが自動的にダウンロードしていないので、私たちのイメージを更新しました、我々は彼らのURLをつかみ、労働者にそれらを渡して、我々はブラウザに画像をダウンロードしました!

    ステップ4 :画像データをUIスレッドに返す
    BLOBとしてイメージを持っているので、レンダリングするUIスレッドに戻る必要があります.私たちが単独でストリングを送るならば、UI糸はどこでそれを描くべきかについて、わかりません.代わりに、UIスレッドに何を表示するか、どこに指示するかを返します.
    /*
     * image-loader.worker.js
     */
    
    self.addEventListener('message', async event => {
      const imageURL = event.data
    
      const response = await fetch(imageURL)
      const blob = await response.blob()
    
      // Send the image data to the UI thread!
      self.postMessage({
        imageURL: imageURL,
        blob: blob,
      })
    })
    
    当社の労働者ファイルが完了です!最後のステップは、UIスレッドで受け取ったものを処理することです.

    ステップ6:そのイメージをレンダリング!
    我々は、終わっているので、とても近いです!我々がする必要がある最後のことは、メインを更新します.Webワーカーから返されるイメージデータを受信して処理するJSファイル.
    /*
     * main.js
     */
    
    const ImageLoaderWorker = new Worker('/workers/image-loader.worker.js')
    const imgElements = document.querySelectorAll('img[data-src]')
    
    // Once again, it's possible that messages could be returned before the
    // listener is attached, so we need to attach the listener before we pass
    // image URLs to the web worker
    ImageLoaderWorker.addEventListener('message', event => {
      // Grab the message data from the event
      const imageData = event.data
    
      // Get the original element for this image
      const imageElement = document.querySelectorAll(`img[data-src='${imageData.imageURL}']`)
    
      // We can use the `Blob` as an image source! We just need to convert it
      // to an object URL first
      const objectURL = URL.createObjectURL(imageData.blob)
    
      // Once the image is loaded, we'll want to do some extra cleanup
      imageElement.onload = () => {
        // Let's remove the original `data-src` attribute to make sure we don't
        // accidentally pass this image to the worker again in the future
        imageElement.removeAttribute(data-src)
    
        // We'll also revoke the object URL now that it's been used to prevent the
        // browser from maintaining unnecessary references
        URL.revokeObjectURL(objectURL)
      }
    
      imageElement.setAttribute('src', objectURL)
    })
    
    imgElements.forEach(imageElement => {
      const imageURL = imageElement.getAttribute('data-src')
      ImageLoaderWorker.postMessage(imageURL)
    })
    
    チェックアウトCodepen demo すべてが一緒に働いている

    Webpackによるウェブワーカーの実施
    WebPackを使用してすべてのコードをコンパイルする場合は、Web作業員を読み込むための別のオプションがあります. worker-loader . このローダを使用すると、Webワーカーをファイルにインポートし、通常のクラスのように初期化できます.
    私は、それがこのようにもう少し自然に感じると思います.内容を変えることなくimage-loader.worker.js , これは、あなたが持っているなら、実装がどのように見えるかですworker-loader WebPackの設定
    /*
     * main.js
     */
    
    import ImageLoaderWorker from './workers/image-loader.worker.js'
    
    const imageLoader = new ImageLoaderWorker
    
    imageLoader.postMessage('Hello world!')
    
    我々のバニラ実装のように、我々は見なければなりませんHello world! コンソールでログアウトしました.

    結論
    そして、我々はしました!Webの労働者に画像をダウンロードオフローディングいくつかの異なるブラウザーAPIを使用して素晴らしい運動ですが、より重要なことは、あなたのウェブサイトのレンダリングをスピードアップする素晴らしい方法です.
    以下のコメントであなたの質問や提案をドロップしてください.あなたの好きなウェブワーカーの使用を教えてください、そして、最も重要なことに、私が私の例でものすごい何かを逃したならば、知らせてください.

    更新
    2019年11月13日
  • 追加URL.revokeObjectURL(objectURL) 呼び出しに基づく
    です.
  • 固定いくつかのtypos.