SystemJS 和 import-map-overrides

3,137 阅读3分钟

笔记地址:SystemJS 和 import-map-overrides

SystemJS 诞生于 2015 年,那个时候 ES Module 还未成为标准,在浏览器端只能通过 requirejs、seajs 等方案实现模块加载,SystemJS 最初诞生的目的是为了做一个通用的模块加载器,在浏览器端实现对 CommonJS、AMD、UMD 等各种模块的加载。

Script标签的基本属性

nomodule

这个布尔属性被设置来标明这个脚本在支持 ES2015 modules 的浏览器中不执行。

实际上,这可用于在不支持模块化JavaScript的旧浏览器中提供回退脚本。

<script type="module" src="main.mjs"></script>
<script nomodule src="fallback.js"></script>

type

属性的值为MIME类型:

  • text/javascript
  • text/ecmascript
  • application/javascript
  • application/ecmascript
  • 如果没有定义type这个属性,也就是忽略type字段,脚本会被视作JavaScript
  • 如果type属性为module,代码会被当作JavaScript模块 。请参见ES6 in Depth: Modules
  • 如果MIME类型不是JavaScript类型(上述支持的类型),则该元素所包含的内容会被当作数据块而不会被浏览器执行

Content Security Policy

Content Security Policy (CSP) 文档地址

  1. 可以使用 meta 标签来配置策略:
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self'; img-src https://*; child-src 'none';">
  1. 可以使用 csp的http头来声明策略:

Content-Security-Policy: policy

SystemJS 提供的 type="systemjs-importmap"

<script type="systemjs-importmap">
  {
    "imports": {
      "moment": "https://cdn.jsdelivr.net/npm/moment/dist/moment.js",
      "lodash": "https://cdn.jsdelivr.net/npm/lodash/dist/lodash.js"
    }
  }
</script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>

systemjs与webpack互操作

开源库: systemjs-webpack-interop

webpack有一些用于提升 systemjs与webpack的互操作性的特性

webpack - libraryTarget: 'system'

以下内容都可以在 webpack官方文档(v4和v5)的说明里找到。

从webpack@4.30.0开始,提供 libraryTarget: 'system' 这个参数,这样webpack编译的库会提供一个作为 System.register 模块的库。

  1. webpack编译生成的System模块要求 System 作为一个全局变量出现在浏览器的运行环境里;
  2. webpack编译生成的库,符合System.register格式,使用System.import引入无需额外的配置,就可以让webpack bundle 加载进入 System 的 module registry;

webpack - Rule.parser : system

Rule的作用:创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。

Rule.parser的作用:解析选项对象。所有应用的解析选项都将合并。解析器(parser)可以查阅这些选项,并相应地禁用或重新配置,包含了 system 默认值是 false。

systemjs#compatibility-with-webpack

如果 webpack@<5 的话,需要添加这样的配置,避免重写全局 System 对象的引用

{
  module: {
    rules: [
      { parser: { system: false } }
    ]
  }
}

import-map-overrides

github地址:import-map-overrides

  1. 是一个 in-brower 和 nodejs 模块,可以重写 import map功能;

  2. 可以和 浏览器 原生 import maps 协同工作;

    可以和 SystemJS 的 import maps 系统工作;

    可以和 es-module-shims的 importmaps 协同工作;

  3. 他把覆写信息(override)保存在浏览器本地的 storage 里面,以便用户可以动态的修改 js 模块的 url;

  4. 开发者可以单独替换某一个模块到他们的本地开发环境,而不必重启本地环境、开发服务器或者其他环境;

Import Map Type

他默认与浏览器原生的Import Map工作,如果需要和其他模块协同,需要明确指定

例如,使用 SystemJS:

Override Mode

ModeNative import mapsSystemJSes-module-shimsWorks with ****external mapsOverrides when server renderingEasy to set up
Client-side multiple maps❌1
Client-side single map❌2❌1
Server-side multiple maps🤔3
Server-side single map✅4

他的四种模式:

  1. Client-side multiple maps (default)

  2. Client-side single map

    使用这种模式,将 type="overridable-importmap"

  3. Server-side multiple maps

  4. Server-side single map

Domain List

启用 domains这个功能:

  1. 在header中添加: ;
  2. 可以在 meta的content字段设置两种:denylist和allowlist;
<!-- Disable the entirety of import-map-overrides unless you're on the dev or stage environments -->
<meta
  name="import-map-overrides-domains"
  content="allowlist:dev.example.com,stage.example.com"
/>

<!-- Disable the entirety of import-map-overrides when you're on the production environment -->
<meta name="import-map-overrides-domains" content="denylist:prod.example.com" />

<!-- You can also use wildcards url -->
<meta
  name="import-map-overrides-domains"
  content="allowlist:*.example.com,example-*.com"
/>

安全

  1. 强烈推荐:在你的server上设置你的html的 CSP 的 http 请求头来 确保安全;
  2. 考虑在生产环境上删除 import-map-override 段,或者配置 domain list;

API

该库加载了之后,会在window全局建立自己的对象,在这个对象上有所有的API接口可以调用。

window.importMapOverrides

两个事件 init 和 change

// init 事件
window.addEventListener("import-map-overrides:init", () => {
  console.log('init');
})

// change 事件
window.addEventListener("import-map-overrides:change", logImportMap);

// Later on you can remove the event listener
window.removeEventListener("import-map-overrides:change", logImportMap);

function logImportMap(evt) {
  console.log(window.importMapOverrides.getOverrideMap());
}

启用 UI

你可以在Dom中为他准备一个节点,这样就可以在界面上操作。有以下三种方法:

<!-- 这是全UI,包含了 trigger 按钮
show-when-local-storage 属性介绍:
1,忽略show-when-local-storage这个属性,黄色/橘色方块始终可见;
2,设置他的属性为某一个值,那么需在控制台console中使用
   localStorage.setItem('overrides-ui', true); 控制 UI 可见;
   
dev-libs 属性介绍:
加上这个属性之后,会优先选择 deveopment 版本的js。
想要关闭那么可以调用: `localStorage.setItem('import-map-overrides-dev-libs', false)` 关闭。
设置 true 就可以开启
-->
<import-map-overrides-full
  show-when-local-storage="overrides-ui"
  dev-libs
></import-map-overrides-full>

<!-- Alternatively, just the black popup itself -->
<import-map-overrides-popup></import-map-overrides-popup>

<!-- Or if you only want the actual import map list and UI for overriding -->
<import-map-overrides-list></import-map-overrides-list>

<!-- Or if you prefer enabling via javascript -->
<script>
  importMapOverrides.enableUI();
</script>

import-map-deployer

github地址: import-map-deployer

是后端服务更新 import map json files的工具库。当使用 import-map-deployer的时候,一个前端开发完全准寻以下两步:

  1. 上传js文件到CDN;
  2. 发送一个http请求去修改已存在的 import map指向新的文件;