基于Ganos百行代码实现亿级矢量空间数据在线可视化

简介: 本文介绍如何使用RDS PG或PolarDB(兼容PG版或Oracle版)的Ganos时空引擎提供的数据库快显技术,仅用百行代码实现亿级海量几何空间数据的在线快速显示和流畅地图交互,且无需关注切片存储和效率问题。

01 引言

 

如何对时空数据库中的亿级矢量空间数据进行在线可视化一直是业界难题。因数据体量大,传统方法需要将数据库中数据进行基于缓存切片的服务发布才能可视化,操作流程冗长,且有一大堆需要考虑的问题:

 

  • 如果对矢量数据进行预切片,数据要切多久?切多少级合适?存储瓦片的硬盘空间够用吗?
  • 如果使用实时瓦片,实时渲染瓦片的响应时间能保证吗?
  • 如果使用矢量瓦片,小比例尺的瓦片可能会有多大体积?传输会不会成为瓶颈?前端渲染能承受多大的数据量?
     

 

如果是要快速浏览数据库中的大规模在线数据,传统用于“底图服务”的离线切片生产流程几乎无解,不但费时费力,又无法在线联机处理。黑科技来了,本文介绍如何使用RDS PG或PolarDB(兼容PG版或Oracle版)的Ganos时空引擎提供的数据库快显技术,仅用百行代码实现亿级海量几何空间数据的在线快速显示和流畅地图交互,且无需关注切片存储和效率问题。

 

02 技术特性解读

 

Ganos的在线快显处理的核心是将数据库和可视化进行了关联,提供了一种新的可视化索引技术——稀疏矢量金字塔(Sparse Vector Pyramid,SVP)索引。SVP具备两个关键特性:快与省

 

其中,快指两个阶段的快:

  • 金字塔创建快:Ganos利用空间索引对数据在空间上进行密集度划分,根据密集度建立一种稀疏矢量金字塔索引,相比传统切图流程减少了90%的数据计算量。同时,创建金字塔采用了完全并行处理模式,即使1亿条地类图斑数据生成金字塔也仅需耗费约10分钟时间。
  • 数据展现快:Ganos采用了视觉可见性剔除算法,根据Z-order排序,过滤掉大量不影响显示效果的数据,从而加快实时显示的效率。Ganos支持直接输出PNG格式的栅格瓦片和MVT格式的矢量瓦片,1亿地类图斑数据实时渲染显示的响应时间都达到秒级。

 

省也具有两个维度:

  • 节省磁盘空间:1亿条地类图斑数据生成金字塔索引仅仅占据原表5%大小的额外空间。
  • 节省开发时间:仅使用简单的SQL语句,通过调整语句参数即可灵活控制显示效果。

 

03 使用步骤

 

Ganos的快显引擎使用上非常简洁,已高度封装了SQL函数。需要注意的是,第一次使用快显引擎之前,需要显式创建对应的扩展模块,执行的语句如下:

 

CREATE EXTENSION ganos_geometry_pyramid CASCADE;

 

通过执行以上语句,快显引擎的计算组件将会被加载起来。

 

3.1 建立稀疏矢量金字塔

 

假设您已创建了某个矢量大表并导入了数据,接着就可以使用Ganos的st_buildpyramid方法创建矢量金字塔。

 

方法原型如下,更详细的参数描述可以参考官方文档。

 

boolean ST_BuildPyramid(cstring table, cstring geom, cstring fid, cstring config)

注:*左右滑动阅览

 

其中

  • table:矢量数据所在的表名。
  • geom:矢量字段名。
  • fid:矢量要素记录的唯一标识,支持Int4/Int8类型。
  • config:json格式的配置参数字符串。
    • 在本例中,我们指定矢量金字塔的名称和使用的逻辑瓦片大小(这个瓦片大小并非真实存在的瓦片,仅表示一种空间上的逻辑划分)

 

实际调用如下:

 

ST_BuildPyramid('points', 'geom', 'gid', '{"name":"points_geom","tileSize":512}')

注:*左右滑动阅览

 

我们为表points的geom字段创建了一个矢量金字塔,金字塔名我们指定为points_geom,同时设定金字塔的逻辑瓦片大小为512。

 

3. 2 获取栅格瓦片

 

栅格瓦片是图片形式的瓦片(Tile),是使用最广泛的一种地图瓦片形式。Ganos的ST_AsPng方法提供了在数据库端将矢量数据按需动态渲染为栅格瓦片的功能。该功能提供了最基础的栅格符号化能力,更多面向一些不需要复杂符号化的轻量级场景,如数管系统中。

 

方法原型如下,更详细的参数描述可以参考官方文档

 

bytes ST_AsPng( cstring name, cstring tile, cstring style)

 

其中

  • name:金字塔表名。
  • tile:瓦片索引行列号,Z_X_Y的形式。
  • style:渲染样式。我们可以通过如下参数调节渲染效果:
    • point_size:点大小,单位为像素。
    • line_width:线宽,对线要素和面要素的外边框起作用,单位为像素。
    • line_color:线渲染颜色,对线要素和面要素的外边框起作用。前6位为16进制颜色,后2位为16进制透明度。
    • fill_color:填充颜色,对面要素起作用。
    • background:背景色。一般设置为FFFFFF00,即纯透明。

 

实际调用如下:

ST_AsPng('points_geom', '1_2_1','{"point_size": 5,"line_width": 2,"line_color": "#003399FF","fill_color": "#6699CCCC","background": "#FFFFFF00"}')

注:*左右滑动阅览

 

我们从矢量金字塔中获取到索引行列号为x=2,y=1,z=1的矢量瓦片,并将该矢量瓦片按照我们配置的样式渲染为栅格瓦片,返回PNG格式的图片。

 

3. 3 获取矢量瓦片

 

矢量瓦片是新兴的地图瓦片技术,具有在前端配置样式的灵活特性,使用WebGL渲染,效果也更加美观,Mapbox等地图框架可以方便的支持这一格式。使用Ganos的ST_Tile方法可以将矢量金字塔中的数据以矢量瓦片的形式提供。

 

方法原型如下,更详细的参数描述可以参考官方文档。

 

bytea ST_Tile(cstring name, cstring key);

 

其中

  • name:金字塔名。在本例中为表名_矢量字段名
  • key:瓦片索引行列号,Z_X_Y的形式。

 

我们只需要提供标准的TMS行列号和金字塔表的名称即可调用。

 

实际调用如下:

 

ST_Tile('points_geom', '1_2_1');

 

我们从矢量金字塔中获取到索引行列号为x=2,y=1,z=1的矢量瓦片,返回数据为标准的MVT格式。

 

04 实战案例

 

4.1 测试数据

 

我们准备两份矢量数据作为测试样例。

 

buildings表为面数据,数据总计1.25亿条,展现使用栅格瓦片的在线可视化效果。

 

gid|geom                                                                                                                                                                                                                                                           |
---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|1|MULTIPOLYGON(((-88.066953 34.916114 0,-88.066704 34.916114 0,-88.066704 34.91602 0,-88.066953 34.91602 0,-88.066953 34.916114 0)))                 2|MULTIPOLYGON(((-87.924658 34.994797 0,-87.924791 34.99476 0,-87.924817 34.994824 0,-87.924685 34.994861 0,-87.924658 34.994797 0)))                

注:*左右滑动阅览

 

points表为点数据,数据总计10.7万条,展现使用矢量瓦片的在线可视化效果。

 

id|geom                          |
--|------------------------------|1|POINT (113.5350205 22.1851929)|2|POINT (113.5334245 22.1829781)|

 

4.2 全栈架构

 

数据库-快显全栈架构包含数据库服务器、python服务端和用户端三个部分,全栈架构如下图所示。

 

4.3 服务端代码

 

为了代码简洁,更侧重于逻辑的描述,我们选择了Python(兼容Python3.6及以上版本)作为后端语言,Web框架使用了基于Python的Flask(使用pip install flask进行安装)框架,数据库连接框架使用了基于Python的Psycopg2(使用pip install psycopg2进行安装)。

 

值得一提的是,我们实现了最基础的功能,当Web服务自身的性能出现瓶颈时,可按不同的平台与框架进行优化,获得更好的响应性能。

 

我们在后端首先建立了矢量金字塔,其后分别实现了两个接口,矢量瓦片接口使用points表中的数据,栅格瓦片接口使用buildings表中的数据,并定义好样式,供前端直接调用。为了方便说明,后端代码同时提供了矢量栅格两个接口,实际使用时可以按需选择。

 

# -*- coding: utf-8 -*-
# @File : Vector.pyimport json
from psycopg2 import pool
from threading import Semaphore
from flask import Flask, jsonify, Response, send_from_directory
import binascii# 连接参数
CONNECTION = "dbname=postgres user=postgres password=postgres host=YOUR_HOST port=5432"class ReallyThreadedConnectionPool(pool.ThreadedConnectionPool):"""面向多线程的连接池,提高地图瓦片类高并发场景的响应。"""def __init__(self, minconn, maxconn, *args, **kwargs):self._semaphore = Semaphore(maxconn)super().__init__(minconn, maxconn, *args, **kwargs)def getconn(self, *args, **kwargs):self._semaphore.acquire()return super().getconn(*args, **kwargs)def putconn(self, *args, **kwargs):super().putconn(*args, **kwargs)self._semaphore.release()class VectorViewer:def __init__(self, connect, table_name, column_name, fid):self.table_name = table_nameself.column_name = column_name# 创建一个连接池self.connect = ReallyThreadedConnectionPool(5, 10, connect)# 约定金字塔表名self.pyramid_table = f"{self.table_name}_{self.column_name}"self.fid = fidself.tileSize = 512# self._build_pyramid()def _build_pyramid(self):"""创建金字塔"""config = {"name": self.pyramid_table,"tileSize": self.tileSize}sql = f"select st_BuildPyramid('{self.table_name}','{self.column_name}','{self.fid}','{json.dumps(config)}')"self.poll_query(sql)def poll_query(self, query: str):pg_connection = self.connect.getconn()pg_cursor = pg_connection.cursor()pg_cursor.execute(query)record = pg_cursor.fetchone()pg_connection.commit()pg_cursor.close()self.connect.putconn(pg_connection)if  record is not None:return record[0]class PngViewer(VectorViewer):def get_png(self, x, y, z):# 默认参数config = {"point_size": 5,"line_width": 2,"line_color": "#003399FF","fill_color": "#6699CCCC","background": "#FFFFFF00"}# 在使用psycpg2时,将二进制数据以16进制字符串的形式传回效率更高sql = f"select encode(st_aspng('{self.pyramid_table}','{z}_{x}_{y}','{json.dumps(config)}'),'hex')"result = self.poll_query(sql)# 只有在使用16进制字符串的形式传回时才需要将其转换回来result = binascii.a2b_hex(result)return resultclass MvtViewer(VectorViewer):def get_mvt(self, x, y, z):# 在使用psycpg2时,将二进制数据以16进制字符串的形式传回效率更高sql = f"select encode(st_tile('{self.pyramid_table}','{z}_{x}_{y}'),'hex')"result = self.poll_query(sql)# 只有在使用16进制字符串的形式传回时才需要将其转换回来result = binascii.a2b_hex(result)return resultapp = Flask(__name__)@app.route('/vector')
def vector_demo():return send_from_directory("./", "Vector.html")# 定义表名,字段名称等
pngViewer = PngViewer(CONNECTION, 'usbf', 'geom', 'gid')@app.route('/vector/png/<int:z>/<int:x>/<int:y>')
def vector_png(z, x, y):png = pngViewer.get_png(x, y, z)return Response(response=png,mimetype="image/png")mvtViewer = MvtViewer(CONNECTION, 'points', 'geom', 'gid')@app.route('/vector/mvt/<int:z>/<int:x>/<int:y>')
def vector_mvt(z, x, y):mvt=mvtViewer.get_mvt(x, y, z)return Response(response=mvt,mimetype="application/vnd.mapbox-vector-tile")if __name__ == "__main__":app.run(port=5000, threaded=True)

注:*左右滑动阅览

 

将以上代码保存为Vector.py文件,执行python Vector.py命令即可启动服务。

 

从代码不难推断,无论我们使用何种语言、何种框架,我们只需将矢量或栅格瓦片的SQL语句封装为接口即可实现完全相同的功能。相比发布传统的地图服务,借助Ganos的矢量金字塔功能实现在线可视化是更加轻量好用的选择:

 

  • 针对栅格瓦片,可以在通过改变代码进行样式控制,灵活性大大增强。
  • 无需引入第三方的其他组件,也不需要进行针对性优化,就有令人满意的响应性能。
  • 可以任意选择使用者熟悉的编程语言与框架,也无需复杂专业的参数配置,对非地理从业者更加的友好。

 

4.4 用户端代码

 

我们选用Mapbox作为前端地图框架,展示后端提供的矢量瓦片层和栅格瓦片层,并为矢量瓦片层配置了渲染参数。

 

为了方便说明,前端代码同时添加了矢量、栅格两个图层,实际使用时可以按需选择。

 

我们在后端代码的同一文件目录下新建名为Vector.html的文件,写入下列代码,在后端服务启动后,就可以通过
http://localhost:5000/vector访问了。

 

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title></title><linkhref="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/1.13.0/mapbox-gl.min.css"rel="stylesheet"/></head><script src="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/1.13.0/mapbox-gl.min.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script><body><div id="map" style="height: 100vh" /><script>const sources = {osm: {type: "raster",tiles: ["https://b.tile.openstreetmap.org/{z}/{x}/{y}.png"],tileSize: 256,},};const layers = [{id: "base_map",type: "raster",source: "osm",layout: { visibility: "visible" },},];const map = new mapboxgl.Map({container: "map",style: { version: 8, layers, sources },});map.on("load", async () => {map.resize();// 添加栅格瓦片数据源map.addSource("png_source", {type: "raster",minzoom: 1,tiles: [`${window.location.href}/png/{z}/{x}/{y}`],tileSize: 512,});// 添加栅格瓦片图层map.addLayer({id: "png_layer",type: "raster",layout: { visibility: "visible" },source: "png_source",});// 添加矢量瓦片数据源map.addSource("mvt_source", {type: "vector",minzoom: 1,tiles: [`${window.location.href}/mvt/{z}/{x}/{y}`],tileSize: 512,});// 添加矢量瓦片图层,并为矢量瓦片添加样式map.addLayer({id: "mvt_layer",paint: {"circle-radius": 4,"circle-color": "#6699CC","circle-stroke-width": 2,"circle-opacity": 0.8,"circle-stroke-color": "#ffffff","circle-stroke-opacity": 0.9,},type: "circle",source: "mvt_source","source-layer": "points_geom",});});</script></body>
</html>

注:*左右滑动阅览

 

4.5 矢量瓦片的动态效果

 

 

可以在前端调节不同效果。调整为新的图层参数后效果如下:

 

{"circle-radius": 4,"circle-color": "#000000","circle-stroke-width": 2,"circle-opacity": 0.3,"circle-stroke-color": "#003399","circle-stroke-opacity": 0.9,
}

 

 

4.6 栅格瓦片的动态效果

 

 

05 与PGADmin集成

 

PG数据库管理工具PGAdmin原生支持矢量数据的可视化,但因缺乏快显技术,仅能单对象显示或有限结果集显示,无法对大规模矢量数据进行畅快淋漓的全局浏览。我们将Ganos的矢量快显功能与PGAdmin集成,数据入库即可在线浏览全局,快速评估数据概况,大大增强了数据管理的使用体验。

 

06 总结

本文从稀疏矢量金字塔的原理与优势入手,介绍了如何利用Ganos实现在线可视化服务的各种功能,并最终通过百行代码实现了一个可以应对亿级数据的地图可视化服务。读者可以进一步在可视化基础上,利用PG/PolarDB Ganos的服务器端快速查询和分析能力进行对象属性查询、空间圈选、空间分析等更复杂功能。这就是Ganos所带来的大规模空间图形显示加速黑科技——稀疏矢量金字塔索引带来的变革。

作者:李鹤

原文链接

本文为阿里云原创内容,未经允许不得转载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/514191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

流批一体生产应用!Bigo 实时计算平台建设实践

简介&#xff1a; 本文由 Bigo 计算平台负责人徐帅分享&#xff0c;主要介绍 Bigo 实时计算平台建设实践的介绍 本文由 Bigo 计算平台负责人徐帅分享&#xff0c;主要介绍 Bigo 实时计算平台建设实践的介绍。内容包括&#xff1a; Bigo 实时计算平台的发展历程特色与改进业务场…

一部手机是否能用 7 年?苹果、三星、Google:三年差不多!

整理 | 苏宓出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;一部手机如果可以流畅地使用 7 年&#xff0c;是种什么样的感觉&#xff1a;有人说&#xff0c;这对于 iPhone 而言&#xff0c;或许会很轻松做到&#xff0c;但也会给一些平价的 Android 手机制造商带来…

五福背后的 Web 3D 引擎 Oasis Engine 正式开源

简介&#xff1a; Oasis 从开源走向新的起点&#xff0c;用 3D 化的交互和表达让世界变得更美好。 相信大家已经体验了今年支付宝五福的活动&#xff0c;无论是今年的五福首页还是打年兽游戏都是由蚂蚁互动图形引擎&#xff08;代号&#xff1a;Oasis Engine&#xff09;驱动的…

我用 Python 自制成语接龙小游戏,刺激!

作者&#xff1a;小小明原文链接&#xff1a;https://blog.csdn.net/as604049322/article/details/118154687本文为读者投稿在 https://github.com/pwxcoo/chinese-xinhua 项目中可以下载到中华成语的语料库&#xff0c;该项目收录包括 14032 条歇后语&#xff0c;16142 个汉字…

基于SLS构建RDS审计合规监控

简介&#xff1a; 数据库是企业业务的数据核心&#xff0c;其安全方面的问题在传统环境中已经成为泄漏和被篡改的重要根源。因此&#xff0c;对数据库的操作行为尤其是全量 SQL 执行记录的审计日志&#xff0c;就显得尤为重要。 背景 数据库是企业业务的数据核心&#xff0c;其…

云效DevOps实践-如何基于云效实现测试自动化集成和分析

简介&#xff1a; 对于现代软件研发来说&#xff0c;持续、快速、高质量、低风险地交付需求特性&#xff0c;是业务对研发的主要诉求。而要做到这一点&#xff0c;除了要有良好的架构设计、卓越的工程能力&#xff0c;快速可靠的测试反馈也是其非常重要的一环&#xff0c;达到这…

spring 使用其他类protected方法_Java操作bean、属性、方法的使用工具类

在实际的项目开发中&#xff0c;反射操作类的实例、属性赋值、执行方法是常规的操作&#xff0c;虽然spring提供了比较完整的API来执行上述操作&#xff0c;不过在实际的应用中&#xff0c;spring的函数隐藏比较深&#xff0c;比较分散&#xff0c;小伙伴们可能懒得花时间去寻找…

2021年阿里云采购季大促主会场全攻略

在疫情的影响下&#xff0c;企业都在谋求各种转机&#xff0c;探寻各种转型之路&#xff0c;为助力企业复工复产低成本上云&#xff0c;日前阿里云开年采购季优惠活动于3月1日正式开启。 从主会场页面来看&#xff0c;活动分为三个阶段&#xff1a; 3月1日-3月16日&#xff1a…

应云而生,幽灵的威胁 - 云原生应用交付与运维的思考

简介&#xff1a; 过去的 2020 是充满不确定性的一年&#xff0c;但也是充满机遇的一年。突发的新冠疫情为全社会的数字化转型按下加速键。云计算已经不再是一种技术&#xff0c;而是成为支撑数字经济发展和业务创新的关键基础设施。在利用云计算重塑企业 IT 的过程中&#xff…

技术干货 | mPaaS 小程序高玩带你起飞:客户端预置小程序无视网络质量

简介&#xff1a; 弱网拉包无障碍&#xff0c;深度提升用户体验 传统的小程序技术容易受到网络环境影响&#xff0c;当网络质量不佳时可能导致拉取不到小程序包的情况。通过预置小程序&#xff0c;即可规避该问题。本文介绍了预置小程序的原理和预置小程序的实现过程。 什么是预…

Delta Lake在Soul的应用实践

简介&#xff1a; 传统离线数仓模式下&#xff0c;日志入库前首要阶段便是ETL&#xff0c;我们面临如下问题&#xff1a;天级ETL任务耗时久&#xff0c;影响下游依赖的产出时间&#xff1b;凌晨占用资源庞大&#xff0c;任务高峰期抢占大量集群资源&#xff1b;ETL任务稳定性不…

亚马逊云科技中国线上峰会开幕,发力汽车产业链、少年人工智能等

亚马逊云科技于9月9日-14日举办以“构建新格局 重塑云时代”为主题的中国线上峰会&#xff0c;推出涵盖行业视野、技术创新、开发者和开源、云安全、以及人工智能的5大主题演讲、覆盖云计算各细分领域的8大技术分论坛&#xff0c;以及汇聚各行业上云趋势及创新实践的10大行业分…

【产品能力深度解读】连续入围Gartner魔力象限的Quick BI有何魔力?

简介&#xff1a; 国际权威分析机构Gartner发布2021年商业智能和分析平台魔力象限报告&#xff0c;阿里云Quick BI再度入选&#xff0c;并继续成为该领域魔力象限唯一入选的中国企业。 Quick BI凭借在增强分析能力上的持续投入、数据中台矩阵化产品优势和电商行业的专业度&…

mysql的json函数与实例_Mysql实例详解Mysql中的JSON系列操作函数

《Mysql实例详解Mysql中的JSON系列操作函数》要点&#xff1a;本文介绍了Mysql实例详解Mysql中的JSON系列操作函数&#xff0c;希望对您有用。如果有疑问&#xff0c;可以联系我们。MYSQL必读前言MYSQL必读JSON是一种轻量级的数据交换格式,采用了独立于语言的文本格式,类似XML,…

256变4096:分库分表扩容如何实现平滑数据迁移?

简介&#xff1a; 本文作者就一个高德打车弹外订单系统进行了一次扩分库分表和数据库迁移。 一、 背景 2020年&#xff0c;笔者负责的一个高德打车弹外订单系统进行了一次扩分库分表和数据库迁移。该订单系统整体部署在阿里云上&#xff0c;服务使用阿里云ECS部署&#xff0c;…

OpenYurt 如何 “0 侵入” 攻破云边融合难点

简介&#xff1a; 随着 5G、IoT、直播、CDN 等行业和业务的发展&#xff0c;越来越多的算力和业务开始下沉到距离数据源或者终端用户更近的位置&#xff0c;以期获得很好的响应时间和成本&#xff0c;这是一种明显区别于传统中心模式的计算方式——边缘计算。 随着 5G、IoT、直…

Python - 深夜数据结构与算法之 Graph

目录 一.引言 二.图的简介 1.Graph 图 2.Undirected graph 无向图 3.Directed Graph 有向图 4.DFS / BFS 遍历 三.经典算法实战 1.Num-Islands [200] 2.Land-Perimeter [463] 3.Largest-Island [827] 四.总结 一.引言 Graph 无论是应用还是算法题目在日常生活中比较…

Docker Desktop宣布收费;腾讯7月已申请注册WECHAT CLOUD商标;MongoDB成为当前最具价值开源软件公司...

NEWS本周新闻回顾Docker Desktop 宣布收费近日 Docker 官方宣布一项新的动作&#xff0c;即将产品订阅划分为个人、专业、团队和商业不同版本。如果企业规模在 250 名员工以上或年收入超过 1000 万美元的公司想要使用 Docker Desktop&#xff0c;那么必须使用付费订阅。付费订阅…

Serverless 2.0,鸡蛋还是银弹?

简介&#xff1a; 本篇旨在介绍 Serverless 如今应用到应用&#xff08;非病句&#xff09;的各种困境&#xff0c;以及帮助用户如何去规避一些问题&#xff0c;提前了解方向。 浪潮 从 2014 年 Serverless 冒头至今&#xff0c;已经有无数的勇士在前面探路&#xff0c;阿里、…

基础组件完善的今天,如何通过业务组件提效?

简介&#xff1a; 无论是在前端刀耕火种的 jQuery/YUI 时代&#xff0c;还是到现在基于数据驱动 UI 的 React/Vue 时代&#xff0c;物料/组件一直是前端永恒的话题。基于大量重复逻辑的封装可以很显而易见地提升前端 UI 的构建效率&#xff0c;简单而直接&#xff0c;因此无论技…