跳至正文
Wener Notes

SystemJS

约 4 分钟阅读
    • 代码控制的模块环境
    • 辅助实现微前端
    • 辅助实现 前端 动态模块化
    • ESM 不受控 - 只能 all in
  • features - core 之上增加特性
    • depcache
    • script-load
    • worker-load
    • fetch-load
      • 加载的内容会使用 eval - 目前 无法修改该行为
    • import-maps
    • node-fetch
      • file: 会直接读取, 其他则直接 fetch
      • source-map-support
      • node-fetch
    • registry
      • System.get,System.set,System.has,System.delete,System.entries
    • resolve
      • System.resolve
  • extras - 额外功能
  • system-core.js
    • 基础 - 其他 dist 在这基础之上增加 hook 行为
    • System.import
    • System.register
    • System.getRegister
    • System.createContext - import.meta
    • System.onload(err,id,deps)
  • system.js
    • script-load, fetch-load, resolve, import-maps, depcache, worker-load, global, module-types, registry
  • system-node.cjs
    • resolve, registry, fetch-load, node-fetch, global
    • 暴露的额外方法
      • applyImportMap(loader, newMap, mapBase)
      • setBaseUrl(loader, url)
  • s.js
    • script-load, fetch-load, resolve, import-maps, depcache, worker-load
  • 参考
bash
npm add systemjs
npx typesync
依赖包
html
<script type="systemjs-importmap">
  {
    "imports": {
      "react": "./react.js"
    }
  }
</script>
<!-- 触发 import -->
<script type="systemjs-module" src="import:react"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
TypeScript
// 注意顺序
import 'systemjs/dist/system';
import 'systemjs/dist/extras/dynamic-import-maps';
import 'systemjs/dist/extras/module-types';
import 'systemjs/dist/extras/named-register';

// 全局
export function getModuleSystem() {
  return (globalThis as any)['System'] as ModuleSystem;
}

export type ModuleSystem = typeof System;

// 新的 System 实例
const clonedSystem = new System.constructor();
  • 只有 import 和 resolve 支持 bare specifier, 其他都需要 resolve
  • bare specifier -> react
    • import map 可将 bare specifier 映射为其他 - 例如: systemjs:react, builtin:react, /react
    • resolve 根据映射后的逻辑进行解析
TypeScript
const System: {
  import: System.ImportFn;

  /**
   * Inserts a new module into the SystemJS module registry. The System.register format is
   * the underlying implementation that allows for ESM emulation.
   * See https://github.com/systemjs/systemjs/blob/master/docs/system-register.md for more details.
   * Register may be called with a name argument if you are using the named-register extra. (See
   * https://github.com/systemjs/systemjs#extras).
   */
  register(dependencies: string[], declare: System.DeclareFn): void;
  register(name: string, dependencies: string[], declare: System.DeclareFn): void;

  // moduleId -> 完整 URL
  // moduleId 不存在会异常
  resolve(moduleId: string, parentUrl?: string): string;

  // System.delete(System.resolve('react'))
  // 返回的函数可用于 re-importing - 重新绑定所有 exports
  delete(moduleId: string): false | System.UpdateModuleFn;

  // System.get(System.resolve('react'))
  get(moduleId: string): System.Module | null;
  get<T>(moduleId: string): T | null;

  // System.has(System.resolve('react'))
  has(moduleId: string): boolean;

  // System.set(System.resolve('react'), window.React)
  set(moduleId: string, module: System.Module): void;

  // Object.fromEntries(System.entries())
  entries(): Iterable<[string, System.Module]>;
};

Hook

TypeScript
const existingHook = System.constructor.prototype.hookName;
System.constructor.prototype.hookName = function (args) {
  return Promise.resolve(existingHook.call(this, args)).then(function (existingHookResult) {
    // custom hook here
    return;
  });
};

FAQ

使用现有环境

html
<script type="systemjs-importmap">
  {
    "imports": {
      "react": "builtin:react",
      "react/jsx-runtime": "builtin:react/jsx-runtime",
      "react-dom": "builtin:react-dom",
      "react-dom/client": "builtin:react-dom/client"
    }
  }
</script>
<script src="system.js"></script>
<script>
  System.set(System.resolve('react'), window.React);
</script>
JavaScript
// 复用 Module
System.set(System.resolve('react'), await import('react'));
System.set(System.resolve('react/jsx-runtime'), await import('react/jsx-runtime'));
System.set(System.resolve('react-dom'), await import('react-dom'));
System.set(System.resolve('react-dom/client'), await import('react-dom/client'));

// 复用全局
System.set(System.resolve('vue'), window.Vue);
System.set(System.resolve('vue-router'), window.VueRouter);

rxjs hack

JavaScript
const originalResolve = System.resolve;

System.resolve = function (name) {
  if (name === 'rxjs/operators') {
    return 'rxjs-operators.js';
  } else {
    return originalResolve.apply(this, arguments);
  }
};

System.register('rxjs-operators.js', [], (_export) => {
  System.import('rxjs').then((rxjs) => {
    _export(rxjs.operators);
  });
  return {};
});

转换逻辑

输入

TypeScript
export * from '@wener/reaction';
import * as React from 'react';
import {isValidElement} from 'react';
import Meta from '@wener/reaction/package.json';

await func();

export const ver = 1;
const ele = React.createElement('div');
export {ele as DivNode};

console.assert(isValidElement(ele));
console.log(`Reaction version ${Meta.version}`);
JavaScript
System.register('myBundle', ['@wener/reaction', 'react'], function (exports) {
  'use strict';
  var _starExcludes = {
    DivNode: 1,
    ver: 1,
    default: 1,
  };
  var React, isValidElement;
  return {
    setters: [
      function (module) {
        var setter = {};
        for (var name in module) {
          if (!_starExcludes[name]) setter[name] = module[name];
        }
        exports(setter);
      },
      function (module) {
        React = module;
        isValidElement = module.isValidElement;
      },
    ],
    execute: async function () {
      await func();

      const ver = exports('ver', 1);
      const ele = exports('DivNode', React.createElement('div'));

      console.assert(isValidElement(ele));
    },
  };
});

reexport

默认不包含 default

TypeScript
export * from 'react';
JavaScript
System.register('myBundle', ['react'], function (exports) {
  'use strict';
  var _starExcludes = {
    default: 1,
  };
  return {
    setters: [
      function (module) {
        // 不包含 default
        var setter = {};
        for (var name in module) {
          if (!_starExcludes[name]) setter[name] = module[name];
        }
        exports(setter);
      },
    ],
    execute: function () {},
  };
});

with default

TypeScript
export * from 'react';
export {default} from 'react';
JavaScript
System.register('myBundle', ['react'], function (exports) {
  'use strict';
  var _starExcludes = {
    default: 1,
  };
  return {
    setters: [
      function (module) {
        // 先包含 default 部分
        var setter = {default: module['default']};
        for (var name in module) {
          if (!_starExcludes[name]) setter[name] = module[name];
        }
        exports(setter);
      },
    ],
    execute: function () {},
  };
});

async/异步

关联信息

反向链接、本文链接的其他页面和外部资料。

参考资料

GitHub13 条
其他外链4 条
最近更新commit 32faea0编辑