# 现代WebGIS开发技术与工具
# 现代WebGIS体系
现代 WebGIS 是现代 Web 技术在 GIS 中的应用。现代WebGIS的体系结构与其他现代 Web 项目的体系结构没有太多本质上的区别。
综上,WebGIS 的发展除了与地理信息系统、数字地图领域发展相关,还和万维网或Web技术的发展密切相关。因此Web技术是WebGIS应用开发者必须学习的基础知识之一。本文围绕现代主流的 Web 开发技术,首先介绍 Web 开发技术的大致发展历程,然后对前端模块化、构建工具、包管理器、语言和前端框架五个部分进行介绍,旨在帮助初级 WebGIS 应用的开发者全面了解 Web 开发技术,进而快速选型。
# 从传统Web到现代Web开发
1991年Tim Berners Lee 公开介绍了他的 World Wide Web(简称Web)项目,这是Web第一次被介绍给全世界,在这一年,越来越多的Web服务器联入网络,越来越多的 Web 网站出现,标志着 Web 时代的到来。
早期 Web 开发等同于内容开发,没有前后端开发的概念,页面由 JSP、PHP 等工程师在服务器端生成,浏览器负责展现。早期 Web 除了内容,它还支持通过超链接和表单的浏览器原生行为,实现一种以页面为最小粒度的交互模式,基于这种交互模式,出现了最早的 Web 应用。之后以页面级交互的需求越来越多,人们开始不能接受生成页面和处理页面的代码混写在一起,进而发展了 MVC 架构的服务器端框架,生成 HTML 的代码变成视图层。随着互联网的发展,对交互有了更多的需求,AJAX 的出现极大地提高了 Web 的用户体验,在 HTML 页面上提供丰富的交互能力。随之而来的是 jQuery 的快速发展,jQuery 对各种浏览器做好了兼容,简化了 DOM 操作,使开发效率大幅提升。
移动互联网的爆发带来了 Web 技术的重大变革。客户端需求复杂化,大量应用流行,对用户体验的期望提高,客户端渲染成为刚需,客户端程序不得不具备完整的生命周期、分层架构和技术栈,这个阶段催生了 Angular 1 等一系列优秀的框架以及AMD、UMD 与 RequireJS 等模块标准与加载工具,前端工程师也成为了专门的开发领域,拥有独立于后端的技术体系与架构模式。
近几年间,随着Web应用复杂度的提升以及用户对于页面交互友好与性能优化的需求,急需更加优秀灵活的开发框架来协助我们更好的完成前端开发。这个阶段涌现出了很多关注点相对集中、设计理念更为优秀的框架,譬如 React、Vue.js、Angular 2 等组件框架允许我们以声明式编程来替代以 DOM 操作为核心的命令式编程,加快了组件的开发速度,并且增强了组件的可复用性与可组合性。由于前端工程师要维护的代码变得极为庞大和复杂,代码维护、打包、发布等流程也变得极为繁琐,在构建工具上,涌现了以 Grunt、Gulp 为代表的任务管理和以 webpack 为代表的项目打包工具,帮助开发者更好的搭建前端构建流程,自动化地进行预处理、异步加载等操作。
现代 Web 开发具有以下特点:
- 以客户端为主体:服务器端部分很薄,大量成熟业务逻辑、数据能力、运维能力和基础设置被 API 化、云服务化
- 不只有客户端:服务端渲染 + API Gateway + 部分面向应用的微服务
- 不只有浏览器:超级 app 平台、各种基于 Web Runtime/JS Runtime的 Hybrid 技术
# 模块化
在 Web 前端开发中,模块化是指独立的、可重用的代码小单元。模块化开发具有诸多优点,因此成为了目前最普遍的前端代码组织方式:
- 可以将程序分成多个部分或模块,并让每个部分负责一个功能或关注点,而不是将所有程序的组件放在一个文件中
- 分模块的代码更易维护,并且出错概率较低
- 模块可以在项目的不同文件和部分中轻松使用和重用,无需编写重复代码
- 由于每个模块是独立的,所以变量或函数名重名不会发生冲突 原生的 JavaScript 中并不能很好地支持模块化开发,因此陆续出现了各种解决方案,包括 AMD、CMD、CommonJS 等,而后 ECMA 组织在 JavaScript 语言标准层面,增加了模块功能 ECMAScript modules(ESM)。
# CommonJS
CommonJS 是一个模块格式化标准。目前 Nodejs 和 Browserify 是最具代表性的实现。CommonJS 规范中约定:
require
用来加载某个模块- 在 CommonjJS 中每一个 js 文件都是一个单独的模块,可以称之为 module,保存了当前模块的信息
exports
是 module 上的一个属性,保存了当前模块要导出的接口或者变量,使用require
加载的某个模块获取到的值就是那个模块使用exports
导出的值。
例如:
// add.js
function add (a,b){
return a + b
}
module.exports = add
//index.js
const add = require('./add')
console.log(add(4,5)) // 9
CommonJS 的优点:
- 依赖关系管理是集成的:模块需要其他模块并按所需的顺序加载
require
可以在任何地方调用- 支持循环依赖
CommonJS 的不足
- 加载模块是同步的
- 每个模块一个文件
- 浏览器需要加载器库或转译
相关链接:
# Asynchronous Module Definition (AMD)
Asynchronous Module Definition (中文:异步模块定义;英文缩写:AMD) API 指定了一种定义模块的机制,以便可以异步加载模块及其依赖项。这特别适合于同步加载模块会带来性能、可用性、调试和跨域访问问题的浏览器环境。AMD 和 CommonJS 的主要区别在于支持异步模块加载。目前最流行的实现是 Require.js 和 Dojo。AMD 具有以下特点:
- 能够改进网站性能。AMD 实现仅在需要时才加载较小的 JavaScript 文件。
- 页面错误更少。AMD 允许开发人员定义在执行模块之前必须加载的依赖项,因此模块不会尝试使用尚不使用的外部代码。
- AMD 规范定义了单个函数“define”,可用作自由变量或全局变量。
define(id?, dependencies?, factory);
AMD 规范的优点:
- 异步加载,更好的启动时间
- 模块可以拆分为多个文件
AMD 规范的不足:
- 语法上略复杂
- 浏览器需要加载器库或转译
相关链接:
- AMD GitHub 项目 (opens new window)
- Dojo 中使用 AMD (opens new window)
- Require.js 中使用 AMD (opens new window)
# ECMAScript modules(ESM)
ECMAScript modules(简称ESM)是组织 JavaScript 代码以供重用的官方标准格式。ESM 是使用各种 import 和 export 语句定义的:
- import 用于将模块导入
- export 用于将模块导出 以下是 ESM 模块导入函数的示例:
import defaultExport from "module-name";
import *
as name from "module-name";
import { export } from "module-name" ;
import { export as alias } from "module -name" ;
import { export1 , export2 } from "module-name" ;
import { foo , bar } from "module-name/path/to/specific/un-exported/file" ;
import { export1,export2 as alias2,[...] } from
module -name
import defaultExport, { export [, [ ...] ] } from "module-name" ;
import defaultExport,
as name from "module-name";
import "module-name'
var promise = import( "module-name" );//这是一个处于第三阶段的提案。
以下是 ESM 模块导出函数的示例:
//导出单个特性
export let name1, name2, ..., nameN; // also var, const
export let name1 = . name2 = ..., ..., nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}
//导出列表
export { name1, name2, ..., nameN };
//重命名导出
export { variable1 as name1, variable2 as name2, ..., nameN };
//解构导出并重命名
export const { name1, name2: bar } = o;
//默认导出
export default expression;
export default function (...) {...} // also class, function*
Node.js 完全支持当前指定的ESM,并提供它们与其原始模块格式 CommonJS 之间的互操作性。
ESM 规范的优点:
- 支持同步和异步加载
- 构建时可以消除死代码 tree shaking
- 支持循环依赖
- 现代浏览器都已支持
ESM 规范的不足:
- 模块文件过多,网络请求频繁
相关链接:
# Web地图库也顺应着模块化的发展
在 GIS 行业中,主流的 Web 地图库也在顺应着模块化规范的发展,例如 OpenLayers V3 版本采用了 Google Closure,V5 版本使用了 ESM 规范;Leaflet 1.1.0 版本使用了 ESM 规范;MapboxGL 0.45.0 版本从 CommonJS 升级为 ESM;SuperMap iClient JavaScript 9.0.0 版本使用了ESM规范。
# 构建工具
构建工具是一套软件程序或框架,能够帮助开发者实现自动化软件开发,同时处理部署过程中涉及的各种任务。这些工具旨在简化程序构建过程,使其更加高效和可靠。构建工具通常处理诸如将源代码编译为可执行文件、管理依赖项、优化和压缩资产、运行测试、生成文档以及打包应用程序以进行部署等任务。具体地,大部分构建工具都包含的功能有:
- 缩小和/或压缩文件
- 优化图像和/或字体
- 连接文件
- 编译或转译代码
- 生成开发服务器
- 热重载模块,无需整页刷新
- 检测到更改时自动监视文件并生成构建 以下介绍目前使用最多最受欢迎的几款构建工具:
# webpack
webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图(dependency graph),然后将项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源。 webpack 具有以下特点:
- 支持 ES Modules、CommonJS 和 AMD 模块(甚至组合)
- 可以创建单个包或在运行时异步加载的多个块,以减少初始加载时间
- 依赖关系在编译期间得到解决,从而减少了运行时大小
- 加载器可以在编译时预处理文件,例如 TypeScript 到 JavaScript、Handlebars 字符串到编译函数、图像到 Base64 等
- 高度模块化的插件系统可以完成您的应用程序所需的任何其他操作
- 支持许多不同的静态资源,images, fonts, stylesheets
- 关心性能和加载时间
- 异步地加载 chunk、预取、Tree-shaking
相关链接:
# Vite
Vite 是一款新兴的前端构建工具,核心思想为非捆绑开发构建,能够显著提升前端开发体验。Vite 由两部分组成:
- 一个开发服务器:基于原生 ES Modules 提供了丰富的内建功能,如速度快到惊人的模块热更新(HMR)。
- 一套构建指令,使用 Rollup 打包代码,预配置了可输出用于生产环境的高度优化过的静态资源。
相关链接:
# 包管理器
包管理器可以使开发人员能够更轻松地在不同项目之间共享代码,并在自己的项目中使用其他人的代码。
# npm
npm 是目前世界上最大的软件注册中心,可用于共享和借用软件包,许多组织也使用 npm 来管理私人开发。npm 也是 Node.js 自带的包管理器。 npm 由三个不同的组件组成:
- 网站:使用该网站来发现包、设置配置文件以及管理 npm 体验的其他方面。例如,可以设置组织来管理对公共或私有包的访问。
- 命令行界面 (CLI):CLI 从终端运行,是大多数开发人员与 npm 交互的方式
- 注册表:JavaScript 软件及其相关元信息的大型公共数据库
npm 可以用于:
- 为应用调整代码包,或按原样合并包
- 下载您可以立即使用的独立工具
- 运行包而无需使用 npx 下载
- 随时随地与任何 npm 用户共享代码
- 将代码限制为特定开发人员
- 创建组织来协调包维护、编码和开发人员
- 使用组织组建虚拟团队
- 管理多个版本的代码和代码依赖项
- 更新底层代码时轻松更新应用程序
- 发现解决同一难题的多种方法
- 查找正在处理类似问题和项目的其他开发人员
相关链接:
# Yarn
Yarn 是为了弥补 npm 的一些缺陷而出现的,目前已是一个成熟的开源包管理器。与大多数其他包管理器(通常遵循 npm 来执行非安装相关命令)不同,Yarn 重新实现了所有命令,以便完全控制其开发人员体验和稳定性。运行速度更快、更安全、更可靠。Yarn 具备以下特点:
- 支持插件,并提供便捷的插件添加方式
- 默认支持 Node,同时也支持使用插件添加对其他语言的支持
- Yarn 原生支持 workspaces
- Yarn 使用类似 bash 的便携式 shell,使包脚本可以跨 Windows、Linux 和 macOS 迁移
- Yarn 是第一个可以以编程方式使用的 Node API (通过@yarnpkg/core)
- Yarn 用 TypeScript 编写的并经过全面的类型检查
相关链接:
# PNPM
使用 PNPM 包管理器,能够有效地节省磁盘空间,并进行快速安装。例如当使用 npm 时,如果有 100 个项目,并且所有项目都有一个相同的依赖包,那么,在硬盘上就需要保存 100 份该相同依赖包的副本。然而,如果使用 pnpm,依赖包将被存放在一个统一的位置,因此:
- 如果对同一依赖包需要使用不同的版本,则仅有版本之间不同的文件会被存储起来。 例如,如果某个依赖包包含 100 个文件,其发布了一个新版本,并且新版本中只有一个文件有修改,则只需要通过
pnpm update
添加一个新文件到存储中,而不会因为一个文件的修改而保存依赖包的所有文件。 - 所有文件都保存在硬盘上的统一的位置。当安装软件包时,其包含的所有文件都会硬链接自此位置,而不会占用额外的硬盘空间。可以在项目之间方便地共享相同版本的依赖包。 综上,以项目和依赖包的比例来看,pnmp 节省了大量的硬盘空间,并且安装速度也大大提高。
在安装速度方面,pnpm 分为3个阶段进行安装:
- 依赖解析。识别所有必需的依赖项并将其提取到存储中。
- 目录结构计算。node_modules 目录结构是根据依赖关系计算出来的。
- 链接依赖关系。所有剩余的依赖项都会从存储区获取并硬链接到 node_modules。 这种方法比解析、获取所有依赖项并将所有依赖项写入 node_modules 的传统三阶段安装过程要快得多。 当使用 npm 或 Yarn Classic 安装依赖包时,所有软件包都将被提升到 node_modules 的 根目录下。其结果是,源码可以访问本不属于当前项目所设定的依赖包。默认情况下,pnpm 则是通过使用符号链接的方式仅将项目的直接依赖项添加到 node_modules 的根目录下。
整体上,pnmp 具备以下几个特点:
- 快速:比替代方案快2倍
- 高效:node_modules 内的文件是从单个内容可寻址存储克隆或硬链接的
- 适合单仓多包
- 严格的:包只能访问其 package.json 中指定的依赖项
- 确定性的:有一个名为 pnpm-lock.yaml 的锁定文件
- 是 Node.js 的版本管理器
- 支持 Windows、Linux 和 macOS 目前已经有越来越多的组件库选择使用 pnpm。如果您使用 Monorepo(单仓多包),可以选择 pnpm。
相关链接:
# 语言
# JavaScript
JavaScript(JS)是一种编程语言,与 HTML 和 CSS 一样,是万维网的核心技术之一。截至 2023 年,98.7% 的网站在客户端使用JavaScript 进行网页行为。所有主要的网络浏览器都有专用的 JavaScript 引擎在用户设备上执行代码。 JavaScript 是一种高级的、通常是即时编译的语言,符合 ECMAScript 标准。它具有动态类型、基于原型的面向对象和一流的功能。它是多范式的,支持事件驱动、函数式和命令式编程风格。它具有用于处理文本、日期、正则表达式、标准数据结构和文档对象模型的应用程序编程接口(API)(DOM)。 ECMAScript 标准不包括任何输入/输出(I/O),例如网络、存储或图形设施。实际上,Web 浏览器或其他运行时系统提供用于 I/O 的JavaScript API。 JavaScript 引擎最初仅在 Web 浏览器中使用,但现在已成为某些服务器和各种应用程序的核心组件。这种用途最流行的运行时系统是 Node.js。
相关链接:
# TypeScript
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,在 JavaScript 中添加了显式类型系统,在转译时运行类型检查。例如,JavaScript 提供了字符串和数字等语言基元,但它不会检查您是否一致地分配了这些基元,而 TypeScript 可以。TypeScript 的主要好处是它可以突出显示代码中的意外行为,从而减少错误。
推测类型
TypeScript 在许多情况下会自动生成类型。例如,在创建变量并给定值时,TypeScript 将使用该值的类型作为变量的类型。TypeScript 构建了一个接受 JavaScript 代码但具有类型的类型系统,无需添加额外的字符即可在代码中明确类型。
定义类型
对于一些并不容易自动推断类型设计模式,例如,使用动态编程的模式。为了涵盖这些情况,TypeScript 支持 JavaScript 语言的扩展,允许指定类型。 构建类型 TypeScript 允许通过组合简单类型来创建复杂类型。包含两种方式:
- 联合:您可以声明一个类型可以是多种类型中的一种
- 泛型:泛型为类型提供变量。一个常见的例子是数组。没有泛型的数组可以包含任何内容,而具有泛型的数组可以描述数组包含的值。
结构类型系统
TypeScript 的核心原则之一是检查两个对象中的值的形式。这有时称为“鸭子类型”或“结构类型”。在结构类型系统中,如果两个对象具有相同的形式,则它们被认为是同一类型。 相关链接:
# WebAssembly
WebAssembly(缩写为Wasm)是一个可移植、体积小、加载快并且兼容 Web 的全新格式。具备以下特点:
- 高效:WebAssembly 有一套完整的语义,实际上 wasm 是体积小且加载快的二进制格式, 其目标就是充分发挥硬件能力以达到原生执行效率
- 安全:WebAssembly 运行在一个沙箱化的执行环境中,甚至可以在现有的 JavaScript 虚拟机中实现。在web环境中,WebAssembly将会严格遵守同源策略以及浏览器安全策略
- 开放:WebAssembly 设计了一个非常规整的文本格式用来、调试、测试、实验、优化、学习、教学或者编写程序。可以以这种文本格式在web页面上查看wasm模块的源码。
- 标准:WebAssembly 在 web 中被设计成无版本、特性可测试、向后兼容的WebAssembly 可以被 JavaScript 调用,进入 JavaScript 上下文,也可以像 Web API 一样调用浏览器的功能。当然,WebAssembly 不仅可以运行在浏览器上,也可以运行在非 web 环境下 WebAssembly 旨在补充 JavaScript 并与 JavaScript 一起运行,而不是取代 JavaScript。
相关链接:
# 其他一些语言
# CoffeeScript
CoffeeScript 是一种可以编译成 JavaScript 的小语言。是 JavaScript 的“语法糖”。它引入了更短的语法,能够编写更清晰、更精确的代码。通常,Ruby 开发人员喜欢它。
# Dart
Dart 是一种独立的语言,拥有自己的引擎,可以在非浏览器环境(如移动应用程序)中运行,但也可以转换为 JavaScript。由谷歌开发。
# Kotlin
Kotlin 是一种现代、简洁、安全且已经成熟的编程语言,可与 Java 和其他语言互操作,并提供多种在多个平台之间重用代码以进行高效编程的方法。
# 前端框架
框架是指一组可供 Web 开发人员使用的软件工具或平台。开发人员可以借助框架构建可扩展的网站和 Web 应用程序。大多数框架的主要特征之一是使用可重用的组件或代码,代码的可重用性以及框架的其他功能能够加快开发过程。框架可以包括代码库、编译器、API等等。本文将主要介绍前端框架,前端框架是指用于在用户界面中可视化后端指令的软件平台或工具。 随着前端技术的快速发展,开发人员有许多可选择的框架,每个框架都有其独特功能。因此,在选择框架时,开发人员需要注意其业务想要实现的目标并选择适合的选项。 以下就介绍一些目前最广泛使用的前端框架:
# React
React 是一个用于构建用户界面的 JavaScript 库。
- 声明式:React 可以轻松创建交互式 UI。为应用程序中的每个状态设计简单的视图,当数据发生变化时,React 将进行有效地更新并渲染正确的组件。声明式视图可以使您的代码更加可预测、更易于理解和调试。
- 基于组件的:React 支持构建管理其自身状态的组件,然后组合这些组件以创建复杂的 UI。由于组件逻辑是用 JavaScript 而不是模板编写的,因此您可以轻松地通过应用程序传递丰富的数据,并将状态保留在 DOM 之外。
- 一次学习,随处编写:可以在 React 中开发新功能,而无需重写现有代码。React 还可以使用 Node 在服务器上渲染,并使用 React Native 为移动应用程序提供支持。
相关链接:
# Vue
Vue.js 是一个渐进式 JavaScript 框架,已迅速成为顶级 Web 框架之一,经常用于开发单页应用程序。该框架结合了其他框架的优点,例如 React 的组件系统和 Angular 的数据绑定功能。 Vue.js 使用双向数据绑定,这使开发人员更容易保持数据和 UI 同步。此外,Vue.js 还具有高性能的虚拟 DOM 实现,并提供服务器端渲染支持。 使用 Vue.js 的优点之一是其语法简单,这使得它易于学习和使用。此外,Vue.js 提供双向数据绑定,允许用户界面和底层数据模型保持同步。目前Vue在国内的使用量较高。
相关链接:
# Web Components
Web Components是一组 Web 平台 API,支持创建新的自定义、可重用、封装的 HTML 标签以在网页和 Web 应用程序中使用。自定义组件和小部件基于Web Components 标准构建,可以跨现代浏览器工作,并且可以与任何支持 HTML 的 JavaScript 库或框架一起使用。 Web Components 基于四个主要技术:
自定义元素
一组 JavaScript API,允许您定义自定义元素及其行为,然后可以根据需要在用户界面中使用它们。
影子 DOM
一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制相关功能。通过这种方式,您可以将元素的功能保持私有,以便可以对它们进行脚本化和样式化,因此不必担心与文档的其他部分发生冲突。
ES模块
ES模块规范以基于标准、模块化、高性能的方式定义了JS文档的包含和重用。
HTML 模板
HTML 模板元素规范定义了如何声明在页面加载时未使用但可以稍后在运行时实例化的标记片段。
相关链接: