Skip to content

Latest commit

 

History

History
590 lines (457 loc) · 26.8 KB

examples.md

File metadata and controls

590 lines (457 loc) · 26.8 KB

Examples

⚠️ beware, the following examples are sticky to version 0.2.21. For your use, prefer the latest version

Since most browsers do not allow you to access local filesystem, you can start a small express server to run these examples.
Run the following commands to start a basic web server:

npm install express  # or yarn add express
node -e "require('express')().use(require('express').static(__dirname, {index:'index.html'})).listen(8181)"

A more complete API usage example

<!DOCTYPE html>
<html>
<body>
  <div id="app"></div>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script>

    const componentSource = /* <!-- */`
      <template>
        <span class="example">{{ msg }}</span>
      </template>
      <script>
        export default {
          data () {
            return {
              msg: 'world!'
            }
          }
        }
      </script>

      <style scoped>
        .example {
          color: red;
        }
      </style>
    `/* --> */;

    const options = {

      moduleCache: {
        vue: Vue,
      },

      async getFile(url) {

        if ( url === './myComponent.vue' )
          return Promise.resolve(componentSource);

        const res = await fetch(url);
        if ( !res.ok )
          throw Object.assign(new Error(url+' '+res.statusText), { res });
        return await res.text();
      },

      addStyle(textContent) {

        const style = Object.assign(document.createElement('style'), { textContent });
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
      },

      log(type, ...args) {

        console[type](...args);
      },

      compiledCache: {
        set(key, str) {

          // naive storage space management
          for (;;) {

            try {

              // doc: https://developer.mozilla.org/en-US/docs/Web/API/Storage
              window.localStorage.setItem(key, str);
              break;
            } catch(ex) {

              // handle: Uncaught DOMException: Failed to execute 'setItem' on 'Storage': Setting the value of 'XXX' exceeded the quota

              window.localStorage.removeItem(window.localStorage.key(0));
            }
          }
        },
        get(key) {

          return window.localStorage.getItem(key);
        },
      },

      additionalModuleHandlers: {
        '.json': (source, path, options) => JSON.parse(source),
      }
    }

    const { loadModule } = window['vue3-sfc-loader'];
    const myComponent = loadModule('./myComponent.vue', options);

    const app = Vue.createApp({
      components: {
        'my-component': Vue.defineAsyncComponent( () => myComponent ),
      },
      template: 'Hello <my-component></my-component>'
    });

    app.mount('#app');

  </script>

</body>
</html>

open in JSBin

🔝

Load a Vue component from a string

<!DOCTYPE html>
<html>
<body>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script>

    /* <!-- */
    const sfcContent = `
      <template>
        Hello World !
      </template>
    `;
    /* --> */

    const options = {
      moduleCache: {
        vue: Vue,
      },
      getFile(url) {

        if ( url === './myComponent.vue' )
          return Promise.resolve(sfcContent);
      },
      addStyle() {},
    }

    const { loadModule } = window['vue3-sfc-loader'];
    Vue.createApp(Vue.defineAsyncComponent(() => loadModule('./myComponent.vue', options))).mount(document.body);

  </script>
</body>
</html>

open in JSBin

🔝

Using another template language (pug)

<!DOCTYPE html>
<html>
<body>
  <div id="app"></div>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://pugjs.org/js/pug.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script>

    /* <!-- */
    const sfcContent = `
<template lang="pug">
ul
  each val in ['p', 'u', 'g']
    li= val
</template>
`;
    /* --> */

    const options = {

      moduleCache: {
        vue: Vue,
        pug: require('pug'),
      },

      getFile(url) {

        if ( url === './myPugComponent.vue' )
          return Promise.resolve(sfcContent);
      },

      addStyle: () => {},
    }

    const { loadModule } = window["vue3-sfc-loader"];
    Vue.createApp(Vue.defineAsyncComponent(() => loadModule('./myPugComponent.vue', options))).mount('#app');

  </script>
</body>
</html>

open in JSBin

🔝

Using another style language (stylus)

<!DOCTYPE html>
<html>
<body>
  <div id="app"></div>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script src="//stylus-lang.com/try/stylus.min.js"></script>
  <script>

    /* <!-- */
    const vueContent = `
      <template>
        Hello <b>World</b> !
      </template>
      <style lang="stylus">
 b
  color red
      </style>
    `;
    /* --> */

    const options = {
      moduleCache: {
        vue: Vue,
        stylus: source => Object.assign(stylus(source), { deps: () => [] }), // note: deps() does not work in this bundle of stylus (see https://stylus-lang.com/docs/js.html#deps)
      },
      getFile: () => vueContent,
      addStyle(styleStr) {
        const style = document.createElement('style');
        style.textContent = styleStr;
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
      },
    }

    Vue.createApp(Vue.defineAsyncComponent(() => window['vue3-sfc-loader'].loadModule('file.vue', options))).mount(document.body);

  </script>
</body>
</html>

open in JSBin

🔝

SFC style CSS variable injection (new edition)

see at vuejs/rfcs

<!DOCTYPE html>
<html>
<body>
  <div id="app"></div>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script>
    /* <!-- */
    const sfcContent = `
      <template>
        Hello <span class="example">{{ msg }}</span>
      </template>
      <script>
        export default {
          data () {
            return {
              msg: 'world!',
              color: 'blue',
            }
          }
        }
      </script>
      <style scoped>
        .example {
          color: v-bind('color')
        }
      </style>
    `;
    /* --> */

    const options = {
      moduleCache: {
        vue: Vue,
      },
      getFile(url) {

        if ( url === './myComponent.vue' )
          return Promise.resolve(sfcContent);
      },
      addStyle(textContent) {

        const style = Object.assign(document.createElement('style'), { textContent });
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
      },
    }

    const { loadModule } = window["vue3-sfc-loader"];
    Vue.createApp(Vue.defineAsyncComponent(() => loadModule('./myComponent.vue', options))).mount('#app');
  </script>
</body>
</html>

open in JSBin

🔝

Minimalist example (just for the fun)

<!DOCTYPE html>
<html>
<body>
  <script src="https://unpkg.com/vue@next/dist/vue.runtime.global.prod.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script>

    /* <!-- */
    const vueContent = `
      <template> Hello World !</template>
    `;
    /* --> */

    const options = {
      moduleCache: { vue: Vue },
      getFile: () => vueContent,
      addStyle: () => {},
    }

    Vue.createApp(Vue.defineAsyncComponent(() => window['vue3-sfc-loader'].loadModule('file.vue', options))).mount(document.body);

  </script>
</body>
</html>

open in JSBin

🔝

Use options.loadModule hook

<!DOCTYPE html>
<html>
<body>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script>

    /* <!-- */
    const sfcContent = `
      <template>
        Hello World !
      </template>
    `;
    /* --> */

    const options = {
      moduleCache: { vue: Vue },
      async loadModule(path) {

        // (TBD)

      },
      getFile(url) {

        if ( url === './myComponent.vue' )
          return Promise.resolve(sfcContent);
      },
      addStyle() {},
    }

    const { loadModule } = window['vue3-sfc-loader'];
    Vue.createApp(Vue.defineAsyncComponent(() => loadModule('./myComponent.vue', options))).mount(document.body);

  </script>
</body>
</html>

open in JSBin

🔝

Dynamic component (:is Special Attribute)

In the following example we use a trick to preserve reactivity through the Vue.defineAsyncComponent() call (see the following discussion)

<!DOCTYPE html>
<html>
<body>
  <div id="app"></div>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue3-sfc-loader.js"></script>
  <script>

    const options = {

      moduleCache: {
        vue: Vue,
      },

      getFile(url) {

        switch ( url ) {
          case 'a.vue':
            return `
              <template>
                <i> a </i>
              </template>
            `;
          case 'b.vue':
            return `
              <template>
                <b> b </b>
              </template>
            `;
        }

        return fetch(url).then(res => res.ok ? res.text() : Promise.reject( new Error(res.statusText) ));
      },

      addStyle() {},
    }

    const { loadModule } = window["vue3-sfc-loader"];


    const app = Vue.createApp({
      template: `
        <button
          @click="currentComponent = currentComponent === 'a' ? 'b' : 'a'"
        >toggle</button>
        dynamic component:
        <component :is="comp"></component>
      `,
      computed: {
        comp() {

          const currentComponent = this.currentComponent; // the trick is here
          return Vue.defineAsyncComponent( () => loadModule(currentComponent + '.vue', options) )

          // or, equivalently, use Function.prototype.bind function like this:
          // return Vue.defineAsyncComponent( (url => loadModule(url, options)).bind(null, this.currentComponent + '.vue') )
        }
      },
      data() {
        return {
          currentComponent: 'a',
        }
      }
    });

    app.mount('#app');

  </script>
</body>
</html>

open in JSBin

🔝