1. 为什么我劝你别跳过SVG直接玩Canvas
这几年做前端和工控相关的项目,我经常遇到一种情况:新来的同事一上来就抱着Canvas猛啃,觉得能画图就是王道。等真到做组态画面、设备监控面板、流程图这类需求时,才被需求方一句话问住——“这个阀门我想在浏览器里点一下变颜色,再做个小动画,Canvas你怎么搞?”
这一下就尴尬了。Canvas是一场即时绘画,画完了就没有“图形”的概念,只有一堆像素。你想给某个阀门加个点击事件,要么自己维护一堆坐标和命中检测,要么硬着头皮造轮子。而SVG天生就是一个可交互、可动画、可查询的图形文档结构——每个图形都是独立的节点,有ID、有属性、有事件,甚至可以被CSS直接控制。
我劝你认真学SVG,不是因为它比Canvas“高级”,而是因为在**“图形需要被理解、被操作、被复用”**的场景里,SVG是最省心、最贴近业务直觉的方案。工控组态软件里的通用图库、工业监控大屏上的设备状态图、流程图中每条线的动效,以及那些免费下载的svg字体图标文件,本质上都依赖SVG这套文档化描述能力。
还有一个非常现实的原因:SVG的生态已经非常成熟。矢量编辑工具导出SVG、图标库直接用SVG代码、自动化脚本生成SVG素材、甚至visio这样的传统桌面软件也能通过插件或转换工具输出SVG。也就是说,SVG不只是浏览器里的绘图技术,它已经是一条贯穿设计、工程、嵌入式WebView的完整工作流。如果你只在“用Canvas画个粒子背景”这个层面打转,你实际错过的是一个能接入几乎所有可视化系统的通用技能。
这篇文章不会从“SVG历史”这种废话讲起。我会按我真实工作中的技能成长路径来写:先搞懂坐标系和基础语法,然后深入路径、动画、交互,再结合工控图库、字体资源、visio转换、Windows缩略图这些高频搜索词一一把坑和解决方案讲透。只讲能落地的,不堆概念。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. SVG基础语法:从viewBox到路径的认知升级
SVG入门的第一道坎不是标签,而是坐标系。很多人第一次写SVG,直接写<svg width="300" height="200">,然后画出来的图形总是不在自己预期位置。这是因为没有理解viewBox和width/height是两套坐标系在做映射。
2.1 viewBox到底怎么理解
width和height定义的是SVG在页面上的显示尺寸,单位通常是px。viewBox定义的是图形内容自己的“世界坐标”。你可以把viewBox想成摄像机拍到的区域,width/height是想把这个区域投放到多大的屏幕上。
举个例子:
svg复制<svg width="300" height="200" viewBox="0 0 600 400">
<rect x="0" y="0" width="100" height="50" fill="blue" />
</svg>
这段代码里,图形实际是在一个600×400的坐标系中画的,rect的宽度是100个“世界单位”。但因为摄像区域是整个600×400,而这个区域被拉伸到了300×200的屏幕上,所以rect画出来后恰好占屏幕宽度的1/6,也就是50px。
理解了这一点,你就能明白为什么很多工控图库里的SVG都要强调视图区域统一。比如一个电机图标,如果画在0 0 100 100的viewBox里,另一个画在0 0 200 200里,其实图形尺寸占比可能完全一样,但放到同一个布局中,命名空间不同,缩放不一致,就会出现一个大一个小、位置错乱的混乱效果。所以我在整理通用图库时会强制所有图标使用同一个viewBox范围,比如统一为0 0 24 24,这样CSS里一个width: 24px就能保证所有图标视觉大小统一。
2.2 基本图形:把矩形、圆、线条玩明白
SVG的基础图形就那几样:rect、circle、ellipse、line、polyline、polygon。真正用起来要注意几点:
rect支持圆角属性rx和ry,这在画进度条、按钮底框时非常实用。circle的圆心和半径两个参数就能搞定圆形,工控里的指示灯基本都是这种。polyline和polygon的区别在于后者会自动闭合路径。画流程图里的菱形判断框,用polygon加坐标点即可,不用处理首尾闭合。
如果是做组态软件里的简单图元,我会建议尽量用基础图形组合,不要一上来就用path。原因很简单:基础图形可读性强、属性语义明确,后续用脚本批量改颜色、改尺寸时,直接操作属性就可以了。而path是一串坐标和命令的集合,想从代码层面去精确识别“这个path是不是一个圆”就需要做解析,成本高得多。
2.3 path路径:所有复杂图形的底座
path的d属性是SVG里最劝退新人的地方。一堆M、L、C、S、Q、A的字母加数字,看着像天书。但说透了其实很简单:就是一支画笔从某个点开始,按你给的命令移动和画线。
我建议的学习顺序是先只掌握四个命令:
M x y:画笔移动到某个点,不画线。L x y:从当前点到目标点画一条直线。H x和V y:保持x或y不变,画水平/垂直线。Z:闭合路径,从当前位置画直线回起点。
只要你会这四个命令,矩形、三角形、折线、多边形都能画。圆角需要二次贝塞尔曲线Q,弧线需要A,这些可以等用到再查。不要一开始就追求背下所有命令。
实测下来,想快速理解path,最好的办法是手动画一个箭头的路径。箭头由一条横线和两条斜线组成,用M定位起点、L画线、Z闭合,画出来后你对坐标系的掌控感会立刻上一个台阶。
2.4 text与use:容易被忽略的实用标签
text标签用来在SVG中绘制文本。它的x、y属性定义文本基线位置,text-anchor控制水平对齐方式(start/middle/end),dominant-baseline控制垂直对齐方式。在工控图里,要给设备加中文标签,这两个属性必须配合好,否则文字总是偏上或偏下,怎么调都对不齐。
use标签是我个人非常依赖的一个工具。它相当于SVG里的“引用”,可以把一个已经定义好的图形<g id="xxx">,通过<use href="#xxx" x="10" y="20" />在多个位置复用。这解决了SVG代码重复的问题,也让复杂图元具备“模板化”能力。我在做一套工控图库时,先定义好电机的<g>,然后十台设备都通过use引用同一份图形,当需要统一修改时,只需要改定义处一处即可。
3. 工控组态中的SVG图库:为什么行业里都在囤图
“工控组态软件通用svg图库合集”这个搜索词出现的频率很高。实际上,现在很多组态软件和上位机系统已经全面转向Web前端渲染,而SVG作为矢量格式,天然适合工控画面中的设备图元、管线、阀门、仪表盘这些元素。
3.1 组态软件的显示原理
我最初帮人调试组态画面时有个困惑:为什么组态软件导出的画面文件打开后,里面全是SVG?后来明白了,组态软件的“图元”本质上就是一组带坐标和属性的图形对象。传统组态用私有格式存储,但一旦要跨平台展示、在浏览器里操作,SVG就成了最通用、最不依赖厂商的中间格式。
换句话说,你在组态软件里拖一个“泵”出来,它内部可能就是一段SVG代码:一个圆形代表壳体、一个三角形代表流向、几根线代表管道连接点。你要做的,是找到这些元素对应的特征,才能在运行时动态改变颜色或状态。
3.2 一个好图库的标准
按我这些年整理和实际使用的经验,通用的工控SVG图库至少要满足五个条件:
- 坐标系统一:所有图元都在同一个viewBox范围内,比如0 0 100 100,这样缩放时不会出现比例失衡。
- 状态位分离:同一个设备至少要有“运行/停止/故障”“开/关”这种状态区分,通常用颜色或不同子元素来控制,方便程序动态切换。
- 命名规范清晰:每个图层、每个分组都要有明确的id,比如
pump-body、valve-handle,不然后续用脚本操作时根本找不到目标。 - 锚点和引脚位置约定:工控画面里设备之间要连线,所以SVG图元最好定好进出线接口的位置,比如左侧中心、右侧中心各留一个连接点,方便程序自动布线和对齐。
- 代码量精简:不要导出那种带一堆垃圾节点和嵌套
<g>的“设计稿SVG”,最好经过清理,保持结构尽量扁平。
如果你刚开始建自己的图库,我强烈建议从“单一设备模板”入手。先在矢量工具里画好一个泵,导出SVG后,自己手动清理代码,删掉不必要的分组和属性,然后定义好运行色和停止色。这个流程走一遍之后,后续再画其他设备就有标准可依了。
3.3 从零建一套自己的图库:实操流程
我分享一个我自己在建图库时用的步骤:
- 在矢量工具里按统一画布尺寸(如100×100)绘制设备外观,把颜色只保留黑白灰,方便程序上色。
- 导出SVG,注意导出选项里选择“包含id”“不包含样式表内联”等,尽量精简。
- 用文本编辑器打开SVG,把无关的metadata、defs清掉,然后给每个需要动态控制的元素加上有意义的id。
- 用脚本批量生成不同状态版本。比如读取一个模板,把
fill="#333333"替换为fill="#00cc66"生成运行态,替换成#ff0000生成故障态。 - 把生成的图元放入一个文件夹,统一命名,比如
pump-run.svg、pump-stop.svg、valve-open.svg、valve-close.svg。
这套方法的最大好处是:当图库到一个规模时,新增设备只需要画基础图形+写几条替换规则,不需要每个状态都手工处理一遍。
4. SVG动画与交互:让静态图形“活”起来
如果只把SVG当静态图用,那它的价值就已经降低了60%。SVG最迷人的是图形可以和用户、和业务状态联动起来。工控画面里,液位的波动、设备的状态闪烁、管道的流动箭头,这些都是很典型的SVG动画应用。
4.1 CSS动画:最易上手的控制手段
SVG元素本质上也是DOM节点,所以CSS可以装饰它们。你可以直接用transform、fill、opacity这些属性写transition和animation。
比如一个报警灯闪烁,我惯用的写法是把灯的状态绑定到一个class上,再给class写动画:
css复制.alarm-light {
fill: #ff4d4f;
animation: blink 1s infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.2; }
}
用CSS的好处是逻辑清晰、易维护,而且可以通过切换class来切换动画状态,不与业务代码强耦合。
但这里有一个非常关键的坑:直接改SVG的transform时,CSS的transform属性与SVG元素的x/y/width/height属性是两个不同的体系。在SVG内部,传统上transform是作为属性出现的,而CSS里也定义了transform属性,二者混用时会相互覆盖,经常导致你改了x属性没效果,或者旋转中心不在预期位置。我的建议是:能用CSS变换属性就用CSS变换,不要把坐标位移和CSS transform混着写,避免给自己挖坑。
4.2 SMIL动画:工控场景里的老朋友
SVG里原生的<animate>、<animateTransform>等标签提供了一套声明式动画方案,叫SMIL。它最大的价值是:不需要JS干预,靠标签就能描述动画。
我在做管道流动效果时经常用它:
svg复制<circle r="4" fill="#1890ff">
<animate attributeName="cx" values="0; 100" dur="2s" repeatCount="indefinite" />
<animate attributeName="cy" values="50; 50" dur="2s" repeatCount="indefinite" />
</circle>
一小段蓝色圆点沿着管线来回移动,视觉上就形成了“流体正在流动”的效果,非常直观。SMIL的attributeName可以指定任意可动画属性,values用分号分隔关键帧值,dur是持续时间,repeatCount是循环次数,begin可以设置延迟或从某个事件触发。
不过SMIL也有两个坑要注意:一是它在IE时代兼容性很差,好在今天的主流浏览器基本都没问题;二是SMIL动画的控制能力较弱,如果你想在某个业务事件到来时暂停动画、切换动画方向,就比较费劲。所以我的选择标准很简单:简单循环动画用SMIL,需要业务状态驱动时用CSS,需要复杂时间线和交互时再上JS。
4.3 事件绑定与坐标转换:点击设备图元的细节
SVG里点击事件的绑定和HTML元素差别不大,addEventListener('click', fn)就行。但第一个难点是:你想知道用户点击到了SVG里的哪个坐标。
SVG的getScreenCTM()方法可以拿到当前SVG坐标到屏幕坐标的变换矩阵。但更稳妥的做法是使用DOMPoint去做矩阵变换:
javascript复制const svg = document.querySelector('svg');
const point = svg.createSVGPoint();
point.x = event.clientX;
point.y = event.clientY;
const transformed = point.matrixTransform(svg.getScreenCTM().inverse());
// transformed.x 和 transformed.y 就是SVG世界坐标系里的点击位置
这样做的好处是,即使SVG被CSS缩放、平移过,点击坐标换算到SVG世界坐标依然准确。
另一个细节是:给SVG元素绑定事件时,如果图形很小,用户手指或鼠标很难点中。通常我会在可点击元素底下垫一个透明的大矩形,用来扩大命中区域,颜色设为fill="transparent",然后用pointer-events: all让它可以响应鼠标事件。这个技巧在工控触摸屏场景里尤其重要——现场操作工可不会用鼠标精确去点一个8像素的圆。
5. SVG字体与文字渲染:彩色字体和图标库背后的技术
搜索词里出现“notocoloremoji svg字体免费下载”这类需求,其实牵涉到一个更深层的知识点:SVG不仅能画形状,也能承载字体信息。而现在很多图标库、emoji字体之所以能做出来,背后都有SVG的参与。
5.1 彩色字体是怎么来的
传统字体在计算机里存的是字形轮廓,渲染时用单一颜色填充,所以默认字体的颜色是固定的。彩色字体则在字体文件中直接内置了颜色、渐变甚至多个图层,比如苹果设备上的彩色emoji。Noto Color Emoji就是一个典型:它用COLR/CPAL或CBDT/CBLC等OpenType技术来存储彩色字形,而某些版本的实现路径就是从SVG字形转换而来的。
换句话说,“svg字体免费下载”下载到的那些资源,很多就是用SVG描述每个字形的轮廓,然后打包进字体文件。这种做法的好处是字形可以保持矢量清晰度,放大不糊,同时还能带上颜色信息。
5.2 网页里用SVG字体的几种方式
如果你只是想在网页上用某个SVG图标字体,有几种集成方案:
- 字体文件方案:下载字体文件,
@font-face引入,然后通过十六进制字符码或专用class来显示图标。这种方案性能好、兼容性也不错,但缺点是彩色字体支持不彻底,要访问字形细节比较麻烦。 - 内联SVG方案:把图标直接作为SVG代码嵌入页面或组件中,用CSS控制颜色和尺寸。这是我个人偏好的方式,尤其适合图标数量不多的情况,好处是文件体积小、可单独控制颜色,还能用事件。
- SVG雪碧图方案:把所有图标集中放到一个SVG文件里,每个图标一个
<symbol>,页面里用<use>引用。这种方案在早期前端项目里非常流行,比如Font Awesome的SVG版就是基于这个思路。
5.3 字体图标的短板与替代思路
字体图标从2015年前后火过一轮,现在反而慢慢被内联SVG取代了。原因很直接:字体图标受字体渲染规则影响,多色、渐变这类表现力做不出来;而且不同系统对自定义字体的抗锯齿处理不同,图标在Windows和macOS上的观感经常不一致。
我现在处理图标类需求,默认方案是“SVG组件化”:每个图标是一个独立的SVG组件,大小、颜色、描边粗细通过属性控制。这样既不会因为加载一个字体文件的体积影响性能,也不受字体渲染差异影响,还能在图标上直接挂交互事件。你下载到的那些svg字体,如果内部确实是SVG定义的,完全可以直接提取出来转成组件使用,这也是“svg字体免费下载”这类资源最常见的二次加工方式。
6. 工具链:从AI/Visio到可维护SVG的转换路径
平时大家接触SVG最多的情况,不是自己手写代码,而是从工具里导出。工控领域尤其常见的是用Visio画电气图、工艺图,然后想转成SVG放到Web页面上做交互。这一步看似简单,实际操作时问题一大堆。
6.1 Visio修改SVG的典型问题
Visio本身支持“另存为SVG”,但导出的文件往往有这样几个问题:
- XML命名空间混乱:Visio会往SVG里塞一大堆自定义命名空间,浏览器显示没问题,但如果你用脚本去querySelector,经常匹配不到想要的元素。
- 样式内联严重:每个图形都会带上
fill="#ff0000" stroke="#000000"这类内联样式,导致你无法用外部CSS统一覆盖颜色。 - 冗余结构:一次简单的复制粘贴或对齐操作,可能会产生意料之外的嵌套
<g>、空分组、transform偏移。
解决办法有两种。一种是在Visio里尽量简化绘图,导出前把不必要的辅助线、标注、背景层全删掉;另一种是导出SVG后做一个清理流程:用文本编辑器或脚本去掉多余的命名空间声明,删掉空分组,把内联样式批量改成class引用。
我实测过一条靠谱的路径:Visio导出普通SVG → 用SVGO工具清理优化 → 再手动调整需要交互的元素加id。整个流程不难,但能让一个300KB的Visio导出文件降到30KB以内,而且结构可读性大幅提升。
6.2 其他矢量工具的导出设置
如果你用的是Figma、Sketch、AI这类设计软件,导出SVG时也要注意选项差异:
- Figma:导出时选择“仅当前图层”“包含id”“不包含样式”等选项,可以显著减少多余代码。Figma导出的SVG通常比较规范,偶尔会把圆角矩形展开成path,结构相对可控。
- Adobe Illustrator:导出SVG时有一个“SVG选项”面板,可以选择CSS属性、小数位数、minify等。建议把小数位数设为2或3,否则某些坐标会变成一长串小数点,文件体积膨胀。
- Inkscape:开源工具,默认导出的SVG代码中会含有很多Inkscape自己的命名空间和元数据,必须手动清理,或者用SVGO过一遍。
其实这些工具的默认导出结果,都算不上“可维护SVG”,绝大多数都需要二次清理。这也是我建议所有想深入SVG的人,至少要学会用SVGO的原因。
6.3 SVG代码清理:SVGO是必备工序
SVGO(SVG Optimizer)是一个基于Node的命令行工具,专门用于压缩和清理SVG文件。它能移除无用的命名空间、合并路径、降低小数精度、删除隐藏元素、清理注释等。
我的日常工作流是:
bash复制npx svgo -f ./svg-src -o ./svg-dist --config svgo.config.js
配置文件里可以指定保留哪些有意义的id,以及是否需要把样式转成属性:
javascript复制module.exports = {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
cleanupIds: {
preserve: ['pump-body', 'valve-handle']
}
}
}
}
]
}
在跑SVGO之前,我会先把需要交互的元素id处理好,避免清理时被优化掉。清理后别忘了实际在浏览器里预览一遍,因为有些极端的合并优化可能会改变视觉细节。
6.4 Windows缩略图不显示SVG的解决办法
“svg缩略图 windows”这个问题我经常被问到。Windows资源管理器默认不显示SVG文件的缩略图,只显示一个通用图标,原因很简单:Windows的预览引擎没有内置SVG解码器。
市面上有一些免费的Shell扩展可以解决,安装后资源管理器就能预览SVG缩略图。但要注意安全,只从官方或口碑好的站点下载。如果你不想装任何第三方工具,也有一个临时办法:把SVG批量转成PNG后,在预览视图里看PNG。这虽然绕了一圈,但在不需要每天浏览大量SVG的场景下也够用了。
还有一个细节:如果SVG文件名是中文或者含有特殊字符,某些预览插件会失效。我习惯在建图库时统一使用英文+数字的命名,既避免插件兼容问题,也方便后续脚本处理。
6.5 批量修改SVG的自动化脚本
当图库数量上百之后,手工编辑已经完全不可行了。我会用Node.js写一些简单的脚本来做批量替换。比如把所有#333333替换成#444444、把所有stroke-width="1"改成stroke-width="1.5"、把viewBox从0 0 48 48换成0 0 24 24。这些操作的关键是正则表达式要写得仔细,最好先把脚本跑在一个副本上,再人工抽检几个文件确认结果。
脚本化操作是SVG技能进阶的重要分水岭。你不需要每次都打开矢量工具去改图,而是可以通过文本处理、批量规则、代码生成的方式管理整套图库。这也和工控组态里“一个图元库支撑多个项目”的思路完全吻合。
7. 实际项目复盘:SVG在组态画面里的完整落地过程
写了这么多理论,我用一个真实的项目流程把前面所有知识点串联起来,让大家看到SVG技能在一个实际任务中是怎么层层发挥作用的。
这个项目是一个小型水处理系统的监控画面,需要在Web端展示泵、阀门、液位计、管道,并且根据PLC传来的数据实时改变状态。整体流程分为四个阶段。
7.1 阶段一:整理图元库
我先从已有素材库里翻出泵和阀门的SVG图元,然后逐一检查viewBox是否统一、id是否规范、状态位是否完整。有发现id带数字后缀、fill写死成具体颜色的问题,统一做了规范化。图元库整理完成后,所有元素都具备三个状态:正常运行(绿色)、停止(灰色)、故障(红色闪烁)。
这个阶段我用到了前面说的SVGO和批量脚本。大概花了半天时间,把几十个图元全部规范化,工作量没有想象中那么大。
7.2 阶段二:搭建画面布局
画面布局直接用HTML+CSS完成。把SVG当作普通的DOM元素嵌入布局中,用flex布局排列各个区域。工序流程从左到右排列,管道以线状图形连接各设备,并用一个专门的管道SVG承载流动动画。
在这一步最容易踩的坑是:管道SVG和设备SVG的坐标系不一致,导致连线的起终点位置对不上。我的解决方法是:所有设备SVG都在其内部定义好连接点,页面布局时再通过CSS绝对定位或固定边距来对齐。这样虽然多了一步换算,但整体逻辑清晰,后续维护不会因为微调布局而整体崩掉。
7.3 阶段三:绑定数据和交互
PLC通过WebSocket定时推送设备状态。前端收到数据后,根据设备id找到对应的SVG节点,切换class来改变颜色、运行状态和闪烁动画。同时,操作工点击某台设备时,页面弹出设备详情浮层,展示电流、频率、温度等参数。
这里有个关键细节:设备的状态切换要尽量用class切换,而不是直接改fill属性。因为class可以统一管理多属性变化,包括fill、opacity、stroke等,而且切换动画也更容易控制。如果你在代码里直接setAttribute('fill', '#ff0000'),虽然也能变色,但后续如果要加渐变动画或状态回退,就非常难维护。
7.4 阶段四:性能调优和兼容性处理
上线前我做了几项性能优化:
- 移除离线不需要的动画:部分设备在“暂停”状态下不需要流动动画,用class控制动画暂停,减少不必要的渲染。
- 限制重绘范围:SVG中某个小元素颜色变化时,浏览器会局部重绘,一般不会影响整体。但如果SVG特别复杂、节点很多,建议把每个设备拆成独立的SVG,而不是全部画在一个大SVG里。
- 字体渲染统一:现场操作系统是Windows,中文标签如果不做处理容易发虚。我直接把中文标签用
<text>绘制,并指定font-family为系统默认黑体类字体,实际显示比依赖外部字体加载的方案稳定得多。
兼容性方面,由于组态电脑是老旧工控机,浏览器版本偏低,我尽量避开了较新的CSS特性,比如gap在flex布局中的支持就不太可靠,改用了margin做间距。这些细节看着很小,但在现场如果出了问题,排查成本远高于开发时的规避成本。
8. SVG学习路线的最后建议
文章最后,我分享一点个人体会。很多人在学SVG时容易陷入两个极端:要么只学命令语法,背了一堆字母却不知道在真实需求里怎么用;要么只会在工具里“画完导出”,出了问题完全看不懂代码。真正能把SVG用起来的人,其实都是两条腿走路的:会从代码层面理解SVG,也知道怎么从工具里拿到相对干净的结构。
如果你是从零开始,我的建议顺序如下:
- 先花三天手写基础图形和path,做到能不看文档画出矩形、圆、三角形、箭头。
- 再花一周练习CSS控制SVG,包括hover变色、点击切换、简单的粒子动画。
- 然后选一个真实场景,比如做一组设备状态图标,建一个小型图库,走一遍“设计→导出→清理→脚本批量处理→在页面中引用”的完整流程。
- 最后再根据实际项目需要,深入学习交互事件、坐标转换、动画控制和跨端兼容问题。
不要一开始就追求把SVG规范全部背下来,那些命令和属性是以备查用的,不是背给面试官听的。你需要的是建立一种直觉:拿到一个可视化需求,知道用SVG能不能做、用什么标签做、怎么把图形和业务状态联系起来。这种直觉,只有通过实际动手处理那些“导出的SVG没法用”“id对不上”“动画不生效”的麻烦,才能真正建立起来。
如果你在工控、可视化、前端界面里经常和图形打交道,SVG是一个非常值得长期投入的技能点。它不性感,不像WebGL那样炫酷,但就是能在无数现实场景里帮你稳定地解决问题,而且这套技能会一直随着你处理过的图元、踩过的坑、写过的脚本不断累积下去。
