1.0.0-beta.9 • Published 2 years ago

@mega-apps/vue-addon-loader v1.0.0-beta.9

Weekly downloads
-
License
-
Repository
-
Last release
2 years ago

@mega-apps/vue-addon-loader

Vue 扩展加载器,适用于动态加载 Vue 组件。

Vue Addon Loader is a small library that allows you to load Vue components from a module dynamically. It's only dependent on the vue runtime. no need to install any other dependencies. No node.js or webpack dependencies.

1. 主要特性

  • 完全支持 Vue2 的组件
  • 可支持IE11版本
  • 仅需要Vue运行时,无需其他依赖
  • 提供 esm and umd
  • 支持 JSX 语法
  • 支持 嵌入ES6模块写法
  • 支持自定义 CSS、HTML、脚本支持
  • 支持 SFC 自定义模块
  • 支持编译错误定位
  • 支持远程组件,来源于网络、数据库、本地文件... 等

安装

# yarn 安装
yarn add @mega-apps/vue-addon-loader

# 或 pnpm 安装
pnpm add @mega-apps/vue-addon-loader

简单示例

2. 支持 JSX 语法

<script>
  export default {
    data() {
      return {
        type: 'jsx 代码',
        msg: 'Hello Vue!'
      }
    },
    render(h) {
      return (
        <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
          <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
          {/* 注释 */ this.msg }
        </div>
      )
    }
  }
</script>

3. 支持的ECMAScript 特性

3.1. 支持计算属性: computed-properties

const foo = 'foo', bar = 'bar';
var obj = {
  ["x" + foo]: "heh",
  ["y" + bar]: "noo",
  foo: "foo",
  bar: "bar",
};

3.2. 支持属性名表达式: property-name-expression

const foo = 'foo', bar = 'bar';
var obj = {
  [foo]: "heh",
  [bar]: "noo",
  foo: "foo",
  bar: "bar",
};

3.3. 支持属性简写: property-shorthand

const foo = 'foo', bar = 'bar';
var obj = {
  foo,
  bar,
  foo: "foo",
  bar: "bar",
};

3.4. 支持属性绑定: property-binding

const foo = 'foo', bar = 'bar';
var obj = {
  foo: "heh",
  bar: "noo",
  foo: "foo",
  bar: "bar",
};

3.5. 支持BigInt 大整数

const expected = 4n / 2n;

3.6. 支持模板字符串: template-string

const foo = 'foo', bar = 'bar';
const info = `${foo} ${bar}`;

3.7. 支持箭头函数: arrow-function

const foo = 'foo', bar = 'bar';
const info = (foo, bar) => {
  return foo + bar;
};

3.8. 支持类: class

class Foo {
  constructor(foo, bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

3.9. 支持类继承: class-extends

class Foo extends Bar {
  constructor(foo, bar) {
    super(foo, bar);
    this.foo = foo;
    this.bar = bar;
  }
}

3.10. 支持类静态属性: class-static-properties

class Foo {
  static foo = 'foo';
  static bar = 'bar';
}

3.11. 支持类静态方法: class-static-methods

class Foo {
  static foo() {
    return 'foo';
  }
  static bar() {
    return 'bar';
  }
}

3.12. 支持类实例属性: class-instance-properties

class Foo {
  foo = 'foo';
  bar = 'bar';
}

3.13. 支持类实例方法: class-instance-methods

class Foo {
  foo() {
    return 'foo';
  }
  bar() {
    return 'bar';
  }
}

3.14. 支持类静态属性和方法: class-static-properties-and-methods

class Foo {
  static foo = 'foo';
  static bar() {
    return 'bar';
  }
}

3.15. 支持类实例属性和方法: class-instance-properties-and-methods

class Foo {
  foo = 'foo';
  bar() {
    return 'bar';
  }
}

3.16. 支持类静态属性和实例属性: class-static-properties-and-instance-properties

class Foo {
  static foo = 'foo';
  foo = 'foo';
}

3.17. 支持类静态属性和实例方法: class-static-properties-and-instance-methods

class Foo {
  static foo = 'foo';
  foo() {
    return 'foo';
  }
}

3.18. 支持异步生成器: async-generator

async function* foo() {
  yield 1;
  yield 2;
  yield 3;
}

3.19. 支持类型断言: type-assertion

const foo = 'foo';
const bar = 'bar';
const info = foo as string;

3.20. 支持类型保护: type-guard

if (foo instanceof Foo) {
  foo.foo();
}

3.21. 支持类型别名: type-alias

type Foo = {
  foo: string;
  bar: string;
};

3.22. 支持类私有属性和方法: private-property-and-methods

class Foo {
  #foo = 'foo';
  #bar() {
    return 'bar';
  }
}

3.23. 支持类属性修饰符: property-modifiers

class Foo {
  #foo = 'foo';
  #bar() {
    return 'bar';
  }
  get foo() {
    return this.#foo;
  }
  set foo(value) {
    this.#foo = value;
  }
  get bar() {
    return this.#bar();
  }
}

3.24. 支持类静态块: class-static-block

class Foo {
  static foo = 'foo';
  static bar = 'bar';
  static #baz = 'baz';
  static #qux() {
    return 'qux';
  }
}

3.25. 支持类实例块: class-instance-block

class Foo {
  foo = 'foo';
  bar = 'bar';
  #baz = 'baz';
  #qux() {
    return 'qux';
  }
}

3.26. 支持动态导入: dynamic-import

import('./foo.js').then(foo => {
  foo.foo();
});

3.27. 支持模块化导入: module

import { foo } from './foo.js';

3.28. 支持模块化默认导入: module-with-default-export

import foo from './foo.js';

3.29. 支持导出命名空间: export-namespace

export * as foo from './foo.js';

3.30. 支持导出类: export-class

export class Foo {
  constructor(foo, bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

3.31. 支持函数sent特性: function-sent

function foo(x = (y = z)) {
  return x;
}

function *adder(total=0) {
  let increment=1;
  do {
      switch (request = function.sent){
          case undefined: break;
          case "done": return total;
          default: increment = Number(request);
      }
      yield total += increment;
  } while (true)
}

3.32. 支持逻辑赋值: logical-assignment-operators

let b = 0;
let a = b ||= 1;

3.33. 支持模块字符串: module-strings

import { "😄" as smile } from "emojis";

3.34. 支持空选链: nullish-coalescing-operator

let foo = bar ?? 'foo';

3.35. 支持空赋值: nullish-assignment-operator

let foo = bar ??= 'foo';

3.36. 支持对象rest spread: object-rest-spread

let { foo, ...bar } = { foo: 'foo', bar: 'bar' };

3.37. 支持数组rest spread: array-rest-spread

let [foo, ...bar] = ['foo', 'bar'];

3.38. 支持可选捕获绑定: optional-catch-binding

try{
  console.log(1);
}finally{
  console.log('finally');
}

3.39. 支持可选参数: optional-parameters

function foo(x = 1) {
  return x;
}

3.40. 支持可选参数和可选属性: optional-chaining

const foo = {
  bar: {
    baz: 'baz'
  }
};

const baz = foo?.bar?.baz;

3.41. 支持私有In: private-in

class C {
  #brand;

  #method() {}

  get #getter() {}

  static isC(obj) {
    return #brand in obj && #method in obj && #getter in obj;
  }
}

3.42. 支持顶级 await: top-level-await

async function foo() {
  await bar();
}

// 依赖回滚
let jQuery;
try {
  jQuery = await import('https://cdn-a.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.com/jQuery');
}

4. 支持ECMAScript 提案

4.1. 支持异步do表达式: async-do-expressions

async
do {
  await fetch('http://www.bing.com').json()
}

4.2. 支持decimal

let budget = 1_000_000_000_000;
console.log(budget === 10 ** 12); // true

let nibbles = 0b1010_0001_1000_0101;
console.log(!!(nibbles & (1 << 7))); // true

// Messages are sent as 24 bit values, but should be
// treated as 3 distinct bytes:
let message = 0xa0_b0_c0;

// What's the value of the upper most byte? It's A0, or 160.
// We can confirm that:
let a = (message >> 16) & 0xff;
console.log(a.toString(16), a); // a0, 160

// What's the value of the middle byte? It's B0, or 176.
// Let's just make sure...
let b = (message >> 8) & 0xff;
console.log(b.toString(16), b); // b0, 176

// What's the value of the lower most byte? It's C0, or 192.
// Again, let's prove that:
let c = message & 0xff;
console.log(c.toString(16), b); // c0, 192

4.3. 支持装饰器:decorators

function logged(value, { kind, name }) {
  if (kind === "method") {
    return function (...args) {
      console.log(\`starting $\{ name \} with arguments $\{ args.join(", ") \} \`);
      const ret = value.call(this, ...args);
      console.log(\`ending $\{ name \}\`);
      return ret;
    };
  }
}

class C {
  @logged
  m(arg) {}
}

4.4. 支持do表达式: do-expressions

let x = do {
  let y = 1;
  y + 1;
};

4.5. 支持导出默认值: export-default-from

export default from './foo.js';

4.6. 支持导出from: export-from

export v from 'vue';
export default {
  data() {
    return {
      type: 'exportDefaultFrom export from 语法',
      msg: 'https://github.com/tc39/ecmascript-export-default-from',
      isProposal: true
    }
  },
  ${commonCode}
}

4.7. 支持函数绑定: function-bind

            const box = {
              weight: 2,
              getWeight() {
                return this.weight;
              },
            };

            const { getWeight } = box;

            console.log(box.getWeight()); // prints '2'

            const bigBox = { weight: 10 };
            console.log(bigBox::getWeight()); // prints '10'

            // Can be chained:
            function add(val) {
              return this + val;
            }

            console.log(bigBox::getWeight()::add(5)); // prints '15'

4.8. 支持导入断言: import-assert

import Vue from "vue" assert { type: "js" };

4.9. 支持局部模块化: module-declaration

 let m = module { export let y = 1; };

4.10. 支持 局部应用: partial-application

function add(x, y) { return x + y; }

const addOne = add(1, ?); // apply from the left
addOne(2); // 3

const addTen = add(?, 10); // apply from the right
addTen(2); // 12

4.11. 支持流水线操作符: pipeline-operator

let result = "hello"
              |> console.log;

4.12. 支持 Record 和 元组类型

let x = #{x: 1, y: 2};
let y = #[1, 2];

4.13. 支持抛出异常表达式: throw-expressions

class Product {
  get id() { return this._id; }
  set id(value) { this._id = value || throw new Error("Invalid value"); }
}

5. 代码示例

// TODO: 要支持TailwindCss 的at rule

const commonCode = `
  render(h) {
    return (
      <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
        <h1 class="text-xl font-bold"> { !!this?.isProposal ? 'ECMAScript 提案' : 'ECMAScript 特性' } 【{this.type}】</h1>

        <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
      </div>
    )
  }
`

export default {
  base: {
    "demo": `
      <template>
        <span class="bg-yellow-200 p-4">Hello from Vue {{ require('myCustomModel').vueVersion }} !</span>
      </template>
    `,
  },
  // 验证是否支持tailwindcss的at rule
  // 试验场:https://play.tailwindcss.com/
  // TODO:嵌入tailwind 的编译器,用来解析at rule,参照 https://play.tailwindcss.com/ 网站源码
  tailwindcss: {
    "at-rules": {
      getContentData: () => {
        return `
          <template>
            <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
              <h1 class="text-xl font-bold">验证TailwindCss at rule 是否可用</h1>
              <button class="w-32 rounded-md bg-gray-300 customBtn">Button</button>
            </div>
          </template>
          <style scoped>
            .customBtn {
              // 指令不可用
              @apply bg-blue-700 text-white;
            }
          </style>
        `
      },
      type: '.vue'
    }
  },
  // 语法特性
  syntax: {
    "jsx": {
      getContentData: () => {
        return `
          <script>
            export default {
              data() {
                return {
                  type: 'jsx 代码',
                  msg: 'Hello Vue!'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                    {/* 注释 */ this.msg }
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "__filename": {
      getContentData: () => {
        return `
          <script>
            export default {
              data() {
                return {
                  type: '__filename 注入',
                  msg: __filename
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                    {/* 注释 */ this.msg }
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "__dirname": {
      getContentData: () => {
        return `
          <script>
            export default {
              data() {
                return {
                  type: '__dirname 注入',
                  msg: __dirname
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                    {/* 注释 */ this.msg }
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    //#region ECMASript 特性
    "computed-properties": {
      getContentData: () => {
        return `
          <script>
            const foo = 'foo', bar = 'bar';
            var obj = {
              ["x" + foo]: "heh",
              ["y" + bar]: "noo",
              foo: "foo",
              bar: "bar",
            };

            export default {
              data() {
                return {
                  type: 'computed-properties 计算属性',
                  msg: 'https://babeljs.io/docs/en/babel-plugin-transform-computed-properties'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                   
                    <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "BigInt": {
      getContentData: () => {
        return `
          <script>
            const expected = 4n / 2n;

            export default {
              data() {
                return {
                  type: 'BigInt 大整数',
                  msg: 'https://github.com/tc39/proposal-bigint'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                   
                    <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "asyncGenerators": {
      getContentData: () => {
        return `
          <script>

            const fn1 = async function* () {};

            export default {
              data() {
                return {
                  type: 'asyncGenerators 异步迭代器,生成器',
                  msg: 'https://github.com/tc39/proposal-async-iteration'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                   
                    <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classProperties": {
      getContentData: () => {
        return `
          <script>

            class A { b = 1; }

            export default {
              data() {
                return {
                  type: 'classProperties 类字段',
                  msg: 'https://github.com/tc39/proposal-class-public-fields'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classPrivateProperties": {
      getContentData: () => {
        return `
          <script>

            class A { #b = 1; }

            export default {
              data() {
                return {
                  type: 'classPrivateProperties 类私有字段',
                  msg: 'https://github.com/tc39/proposal-private-fields'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classPrivateMethods": {
      getContentData: () => {
        return `
          <script>

            class A { #c() {} }

            export default {
              data() {
                return {
                  type: 'classPrivateMethods 类私有方法',
                  msg: 'https://github.com/tc39/proposal-private-methods'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classStaticBlock": {
      getContentData: () => {
        return `
          <script>

            class A { static {} }

            export default {
              data() {
                return {
                  type: 'classStaticBlock 类静态块',
                  msg: 'https://github.com/tc39/proposal-class-static-block'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "dynamicImport": {
      getContentData: () => {
        return `
          <script>

            import('vue').then(() => {});

            export default {
              data() {
                return {
                  type: 'dynamicImport 动态导入',
                  msg: 'https://github.com/tc39/proposal-dynamic-import'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "exportNamespaceFrom": {
      getContentData: () => {
        return `
          <script>

            export * as ns from "vue";

            export default {
              data() {
                return {
                  type: 'exportNamespaceFrom 动态导入',
                  msg: 'https://github.com/leebyron/ecmascript-export-ns-from'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "functionSent": {
      getContentData: () => {
        return `
          <script>

            function *adder(total=0) {
              let increment=1;
              do {
                  switch (request = function.sent){
                      case undefined: break;
                      case "done": return total;
                      default: increment = Number(request);
                  }
                  yield total += increment;
              } while (true)
            }

            export default {
              data() {
                return {
                  type: 'functionSent 动态导入',
                  msg: 'https://github.com/tc39/proposal-function.sent'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "logicalAssignment": {
      getContentData: () => {
        return `
          <script>

            let b = 0;
            let a = b ||= 1;

            export default {
              data() {
                return {
                  type: 'logicalAssignment 逻辑赋值',
                  msg: 'https://github.com/tc39/proposal-logical-assignment'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "moduleStringNames": {
      getContentData: () => {
        return `
          <script>

            import { "😄" as smile } from "emojis";

            export default {
              data() {
                return {
                  type: 'moduleStringNames 模块字符串命名',
                  msg: 'https://github.com/tc39/ecma262/pull/2154'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "nullishCoalescingOperator": {
      getContentData: () => {
        return `
          <script>
            let a, b = 1;
            let c = a ?? b;

            export default {
              data() {
                return {
                  type: 'nullishCoalescingOperator 空值合并运算',
                  msg: 'https://github.com/babel/proposals/issues/14'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "objectRestSpread": {
      getContentData: () => {
        return `
          <script>
            const { a, ...rest } = { a: 1, b: 2, c: 3 };
            export default {
              data() {
                return {
                  type: 'objectRestSpread 解构',
                  msg: 'https://github.com/tc39/proposal-object-rest-spread'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "optionalCatchBinding": {
      getContentData: () => {
        return `
          <script>
            // input
            try{
              console.log(1);
            }finally{
              console.log('finally');
            }

            export default {
              data() {
                return {
                  type: 'optionalCatchBinding 可选捕获绑定',
                  msg: 'https://github.com/babel/proposals/issues/7'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "optionalChaining": {
      getContentData: () => {
        return `
          <script>
            const obj = { name: 'Lee' };
            const name = obj?.name;

            export default {
              data() {
                return {
                  type: 'optionalChaining 可选链',
                  msg: 'https://github.com/tc39/proposal-optional-chaining'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "privateIn": {
      getContentData: () => {
        return `
          <script>
            class C {
              #brand;

              #method() {}

              get #getter() {}

              static isC(obj) {
                return #brand in obj && #method in obj && #getter in obj;
              }
            }

            export default {
              data() {
                return {
                  type: 'privateIn 私有字段In',
                  msg: 'https://github.com/tc39/proposal-private-fields-in-in'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "topLevelAwait": {
      getContentData: () => {
        return `
          <script>
            // 依赖回滚
            let jQuery;
            try {
              jQuery = await import('https://cdn-a.com/jQuery');
            } catch {
              jQuery = await import('https://cdn-b.com/jQuery');
            }

            export default {
              data() {
                return {
                  type: 'topLevelAwait 顶层await,',
                  msg: 'https://github.com/tc39/proposal-top-level-await/'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    //#endregion

    //#region ECMAScript 提案
    "asyncDoExpressions": {
      getContentData: () => {
        return `
          <script>
            async do { await fetch('http://www.bing.com').json() }

            export default {
              data() {
                return {
                  type: 'asyncDoExpressions 顶层await,',
                  msg: 'https://github.com/tc39/proposal-async-do-expressions',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "decimal": {
      getContentData: () => {
        return `
          <script>
            let budget = 1_000_000_000_000;
            console.log(budget === 10 ** 12); // true

            let nibbles = 0b1010_0001_1000_0101;
            console.log(!!(nibbles & (1 << 7))); // true

            // Messages are sent as 24 bit values, but should be
            // treated as 3 distinct bytes:
            let message = 0xa0_b0_c0;

            // What's the value of the upper most byte? It's A0, or 160.
            // We can confirm that:
            let a = (message >> 16) & 0xff;
            console.log(a.toString(16), a); // a0, 160

            // What's the value of the middle byte? It's B0, or 176.
            // Let's just make sure...
            let b = (message >> 8) & 0xff;
            console.log(b.toString(16), b); // b0, 176

            // What's the value of the lower most byte? It's C0, or 192.
            // Again, let's prove that:
            let c = message & 0xff;
            console.log(c.toString(16), b); // c0, 192

            export default {
              data() {
                return {
                  type: 'decimal 提案支持',
                  msg: 'https://github.com/tc39/proposal-decimal',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "decorators": {
      getContentData: () => {
        return `
          <script>
            function logged(value, { kind, name }) {
              if (kind === "method") {
                return function (...args) {
                  console.log(\`starting $\{ name \} with arguments $\{ args.join(", ") \} \`);
                  const ret = value.call(this, ...args);
                  console.log(\`ending $\{ name \}\`);
                  return ret;
                };
              }
            }

            class C {
              @logged
              m(arg) {}
            }

            export default {
              data() {
                return {
                  type: 'decorators 装饰器',
                  msg: 'https://github.com/tc39/proposal-decorators',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "doExpressions": {
      getContentData: () => {
        return `
          <script>
            var a = do { if (true) { 'hi'; } };
            export default {
              data() {
                return {
                  type: 'doExpressions do表达式',
                  msg: 'https://github.com/tc39/proposal-do-expressions',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "exportDefaultFrom": {
      getContentData: () => {
        return `
          <script>
            export v from 'vue';
            export default {
              data() {
                return {
                  type: 'exportDefaultFrom export from 语法',
                  msg: 'https://github.com/tc39/ecmascript-export-default-from',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "functionBind": {
      getContentData: () => {
        return `
          <script>
            const box = {
              weight: 2,
              getWeight() {
                return this.weight;
              },
            };

            const { getWeight } = box;

            console.log(box.getWeight()); // prints '2'

            const bigBox = { weight: 10 };
            console.log(bigBox::getWeight()); // prints '10'

            // Can be chained:
            function add(val) {
              return this + val;
            }

            console.log(bigBox::getWeight()::add(5)); // prints '15'


            export default {
              data() {
                return {
                  type: 'functionBind 函数绑定语法',
                  msg: 'https://github.com/zenparsing/es-function-bind',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "importAssertions": {
      getContentData: () => {
        return `
          <script>
            import Vue from "vue" assert { type: "js" };

            export default {
              data() {
                return {
                  type: 'importAssertions 导入断言',
                  msg: 'https://github.com/tc39/proposal-import-assertions',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "moduleBlocks": {
      getContentData: () => {
        return `
          <script>
            let m = module { export let y = 1; };

            export default {
              data() {
                return {
                  type: 'moduleBlocks 局部模块化',
                  msg: 'https://github.com/tc39/proposal-js-module-blocks',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "partialApplication": {
      getContentData: () => {
        return `
          <script>
            function add(x, y) { return x + y; }

            const addOne = add(1, ?); // apply from the left
            addOne(2); // 3

            const addTen = add(?, 10); // apply from the right
            addTen(2); // 12

            export default {
              data() {
                return {
                  type: 'partialApplication 局部应用',
                  msg: 'https://github.com/babel/proposals/issues/32',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "pipelineOperator": {
      getContentData: () => {
        return `
          <script>
            let result = "hello"
              |> console.log;


            export default {
              data() {
                return {
                  type: 'pipelineOperator 流水线操作符',
                  msg: 'https://github.com/babel/proposals/issues/29',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "recordAndTuple": {
      getContentData: () => {
        return `
          <script>
            let x = #{x: 1, y: 2};
            let y = #[1, 2];

            export default {
              data() {
                return {
                  type: 'recordAndTuple 记录和元组',
                  msg: 'https://github.com/tc39/proposal-record-tuple',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "throwExpressions": {
      getContentData: () => {
        return `
          <script>
            class Product {
              get id() { return this._id; }
              set id(value) { this._id = value || throw new Error("Invalid value"); }
            }

            export default {
              data() {
                return {
                  type: 'throwExpressions 抛出表达式',
                  msg: 'https://github.com/babel/proposals/issues/23',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    //#endregion
  },
  // 复杂的组件
  compex: {
    "demo": {
      getContentData: () => {
        const text = `
        <template>
          <div class="flex flex-col items-center justify-center border border-blue-600 p-4 m-2">
            <!--父组件局部指定传值-->
            <section class="flex flex-col items-center justify-center py-2">
              <h1 class="text-xl font-bold">远程组件 - 源码级别</h1>
              <span>{{ !!$t ? $t('hello') : 'Hello' }} from Vue {{ require('myCustomModel').vueVersion }} !</span>
              <span>{{ __filename }}</span>
            </section>

            <!--附加远程组件-->
            <section class="flex flex-col items-center justify-center py-2">
              <calendar-range :selection="selection" :events="calendarEvents"/>
              <footer class="flex space-x-3 min-w-full">
                  <button class="w-32 flex items-center justify-center rounded-md bg-blue-700 text-white" @click="add">Add</button>
                  <button class="w-32 flex items-center justify-center rounded-md bg-gray-300 border border-gray-300" @click="remove">Remove</button>
                  <button class="myBtn w-32 flex items-center justify-center rounded-md border  border-gray-300" @click="fetch">Fetch</button>
                  <button class="myBtn w-32 flex items-center justify-center rounded-md bg-pink-500 border  border-gray-300" @click="log">Log</button>
              </footer>
            </section>

            <!-- 验证是否可以使用全局组件 -->
            <section class="flex flex-col items-center justify-center py-2">
              <header class="flex flex-col items-center justify-center">
                <span class="text-xl font-bold"> 验证是否可以使用全局组件</span>
              </header>
              <body class="flex flex-col items-center justify-center">
                <lazy-component-text :data="'Lazy loading the global compoent'" />
                <component-text :data="'Loading the global compoent'" class="bg-gray-400 rounded"/>
              </body>
            </section>

            <!-- 验证是否可以使用父组件传递过来的局部组件 -->
           <section class="flex flex-col items-center justify-center py-2">
              <header class="flex flex-col items-center justify-center">
                <span class="text-xl font-bold"> 验证是否可以使用父组件传递过来的局部组件 </span>
              </header>
              <body class="flex flex-col items-center justify-center">
                <CustomComponent name="909090L"/>
              </body>
            </section>

          </div>
        </template>
        <script>
          // 验证:加载互联网远端依赖组件
          import calendarRange from 'https://raw.githubusercontent.com/FranckFreiburger/vue-calendar-picker/v1.2.1/src/calendarRange.vue'
          import CustomComponent from 'customComponent';

          export default {
            // components: true,
            components: {
              calendarRange,
              CustomComponent,
            },
            data() {
              return {
                selection: { start: Date.now(), end: Date.now() },
                calendarEvents: [],
                bgcolor: 'red'
              }
            },
            methods: {
              add: function() {
                this.calendarEvents.push({
                  color: '#'+Math.floor(Math.random()*16777215).toString(16),
                  start: this.selection.start,
                  end: this.selection.end
                });
              },
              remove() {
                this.calendarEvents.pop();
              },
              fetch: async () => {
                const url = "https://raw.githubusercontent.com/FranckFreiburger/vue-calendar-picker/v1.2.1/src/calendarRange.vue";
                const res = await fetch(url);
                // 无法解析\`\`
                console.log(res);
                alert('fetch 请求,状态码: ' + res.status);
              },
              log() {
                console.log('vm=', this);
              }
            }
          }
        </script>
        <style scoped>

        .myBtn {
          // 验证:关于css指令的处理还不支持
          @apply bg-blue-700;
        }
        </style>
        <i18n>
          {
            "en": {
              "hello": "hello world!"
            },
            "ja": {
              "hello": "こんにちは、世界!"
            },
            "zh": {
              "hello": "你好,世界!"
            }
          }
        </i18n>
        `;

        return text;
      },
      type: '.vue'
    }

  },
}
<template>
  <div class="flex flex-col justify-center items-center">
    <div class="flex flex-col py-4 px-4 min-w-min">
      <h3 v-if="remoteComponentList.length < 1">Loading remote vue component file contents ...</h3>
      <span class="bg-yellow-600 text-center border border-dotted border-yellow-300 p-4 m-2">待验证支持的语法仍有: {{ remoteComponentList.filter(({component}) => !component).length }} 个</span>
      <div class="flex flex-col justify-center items-center text-center border border-dotted border-yellow-300 p-4 m-2" v-for="({component, description }, index) of remoteComponentList" :key="index">
        <span :class=" component ? 'bg-green-100': 'bg-red-700 text-yellow-200' ">{{ description }}</span>
        <component
          v-if="component"
          :is="component"
        ></component>
      </div>
    </div>
    <div class="flex justify-center items-center text-center py-1 m-10px h-1/2">
      <div class="flex">
        <div class="flex-none w-48 relative">
          <img
            src="https://www.tailwindcss.cn/_next/static/media/kids-jumper.47a06f045002a3e6ba595351a36a46eb.jpg"
            alt
            class="absolute inset-0 w-full h-full object-cover"
          />
        </div>
        <form class="flex-auto p-6">
          <div class="flex flex-wrap">
            <h1 class="flex-auto text-xl font-semibold">Classic Utility Jacket</h1>
            <div class="text-xl font-semibold text-gray-500">$110.00</div>
            <div class="w-full flex-none text-sm font-medium text-gray-500 mt-2">In stock</div>
          </div>
          <div class="flex items-baseline mt-4 mb-6">
            <div class="space-x-2 flex">
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center bg-gray-100 rounded-lg"
                  name="size"
                  type="radio"
                  value="xs"
                  checked
                />
                XS
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="s"
                />
                S
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="m"
                />
                M
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="l"
                />
                L
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="xl"
                />
                XL
              </label>
            </div>
            <div class="ml-auto text-sm text-gray-500 underline">Size Guide</div>
          </div>
          <div class="flex space-x-3 mb-4 text-sm font-medium">
            <div class="flex-auto flex space-x-3">
              <button
                class="w-1/2 flex items-center justify-center rounded-md bg-black text-white"
                type="submit"
              >Buy now</button>
              <button
                class="w-1/2 flex items-center justify-center rounded-md border border-gray-300"
                type="button"
              >Add to bag</button>
            </div>
            <button
              class="flex-none flex items-center justify-center w-9 h-9 rounded-md text-gray-400 border border-gray-300"
              type="button"
              aria-label="like"
            >
              <svg width="20" height="20" fill="currentColor">
                <path
                  fill-rule="evenodd"
                  clip-rule="evenodd"
                  d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"
                />
              </svg>
            </button>
          </div>
          <p class="text-sm text-gray-500">Free shipping on all continental US orders.</p>
        </form>
      </div>

      <button
        class="bg-blue mt-4 hover:bg-blue-dark text-white font-bold py-2 px-4 rounded"
        @click="$router.push('/about')"
      >About</button>
    </div>
  </div>
</template>

<script lang="js">
import Vue from "vue";
import * as emojis from "emojis-list";
import _ from "lodash";
import dateFnsLocalZHCN from "date-fns/locale/zh_cn";
import extendVueSfcLoader from "./vue-loader";
import componentRemoteCfg from "./data-remote-components";

export default {
  data() {
    return {
      remoteComponentList: []
    }
  },
  mounted() {
    this.fetchData();
  },
  methods: {
    fetchData() {
      console.log('----->',extendVueSfcLoader);

      extendVueSfcLoader.loadModule(() => {
        const { deps } = extendVueSfcLoader;
        const { loadModule,vueVersion } = deps[Vue.version];

        const options = {
          moduleCache: {
            // 系统级别模块
            vue: Vue,
            emojis,

            // 自定义模块
            myCustomModel: {
              vueVersion: vueVersion,
            },

            // 自定义组件模块
            customComponent: {
              name: "CustomComponentDemo",
              render(h) {
                return h("div",{
                  class: "bg-red-500 text-white p-2 roundedb"
                },[
                  h("h1",{},[ `${this.name} - ${this.title}`] ),
                  h("p",{},this.message),
                  h(
                    'button',
                    {
                      class: 'w-32 flex items-center justify-center rounded-md bg-gray-300 border border-gray-300',
                      on: {
                        click: () => {
                          this.toggleMood();
                        }
                      }
                    },
                    this.mood ? 'On' : 'Off'
                  )
                ]);
              },
              props: {
                name: {
                  type: String,
                  default: '',
                },
              },
              data() {
                return {
                  mood: true,
                  title: 'Vue2 Title',
                  message: 'Vue2 Message',
                }
              },
              methods: {
                toggleMood() {
                  this.mood = !this.mood;
                  this.mood ? this.title = "Clicked" : this.title = "Not Clicked";
                }
              }
            },

            // 加载模块的几种方式
            'date-fns/locale/en/index.js': {}, // handle require('date-fns/locale/' + this.locale.toLowerCase() + '/index.js');
            'date-fns/locale/zh-tw/index.js': require('date-fns/locale/zh_tw/index.js'),
            'date-fns/locale/zh-cn/index.js': dateFnsLocalZHCN, // handle require('date-fns/locale/' + this.locale.toLowerCase() + '/index.js');
            'date-fns/locale/zh/index.js': dateFnsLocalZHCN, // handle require('date-fns/locale/' + this.locale.toLowerCase() + '/index.js');
          },
          pathResolve({ refPath,relPath }) {

            if (relPath === 'date-fns')
              return 'https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js';

            if (relPath === '.') // self
              return refPath;

            // relPath is a module name ?
            if (relPath[0] !== '.' && relPath[0] !== '/')
              return relPath;

            return String(new URL(relPath,refPath === undefined ? window.location : refPath));
          },
          getFile: async (url) => {
            const pathName = new URL(url).pathname;

            console.log('%c%s','color: #aa00ff',`pathName = ${pathName}`);
            if (pathName.startsWith('/get-remote/')) {
              const actions = [
                (e) => e.replace(/^\/get-remote\//, ''),
                pathName.endsWith('.vue') ? (e) => e.slice(0, -4) : null,
                (e) => e.split('/')
              ];

              const componentKeys = actions.reduce((acc, cur) => {
                if (cur) {
                  return cur(acc);
                }
                return acc;
              }, pathName);

              const component = _.get(componentRemoteCfg,componentKeys);
              if (component) {
                  console.log(`${pathName}\n`,component)
                  return Promise.resolve(component);
              }
            } else {
              return fetch(url).then(res => res.text());
            }

            return Promise.reject(`${pathName} not found`);
          },
          addStyle(textContent) {
            if (textContent?.length > 0) {
              // console.log('%c%s', 'color: #e50000', `addStyle(textContent): textContent`, textContent);
              const style = Object.assign(document.createElement('style'),{ textContent });
              const ref = document.head.getElementsByTagName('style')[0] || null;
              document.head.insertBefore(style,ref);
            }
          },
        }

        const { base, tailwindcss, syntax, compex } = componentRemoteCfg;

        for (const url of [
          ...Object.keys(base).map(e => `base/${e}`),
          ...Object.keys(tailwindcss).map(e => `tailwindcss/${e}`),
          ...Object.keys(syntax).map(e => `syntax/${e}`),
          ...Object.keys(compex).map(e => `compex/${e}`),
        ].map(e => `/get-remote/${e}`)) {
          console.log('%c%s','color: #733d00',`url => ${url}`)
          loadModule(url,options).then((component) => {
            console.log('%c%s','color: #ff0000',`已构造完成远程组件(${url}) =`,component);
            this.remoteComponentList.push({
              component,
              description: url,
            });
          }).catch(err => {
            console.error('%c%s','color: #aa00ff',`构造远程组件失败(${url}) =`,err);
            this.remoteComponentList.push({
              component: null,
              description: `${url} 错误: ${err}`,
            });
          });
        }
      })
    },
  },
};
</script>
1.0.0-beta.6

2 years ago

1.0.0-beta.7

2 years ago

1.0.0-beta.8

2 years ago

1.0.0-beta.9

2 years ago

1.0.0-beta.3

3 years ago

1.0.0-beta.4

3 years ago

1.0.0-beta.2

3 years ago

1.0.0-beta.1

3 years ago

1.0.0-beta.0

3 years ago