1.1.1-beta • Published 4 years ago

@tygr/async-loader-webpack-plugin v1.1.1-beta

Weekly downloads
1
License
ISC
Repository
github
Last release
4 years ago

DEPRECATED

I've decided no longer to support this project due to lack of interest.

If anybody is interested in using it please ask me for help at

tylergrinn@gmail.com

Async Loader Webpack Plugin

DEMO

Is your SPA getting unwieldy? Use this plugin to give immediate content to clients while loading large scripts and styles in the background.

When using async-loader-webpack-plugin, an object named AsyncLoader is exposed which emits loading progress events. Write a loading bar in your index.html template file that subscribes to AsyncLoader. Once the scripts have been downloaded and injected and processed by the browser, AsyncLoader will emit a complete event.

This plugin is meant to be used with HtmlWebpackPlugin

Installation

npm install --save-dev @tygr/async-loader-webpack-plugin

yarn add --dev @tygr/async-loader-webpack-plugin

Plugin Options

optiontypedefaultdescription
chunksstring[]nullChoose which files to load by specifiying a list of chunks where each chunk's list of files will load asyncronously
patternsRegExp[]nullChoose which files to load by specifying a list of regex patterns that will test each file emitted by webpack
filesstring[][]Choose files manually using a list of strings, each one will be attempted to load asynchronously regardless of whether or not it is emitted by webpack
extensionsstring[]['css', 'js']Specify which extensions you would like to load asynchronously. Files specified by 'files' option ignores this option
scriptNamestring'async-loader'AsyncLoader will put a script named 'async-loader.js' in your output directory and add it into HTMLWebpackPlugin. You may specify a different name for the script here
loadbooleantrueChoose whether or not to load files immediately or wait until AsyncLoader.load() is called

chunks and patterns are mutually exlusive, use one or the other

AsyncLoader Events

AsyncLoader will be injected into the browser globally. To listen to an event from AsyncLoader, call AsyncLoader.addEventListener(<event-name>, <handler>). Access event parameters in your handler by calling ev.detail.<parameter>

eventparametersdescription
progress'percentage', 'loaded', 'total'Called every time a progress event is recorded while downloading files
download-completenoneCalled when all files have finished downloading
completenoneCalled after files have finished being injected and parsed by the browser

AsyncLoader functions

nameparametersdescription
loadLoad all scripts and inject them. Called immediately by default if load option is set to true

Setup

You will want to edit the HtmlWebpackPlugin options to exclude the chunks you specify in the AsyncLoader plugin. It's also required that you do not sort the chunks by specifying chunksSortMode: 'none'. You'll also want to inject the scripts in the head so they will be available to you in the body using inject: 'head'.

(webpack.config.js)

const AsyncLoader = require('@tygr/async-loader-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  ...
  plugins: [
    new AsyncLoader({
      chunks: ['app', 'chunk-vendors']
    }),
    new HtmlWebpackPlugin({
      ...,
      excludeChunks: ['app', 'chunk-vendors'],
      chunksSortMode: 'none',
      inject: 'head'
    })
  ]
}

vue CLI setup

When using the vue CLI to create your app, you'll need to edit your webpack config in a root level file named vue.config.js. This configuration also modifies the 'preload' plugin that comes built in automatically. By specifying some scripts to preload, it may slow down your initial render, so it's been modified to specify prefetch instead.

const AsyncLoader = require('@tygr/async-loader-webpack-plugin')

module.exports = {
  configureWebpack: {
    plugins: [ new AsyncLoader({
      chunks: ['app', 'chunk-vendors'],
    }) ]
  },
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => [{
        ...args[0],
        excludeChunks: ['app', 'chunk-vendors'],
        inject: 'head',
        chunksSortMode: 'none'
      }])
    config
      .plugin('preload')
      .tap(args => [{
        ...args[0],
        rel: 'prefetch',
      }])
  }
}

index.html Setup

Inside the index.html template file is where you should hook into the AsyncLoader plugin. It should look something like this:

<html>
  ...
  <body>
    <div id="app"></div>
    <!-- Use the AsyncLoader object (already injected as a global object by the plugin) here -->
    <script>
      AsyncLoader.addEventListener('progress', ev => {
        console.log(ev.detail.percentage)
      })

      AsyncLoader.addEventListener('complete', () => {
        console.log('Finished loading and injecting scripts')
      })
    </script>
  </body>
</html>

Vue tips

If you would like to add an enter website button, modify your src/main.js so that it does not mount the app immediately but instead exposes it to the window object:

// @ts-ignore
window['app'] = new Vue({
  router,
  store,
  render: (h) => h(App),
});

Then, in your public/index.html, wait for a complete event to show the enter button and set the onclick handler to $mount the app:

<html>
  ...
  <body>
    <button id="enter-btn" style="display: none;" onclick="enterApp()">Enter</button>
    <div id="app"></div>
    <script>
      AsyncLoader.addEventListener('complete', () => {
        document.getElementById('enter-btn').style.display = 'block'
      })

      const enterApp = () => {
        document.getElementById('enter-btn').style.display = 'none'
        app.$mount('#app')
      }
    </script>
  </body>
</html>