# ES Module技术专题

# 概述

在前端开发中,为了避免变量名中的意外冲突,同时降低代码的复杂度,提高代码的可维护性,前端技术领域逐渐发展出了模块化开发的理念,并推出了AMD(异步模块定义)、CMD(通用模块定义)、CommonJS、ES Module 等一系列模块化规范。

ES Module(以下简称:ESM)是当今 JavaScript 开发中主流的模块化规范 ,它是由 JavaScript 语言规范《ECMAScript 2015标准》(ES2015)提出的模块化方案。ESM 在语言标准的层面上实现了模块功能,语法简洁,实现简单,是浏览器和服务器通用的模块化规范。

本文将从以下几个方面对 ESM 进行详细介绍,并通过一个完整的模块化开发示例为您介绍如何在 SuperMap iClient JavaScript 中基于 ESM 实现 WebGIS 项目的开发。

# ESM特点

ES Module 作为现代 JavaScript 开发中通用的模块化规范,具有支持静态加载、动态加载、支持主流浏览器的特点。

  • 静态加载

ESM的加载方式为静态加载,在编译过程中就能确定引入和导出的变量、模块间的依赖关系,与 CommonJS 的模块采用动态加载相比,ESM 模块加载效率更高。支持在开发阶段对引入和导出模块进行代码检查,并且可以在打包阶段结合 webpack 等工具进行 tree shaking,减小代码打包体积、缩短程序运行时间。

  • 动态加载

在 ES2020 后,ESM 支持使用 import() 函数动态引入模块,可以在运行时根据需求延迟加载模块,而不是在应用程序启动时一次性加载所有模块,有效地提高了应用程序的性能和响应速度。

  • 浏览器兼容性

AMD、CMD、CommonJS 等模块化规范基于的是 API 规范而非 ECMA 标准,并且它们未能被主流浏览器原生支持,而 ESM 是语言层面的规范,主流的 Chrome, Edge, Safari 和 Firefox 等浏览器均支持原生的 ESM,支持 ESM 的浏览器及其版本的详细信息如下。

图 支持 ESM 的浏览器及其版本信息

# ESM基本语法

本小节介绍了使用 ESM 的基本语法,包括:如何将一个文件标识为 ESM、引入模块和导出模块。

  • 将文件标识为 ESM 将文件扩展名设置为 .mjs ,可将文件声明为 ESM,以确保模块文件被正确解析。 在 Node.js 中,可通过设置 package.json 中的 type 属性为 module 来强制 package.json 下的所有文件使用 ESM。
{
  "type": "module"
}
  • 引入

使用 import 关键字将其他模块引入到当前模块中,import 关键字是静态的,只能在模块的顶层使用。

// 引入变量
import { CONSTANT, variable } from './module.js';

// 引入模块中的所有变量
import * as module from './module.js';

// 引入模块中的默认导出
import module from './module.js';

// 引入模块中的默认导出和其他变量
import module, { CONSTANT, variable } from './module.js';

如果要实现动态引入,需要使用 import() 函数。

const module = await import(pathToModule);
 
async function renderWidget() {
    const container = document.getElementById('widget');
    if (container !== null) {
        const widget = await import('./widget.js');
        widget.render(container);
    }
}
renderWidget();
  • 导出

使用 export 关键字将模块中内容导出到其他模块,可以使用 export default 关键字来进行默认导出,每个模块只能有一个默认导出。

// 导出变量
export const CONSTANT = 42;

// 默认导出
const CONSTANT = 42;
export default CONSTANT;

// 导出多个变量
const name = '张三';
const CONSTANT = 42;
export { name, CONSTANT };

// 导出所有变量
export * from './module.js';

# 模块化开发示例

本小节介绍了如何在一个 webpack 项目中,基于 ESM 引入 SuperMap iClient for Leaflet 进行模块化开发,实现功能:在浏览器中显示地图并进行矩形范围查询。

# 1.安装Node.js

开发前需要检查计算机中是否安装了 JavaScript 运行时环境 Node.js ,若未安装,可以在 Node.js 官网 中进行下载安装。Node.js 在成功安装后会附带 Node.js 的包管理器 npm。

安装完成后,打开命令提示符,输入以下命令,验证 Node.js 和 npm 是否安装成功。

node -v
npm -v

如果命令提示符中输出了 Node.js 和 npm 的版本号,说明已安装成功。

图 验证Node.js与npm安装成功

# 2.创建webpack项目

构建一个基本的 webpack 项目,用于后续的模块化开发。在命令行中输入以下命令,创建一个项目文件夹,初始化一个包管理配置文件 package.json,记录与项目有关的配置。

mkdir webpack-demo
cd webpack-demo
npm init -y

在项目根目录下,创建源代码文件夹 ./src 和项目的默认执行文件 ./src/index.js,然后为项目安装以下开发依赖。

npm install webpack@5.82.1 -D
npm install webpack-cli@5.1.1 -D

# 3.开发环境配置

为了使 webpack 项目支持 WebGIS 开发,需要引入 Leaflet 和 SuperMap iClient for Leaflet。SuperMap iClient JavaScript API 接口支持 ECMAScript 6 Promise,简化异步编程,使代码更优雅更易维护。此处引入最新版本的 SuperMap iClient for Leaflet 作为项目的运行依赖。

npm install @supermapgis/iclient-leaflet -S

# 引入CSS

在项目根目录下新建基本的 HTML 文件 index.html,在 <head> 标签中引入 Leaflet CSS 文件 和 iclient-leaflet CSS 文件。

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"/>
<link rel="stylesheet" href="https://iclient.supermap.io/dist/leaflet/iclient-leaflet.min.css"/>

# 引入模块

在 index.js 文件中,通过 ESM 的 import 关键字按需引入开发所需模块,按需引入模块可以减小项目的打包体积。

(1)安装 @supermapgis/babel-plugin-import。

npm install @supermapgis/babel-plugin-import -D

(2)在项目根目录下新建配置文件 .babelrc,添加如下配置。

{
    "plugins": [
        [
        "@supermapgis/babel-plugin-import",
            {
            "libraryName": "@supermapgis/iclient-leaflet"
            }
        ]
    ]
}

(3)在 index.js 文件中,引入开发所需的部分组件,此处引入的是用于实现地图显示的 TiledMapLayer,以及实现矩形范围查询的 QueryServiceQueryByBoundsParameters

import L from 'leaflet';
import {TiledMapLayer,QueryService,QueryByBoundsParameters} from '@supermapgis/iclient-leaflet';

# 4.功能实现

配置好开发环境后,在 webpack 项目中实现在浏览器中显示地图和矩形范围查询的功能。

(1)在 index.html 文件的 <body> 标签中,创建地图容器 map。

<body style="margin: 0;overflow: hidden;background: #fff;width: 100%;height:100%;position: absolute;top: 0;">
    <div id="map" style="margin:0 auto;width: 100%;height: 100%"></div>
</body>

(2)在 ./src/index.js 文件中添加如下代码,实现功能:在浏览器中显示地图。此处 url 填写的是 SuperMap iServer 发布的地图服务 map-world 中地图 世界地图_矢量 的服务地址。

const url = 'https://iserver.supermap.io/iserver/services/map-world/rest/maps/World';

const map = L.map('map', {
    preferCanvas: true,
    crs: L.CRS.EPSG4326,
    center: {lon: 0, lat: 0},
    maxZoom: 18,
    zoom: 2
});
new TiledMapLayer(url).addTo(map);

(3)在 ./src/index.js 文件中继续添加如下代码,实现功能:查询地图服务中指定矩形范围内的要素。

function query() {
  const polygon = L.polygon([[0, 0], [39, 0], [39, 60], [0, 60], [0, 0]]);
  polygon.addTo(map);
  const param = new QueryByBoundsParameters({
    queryParams: { name: 'Capitals@World.1' },
    bounds: polygon.getBounds(),
  });
  new QueryService(url).queryByBounds(param, function (serviceResult) {
    const result = serviceResult.result;
    L.geoJSON(result.recordsets[0].features).addTo(map);
  });
};
query();

(4)执行 webpack 命令打包项目,在项目根目录下会生成 dist 文件夹和里面的 main.js 文件,将 main.js 文件引入 index.html 文件中。

<body style="margin: 0;overflow: hidden;background: #fff;width: 100%;height:100%;position: absolute;top: 0;">
    <script src="../dist/main.js"></script>
</body>

(5)在浏览器中打开 index.html 文件,浏览器中将显示如下图所示的地图和矩形范围查询结果。

图 矩形范围查询结果

至此,您已经基于 ESM 构建了一个可用于 WebGIS 开发的 webpack 项目!更多有关 ESM 的介绍和使用方法可参考:webpack | 概念 | 模块 (opens new window)