UNPKG

@mega-apps/vue-addon-loader

Version:

Vue addon loader, for SFCs and components. You can use it for vue component online.

1,951 lines (1,708 loc) 49.1 kB
# @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 自定义模块 - 支持编译错误定位 - 支持远程组件,来源于网络、数据库、本地文件... 等 ## 安装 ``` bash # yarn 安装 yarn add @mega-apps/vue-addon-loader # 或 pnpm 安装 pnpm add @mega-apps/vue-addon-loader ``` ## 简单示例 ``` ``` ## 2. 支持 JSX 语法 ``` vue <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` ``` js const foo = 'foo', bar = 'bar'; var obj = { ["x" + foo]: "heh", ["y" + bar]: "noo", foo: "foo", bar: "bar", }; ``` ### 3.2. 支持`属性名表达式: property-name-expression` ``` js const foo = 'foo', bar = 'bar'; var obj = { [foo]: "heh", [bar]: "noo", foo: "foo", bar: "bar", }; ``` ### 3.3. 支持`属性简写: property-shorthand` ``` js const foo = 'foo', bar = 'bar'; var obj = { foo, bar, foo: "foo", bar: "bar", }; ``` ### 3.4. 支持`属性绑定: property-binding` ``` js const foo = 'foo', bar = 'bar'; var obj = { foo: "heh", bar: "noo", foo: "foo", bar: "bar", }; ``` ### 3.5. 支持`BigInt 大整数` ``` js const expected = 4n / 2n; ``` ### 3.6. 支持`模板字符串: template-string` ``` js const foo = 'foo', bar = 'bar'; const info = `${foo} ${bar}`; ``` ### 3.7. 支持`箭头函数: arrow-function` ``` js const foo = 'foo', bar = 'bar'; const info = (foo, bar) => { return foo + bar; }; ``` ### 3.8. 支持`类: class` ``` js class Foo { constructor(foo, bar) { this.foo = foo; this.bar = bar; } } ``` ### 3.9. 支持`类继承: class-extends` ``` js class Foo extends Bar { constructor(foo, bar) { super(foo, bar); this.foo = foo; this.bar = bar; } } ``` ### 3.10. 支持`类静态属性: class-static-properties` ``` js class Foo { static foo = 'foo'; static bar = 'bar'; } ``` ### 3.11. 支持`类静态方法: class-static-methods` ``` js class Foo { static foo() { return 'foo'; } static bar() { return 'bar'; } } ``` ### 3.12. 支持`类实例属性: class-instance-properties` ``` js class Foo { foo = 'foo'; bar = 'bar'; } ``` ### 3.13. 支持`类实例方法: class-instance-methods` ``` js class Foo { foo() { return 'foo'; } bar() { return 'bar'; } } ``` ### 3.14. 支持`类静态属性和方法: class-static-properties-and-methods` ``` js class Foo { static foo = 'foo'; static bar() { return 'bar'; } } ``` ### 3.15. 支持`类实例属性和方法: class-instance-properties-and-methods` ``` js class Foo { foo = 'foo'; bar() { return 'bar'; } } ``` ### 3.16. 支持`类静态属性和实例属性: class-static-properties-and-instance-properties` ``` js class Foo { static foo = 'foo'; foo = 'foo'; } ``` ### 3.17. 支持`类静态属性和实例方法: class-static-properties-and-instance-methods` ``` js class Foo { static foo = 'foo'; foo() { return 'foo'; } } ``` ### 3.18. 支持`异步生成器: async-generator` ``` js async function* foo() { yield 1; yield 2; yield 3; } ``` ### 3.19. 支持`类型断言: type-assertion` ``` js const foo = 'foo'; const bar = 'bar'; const info = foo as string; ``` ### 3.20. 支持`类型保护: type-guard` ``` js if (foo instanceof Foo) { foo.foo(); } ``` ### 3.21. 支持`类型别名: type-alias` ``` js type Foo = { foo: string; bar: string; }; ``` ### 3.22. 支持`类私有属性和方法: private-property-and-methods` ``` js class Foo { #foo = 'foo'; #bar() { return 'bar'; } } ``` ### 3.23. 支持`类属性修饰符: property-modifiers` ``` js 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` ``` js class Foo { static foo = 'foo'; static bar = 'bar'; static #baz = 'baz'; static #qux() { return 'qux'; } } ``` ### 3.25. 支持`类实例块: class-instance-block` ``` js class Foo { foo = 'foo'; bar = 'bar'; #baz = 'baz'; #qux() { return 'qux'; } } ``` ### 3.26. 支持`动态导入: dynamic-import` ``` js import('./foo.js').then(foo => { foo.foo(); }); ``` ### 3.27. 支持`模块化导入: module` ``` js import { foo } from './foo.js'; ``` ### 3.28. 支持`模块化默认导入: module-with-default-export` ``` js import foo from './foo.js'; ``` ### 3.29. 支持`导出命名空间: export-namespace` ``` js export * as foo from './foo.js'; ``` ### 3.30. 支持`导出类: export-class` ``` js export class Foo { constructor(foo, bar) { this.foo = foo; this.bar = bar; } } ``` ### 3.31. 支持`函数sent特性: function-sent` ``` js 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` ``` js let b = 0; let a = b ||= 1; ``` ### 3.33. 支持`模块字符串: module-strings` ``` js import { "😄" as smile } from "emojis"; ``` ### 3.34. 支持`空选链: nullish-coalescing-operator` ``` js let foo = bar ?? 'foo'; ``` ### 3.35. 支持`空赋值: nullish-assignment-operator` ``` js let foo = bar ??= 'foo'; ``` ### 3.36. 支持`对象rest spread: object-rest-spread` ``` js let { foo, ...bar } = { foo: 'foo', bar: 'bar' }; ``` ### 3.37. 支持`数组rest spread: array-rest-spread` ``` js let [foo, ...bar] = ['foo', 'bar']; ``` ### 3.38. 支持`可选捕获绑定: optional-catch-binding` ``` js try{ console.log(1); }finally{ console.log('finally'); } ``` ### 3.39. 支持`可选参数: optional-parameters` ``` js function foo(x = 1) { return x; } ``` ### 3.40. 支持`可选参数和可选属性: optional-chaining` ``` js const foo = { bar: { baz: 'baz' } }; const baz = foo?.bar?.baz; ``` ### 3.41. 支持`私有In: private-in` ``` js 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` ``` js 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` ```js async do { await fetch('http://www.bing.com').json() } ``` ### 4.2. 支持`decimal` ``` js 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` ``` js 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` ``` js let x = do { let y = 1; y + 1; }; ``` ### 4.5. 支持`导出默认值: export-default-from` ``` js export default from './foo.js'; ``` ### 4.6. 支持`导出from: export-from` ``` js 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 ``` js 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` ``` js import Vue from "vue" assert { type: "js" }; ``` ### 4.9. 支持`局部模块化: module-declaration` ``` js let m = module { export let y = 1; }; ``` ### 4.10. 支持 `局部应用: partial-application` ``` js 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` ``` js' let result = "hello" |> console.log; ``` ### 4.12. 支持 Record 和 元组类型 ``` js let x = #{x: 1, y: 2}; let y = #[1, 2]; ``` ### 4.13. 支持`抛出异常表达式: throw-expressions` ``` js class Product { get id() { return this._id; } set id(value) { this._id = value || throw new Error("Invalid value"); } } ``` ## 5. 代码示例 ``` js // 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' } }, } ``` ``` 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> ```