尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

技术栈全景图:从AGV到跨端开发的决策地图

技术栈全景图:从AGV到跨端开发的决策地图 做技术这行时间越长越觉得“技术栈”这三个字不是贴在简历上的一串名词而是你多年踩坑、选型、被业务倒逼着成长后沉淀下来的一套方法论。“我的技术栈全景图”听起来像个个人总结其实我更愿意把它理解成一张决策地图——每一个技术选型背后都有一串“当时为什么这么选”的故事。这篇东西不是给你背知识点的是把我自己这些年整理技术栈、画全景图的方式以及几个典型领域工业AGV、uniapp小程序、Electron桌面端的具体实践原原本本拆开来讲。不管你是刚入行的新人还是想系统性梳理自己知识体系的资深开发都能从里面找到能直接用的东西。1. 技术栈全景图的构建逻辑1.1 画全景图的三个维度大部分人整理技术栈是从“我会什么”出发把语言、框架、工具按熟练度排个序。我试过这个做法结果发现它除了让你在面试时背得更流利之外对实际做项目几乎没有指导意义。后来我把整理方式彻底换掉了改成从三个维度来审视自己广度、深度、场景绑定度。广度指你接触过多少不同类型的技术领域。注意是“领域”不是“框架”数量。比如你用过Vue也用过React这算一个领域里的两个分支但你写过小程序、搞过桌面端、又碰过嵌入式底层这才算三个领域。广度的价值在于视野遇到一个陌生问题你能判断出“这个问题在别的领域早就有成熟方案了”。深度指在某个具体技术方向上的掌握程度不是“用过”就算而是能说清楚原理、能定位底层问题、能在这套方案上做出别人做不出的性能优化。场景绑定度这个维度最容易被忽略。同一个技术栈在电商高并发场景、在AGV调度这种实时性要求高的工业场景、在内部工具型桌面应用场景选型和侧重是完全不一样的。技术栈从来不是孤立的它必须绑定场景才有意义。我画全景图的时候会把每个技术点放到一个三维坐标里去定位。比如“Electron”在我的图里是“广度中等、深度较深、场景绑定在跨平台桌面工具”而“uniapp”是“广度较宽、深度中上、强绑定小程序与跨端业务”。这么一整理你马上会发现自己真正的短板在哪下一步该补什么也就清楚了。1.2 怎么把技术栈“画”出来而不只是列出来“全景图”这个词容易让人误解成一张静态的、好看的架构图。我一开始也是这么干的画了一个硕大无比的分层结构图从网络层一直画到业务层看起来像卖课用的宣传海报。后来有一次拿这张图去跟新同事讲项目被一个很基础的问题问住了——他问我“你这里写的MQTT是客户端用的还是服务端用的”我才意识到我的全景图画的是别人的技术栈不是我的。从那以后我改用一种更实用的方式以项目为锚点画“技术栈-场景-产物”的映射关系。每画一个项目我会列出三样东西。第一是“这个项目解决什么问题”一句话说清楚业务价值。第二是“为了实现它我用了哪些关键技术”这里只列真正承担核心职责的不会列出所有依赖库。第三是“每个关键技术的选型理由与当时的备选方案”这个是最重要的因为它留住了决策上下文。这么画出来的全景图不会好看但它是活的每个线条都对应着你真实的项目经验拿去跟别人讲能讲出深度而不是背菜单。我自己的整理节奏是每隔半年重新过一遍。半年周期刚好能覆盖一到两个完整项目的生命周期技术栈变化量也比较可控。每次review的时候我会重点看三个问题有没有哪个技术我画了上去但其实已经半年没碰过了有没有哪个项目里的技术选型在事后复盘时觉得是个败笔有没有哪个新项目引入了新技术但全景图还没更新这三个问题能逼着你诚实地面对自己的技术栈而不是沉浸在“我会的东西很多”的错觉里。2. 工业场景实战AGV技术栈的显性与隐性分层2.1 AGV技术栈里容易被忽视的“隐性层”先解释一下背景AGV是自动导引运输车Automated Guided Vehicle的缩写也就是工厂仓库里那些沿着固定路线跑的无人搬运机器人。很多人一听“AGV技术栈”就觉得全是机械、底盘、电机控制纯纯的硬件领域。实际做了之后你会发现这套技术栈的复杂度远超想象而且它的分层逻辑非常值得借鉴。从整体上看一套AGV系统的技术栈可以粗分为四个大的层次。感知层是车对外界的“眼睛”涉及激光雷达、视觉相机、IMU惯性测量单元这些传感器以及SLAM建图与定位算法比如业界常用的cartographer、gMapping还有朝向稀疏特征场景优化的VINS系列。决策与调度层负责“车接下来该干什么”包含单车的路径规划、避障策略以及多车协同的调度算法任务分配、交通管制、死锁检测都是这个层的重头戏。执行与控制层把决策指令变成真实的电机转动和转向动作涉及运动学模型、PID/MPC控制器还要处理不同驱动方式差速、舵轮、麦克纳姆轮带来的控制差异。信息与运维层则承担和上层系统WMS仓库管理系统、MES制造执行系统的对接以及车辆状态的聚合和大屏监控。前三个层属于“显性技术栈”很多文章都会讲。我更想说的是那些隐性技术栈——它们不出现在架构图的主流程上但缺了它们系统根本跑不起来。第一个是时间同步机制。AGV上通常有激光雷达、相机、编码器、IMU等多种传感器它们各自的数据频率不一样时钟源也是独立的。如果你不做一个统一的时间基准SLAM算法里激光帧和里程计数据的时序就是错的建出来的地图会“漂”。实际工程项目里我会在车辆的工控机上部署chrony作为NTP服务端给所有传感器模块做硬件时间戳对齐。这个细节一旦被忽略后面排查问题会非常痛苦。第二个是日志与回放体系。AGV在真实产线上跑出了问题比如突然急停、走到中途卡住你是不可能靠现场看代码来排障的。正确做法是把所有关键数据——订阅的指令、发布的控制量、传感器原始帧、状态机跳转——都打上时间戳记日志并且支持按时间范围回放。这样出问题时你能在办公室把一个“故障现场”完整复盘。没有这套体系你的AGV调试基本就是靠猜。第三个是仿真与实车的抽象层接口。在没有真车的时候算法怎么验证答案是仿真环境。你需要一个大司机的抽象接口仿真环境和真实底盘都实现同一套接口。控制层调用的是抽象接口不用关心底层是不是真车。这个设计能让你在仿真里把调度算法跑到99%的可靠性再上真车联调极大缩短现场调试周期。这些隐性层看起来没有算法那么“高大上”但在工业项目里它们往往才是决定一个AGV项目能否按时交付的关键。2.2 工业技术栈选型的反常识心得做AGV技术栈选型的时候你会发现一个反常识的现象工业现场最怕的不是技术落后而是技术太新。我接触过的不少AGV项目在一些关键模块上仍在使用“老”技术比如ROS机器人操作系统的真诚版一个长期支持版本再搭配固定路径的磁导航方案而不是“视觉SLAM 激光SLAM 深度学习识别”的全套新式打法。这不是因为团队不思进取而是因为工业场景对稳定性的要求是压倒性的——产线停摆半小时的损失不是一点点炫酷的新功能可以弥补的。我自己的经验是AGV技术栈选型要遵循几个朴素的准则核心控制链路不搞“双创新”。控制链路定位 → 决策 → 控制 → 执行上的每个环节至少要有一个是你在别的项目里跑熟过的。也就是说你可以换新的定位算法但控制策略就用老方案你可以换新的底盘硬件但决策调度逻辑要沿用验证过的。如果一条链路全是新的出了问题你都不知道该怀疑哪个环节。“重缓存、轻交互”的数据链路。AGV的数据是典型的时序数据大户激光帧、IMU数据、任务状态每秒钟产生大量记录。在技术选型时存储层优先考虑的是写入性能和时序检索能力而不是SQL的联表查询便利性。工业园区内网环境也不适合依赖公网云一个高可用的本地时序数据库比如基于InfluxDB或TDengine的方案往往是更务实的答案。先定数据接口再定具体技术实现。AGV的项目里车辆本体和上层系统WMS/MES之间的接口约定比选什么语言重要一百倍。有些项目把大量时间花在争论用REST还是MQTT其实这不该是选择问题——接WMS通常用REST/WebSocket就够了接设备级控制走MQTT/Modbus更自然。真正要先定死的是消息里的字段语义、坐标系定义、单位制。这些不定清楚后面每个阶段都在返工。做AGV技术栈这几年我最深的感受是真正的全景图不能只画技术还要把“现场调试会遇到什么问题”“客户方可能提出什么约束条件”这些软因素画进去。技术栈是工具包但什么时候用什么工具依赖的是你对项目整体的判断力这份判断力才是全景图的纲。3. 小程序侧翼uniapp技术栈的实战取舍3.1 一个“全家桶”式的uniapp选型方案聊完重型工业场景说点离大多数开发者更近的用uniapp做小程序。现在很多人提到小程序开发第一反应还是“微信原生”然后才是uniapp这类跨端框架。我的态度很明确如果你要做多端微信、支付宝、抖音、H5uniapp值得认真对待如果只做微信小程序且团队里全是原生开发熟手那原生也不差。但对绝大多数中小团队uniapp的收益是明显大于代价的。我自己的uniapp项目技术栈是这样组成的你参考着用即可框架层uni-app Vue 3 Vite。注意这里的“Vue 3”不是默认项需要你在创建项目的时候确认选择了Vue 3版本并且搭配HBuilderX工具链或CLI模板。Vue 3带来的组合式APIComposition API对小程序这种页面组件逻辑容易变得驳杂的场景帮助非常大。你可以用script setup把一个页面的所有逻辑按功能块组织代码可读性比Options API高的多。状态层Pinia。在uniapp里持久化缓存、登录态、全局配置都是状态管理的典型场景。Pinia比起Vuex语法更简洁、对TypeScript支持更好是Vue 3时代的主流选择。配合pinia-plugin-persistedstate可以把store里的关键状态直接同步到storage省去很多手工读写的样板代码。UI层uni-ui为主锦上添花的组件再按需引入。uni-ui的优势是官方维护、自动适配多端但如果你做的是后台风的小程序uni-ui的样式偏基础需要自己调。这时候我会引入wot-design-uni它的组件丰富度和样式质感比uni-ui好不少而且是专门为uni-app打造的无需写rpx样式去适配。请求层luch-request或自己封一层基于uni.request的Promise封装。直接裸用uni.request在小程序里会非常难受因为缺乏拦截器、并发控制、统一错误处理这些基础能力。我现在固定用自己封的一层底层还是uni.request但把token刷新、错误码映射、断网提示都收敛到一个文件里。构建与发布HBuilderX本地打包到各平台CI/CD里用CLI方式触发云打包。团队小的时候不必自建签名服务器HBuilderX的云打包能解决大部分分发问题。这套栈跑了一年多在微信、抖音、H5三端表现都稳定踩坑率不高是很适合中小团队直接“抄作业”的组合。3.2 多端适配与条件编译的分寸感uniapp最吸引人的是“一套代码多端运行”但这句话的真相是逻辑代码可以多端复用视图层和接口层永远有差异。我把uniapp多端开发的适配工作分成三个层次按性价比排优先级。第一层是接口适配。各小程序平台在登录、支付、分享这些核心能力上接口形态不同但业务语义一致。这个层用条件编译#ifdef MP-WEIXIN、#ifdef MP-ALIPAY做隔离把平台差异封装在统一的自定义API里。第二层是UI适配。页面结构和交互逻辑通用但某些组件在不同端的渲染表现不同比如微信的button的open-type能力、抖音的隐私弹窗策略这些也需要条件编译来区别处理。第三层才是视觉适配。rpx单位在大部分端上没问题但在H5端实际渲染会有像素偏差刘海屏安全区在每个端都有不同的处理方式从statusBarHeight或CapsuleButtonInfo之类API获取。我的经验是适配的核心不是“消灭差异”而是“封装差异”。所有非通用的能力都要第一时间用条件编译裹成一个公共方法页面层永远只调公共方法。如果你把uni.login这种平台API直接散落在十几个页面里那等产品过来说“抖音端也要上了”你就等着加班吧——你不光要改十几次还要一个个去测试平台差异。3.3 uniapp技术栈里的坑与绕行方案用了快三年的uniapp有些坑是“不踩不足以谈经验”级别我把印象最深的整理给你。分包与体积控制是首要议题。微信小程序单个主包上限2MB这个限制刚接触uniapp的人特别容易撞上。你以为你没装什么大依赖但uniapp把vue、vue-router基础库都打进了主包再放几个页面和图片轻松超限。我的建议是初始化项目时就把以下几条当作铁律所有图片静态资源全部走线上CDN或者放分包页面组件能拆进分包就拆进分包第三方UI库只import用到的组件不要全量引入紧接着在manifest.json里配置subPackages把不常访问的页面比如用户协议、隐私政策、设置页全部塞进分包。即便这样做了我每个版本提审前都会看一眼“主包大小”这个指标超过了1.8MB就开始警惕。小程序setData性能问题是“慢性病”。页面上凡是绑定了大数组数据的要谨慎更新。早前踩过一个大坑一个订单列表页直接绑定了一个几十KB的数组然后每次筛选刷新重新setData全部数据在低端安卓机上交互帧率直接掉到个位数。后面改成局部setData、用v-show控制局部渲染、数据量大的列表用virtual-list组件替代scroll-view里的全量渲染问题才彻底缓解。条件编译的语法不能“自以为”记得。#ifdef前的%号#ifndef的用法有段时间我总把ifdef和ifndef搞反导致编译出来的代码平台判断完全相反页面白屏。这种问题的诡异之处在于它不会报错运行起来才出错。后来我给自己定了个规矩每个平台接入的第一天先跑一段最简单的条件编译demo比如分别在不同端渲染一段不同文字确认编译方向没有反才继续写业务逻辑。uniapp这套技术栈是典型的学习曲线平缓但深挖有料的类型它把前端开发者从平台割裂中解放出来代价是你得接受它的一些“上层抽象黑魔法”。摸清它的脾气之后说实话写一次适配三端的幸福感还是很强的。4. 桌面端延伸Electron技术栈的得与失4.1 为什么桌面端选Electron而不是Tauri或Qt在桌面端领域Electron一直是个争议话题。打包体积大、内存吃紧、启动偏慢这些批评都是真实存在的。但我最后选择Electron而不是Tauri或Qt不是因为Electron更潮而是因为我的项目场景决定了它是“成本最低的正确方案”。我的桌面端项目是这样的需要跨Windows和macOS界面需要大量复用Web前端组件地图可视化、图表、复杂表单并且需要快速迭代。如果选Qt意味着要招或训练一个熟悉C/QML的团队而且和前端生态割裂迭代速度跟不上业务需求。如果选Tauri虽然包体积小、性能好但它的生态成熟度在早期还不够——很多WebView跨端怪癖需要团队去排坑后端Rust开发也有学习成本。Electron的优势恰好全中跨平台稳定、Node.js生态可直接利用、Web技术栈无缝复用、社区各种坑都有现成答案。性能上的问题对于不追求极致游戏级的桌面工具类应用来说是完全可以接受的——一个20MB的安装包换来迭代速度和生态红利我觉得这笔账非常划算。4.2 桌面端技术栈的工程化配置Electron项目写起来不难难的是工程化配置。这里分享一套我用了很久的稳定模板的思路。主进程与渲染进程的严格边界。主进程是Electron的“大脑”负责窗口管理、系统集成、原生能力调用渲染进程是“UI”跑着Vue或React。最忌讳的是在主进程里写大量业务逻辑、在渲染进程里直接Node.js API。我的项目结构是src/main放主进程代码、src/renderer放渲染进程代码、src/preload放预加载脚本三条线严格分开。IPC通信只走preload暴露的唯一条通道用contextBridge暴露API渲染进程不直接require(electron)。打包分发用electron-builder还是electron-forge我的实践结论是electron-builder更顺手。配置package.json里的build字段时注意三个关键项appId不要用默认的com.electron.xxx上架时要改、files白名单只打包必要文件能显著减重、dmg和nsis的图标与配置。另外一个容易忽略的坑是把npmRebuild设为false否则electron-builder会自动重编译原生模块容易在windows机器上莫名报错。开发环境的HMR强调了三个进程主进程改动用electronmon自动重启渲染进程走webpack-dev-server的HMRpreload脚本改动手动重启。如果不做这层拆分开发体验远不如纯前端项目——你会陷入“改一行代码整个应用重启三秒”的泥潭。4.3 Electron技术栈的常见问题排查实录我整理一份工作中最常碰见的Electron问题速查写着“避坑”就够用了问题现象根本原因解决思路安装electron失败或下载极慢默认从GitHub下载二进制文件国内网络不稳设置镜像源把ELECTRON_MIRROR指向国内镜像仓库地址打包后在用户电脑上双击没反应常见是动态链接库缺失或node原生模块没打包使用electron-builder的nativeRebuild重新编译原生模块并检查依赖是否打进asarwindow.open打开的新窗口白屏新窗口的webPreferences未配置contextIsolation: false或者未允许nodeIntegration给新窗口显式设置webPreferences在白名单的webContents创建事件里拦截并重定向渲染进程内存飙升后崩溃页面存在内存泄漏或是主进程不能及时响应排查Vue/React大列表和事件监听器的移除使用win.webContents.setMemoryUsageManagement开启内存回收机制主进程监听IPC卡顿主进程被同步业务阻塞事件循环卡死把耗时操作放到utilityProcess或worker线程主进程只做窗口与原生逻辑调度其中第一个问题几乎是每个Electron新手的“第一课”。不做镜像配置的话npm i electron在某些网络环境里能卡半小时。解决方法很简单在项目根目录加一个.npmrc文件写上electron_mirrorhttps://npmmirror.com/mirrors/electron/再把ELECTRON_BUILDER_BINARIES_MIRROR指到对应的镜像地址之后安装就一路畅通了。另一个容易被忽视的坑是nodeIntegration: true与安全风险的博弈。很多教程为了方便把渲染进程的nodeIntegration设为true这就等于把Node.js的全部能力开放给了页面——一旦页面加载了第三方脚本你的用户电脑相当于裸奔。我自己的准则是永远保持contextIsolation: true渲染层永远不直接碰Node.js需要用原生能力就通过preload暴露一个最小API面。这样既是安全底线也让代码结构更干净少一堆主进程跟渲染进程的边界混乱问题。Electron技术栈给我最大的感受是“上限不高但下限很高”——它不会让你做出性能惊艳的作品但能让你用前端技能稳定地做出商业可用的桌面应用。对于一个主要业务在前端、偶尔需要覆盖桌面端的团队它就是效率最优解。5. 技术栈全景图的动态维护与个人成长5.1 技术栈保鲜的“轻量机制”技术栈全景图不是一次性的文档工作它必须跟你的项目、你的阅读、你的思考同步演化。我维护全景图的方法很轻不是精心排版的大文档而是三样东西的组合。第一样是一个持续更新的Markdown文件叫tech-radar.md放在我的个人知识库里。格式很简单分几个区块正在深度使用的技术、正在学习验证的技术、观望但暂不引入的技术、已经放弃的技术。每个技术下面写一行“为什么”以及“在什么项目里验证过”。第二样是一个项目复盘模板每个项目结束后按模板写复盘把技术选型的成败记录下来链接到tech-radar里的对应条目。第三样是每季度一次的全景图快照把当前所有在用的技术栈导出一张图我用的是Obsidian Excalidraw用来自我审视技术栈的结构是否失衡。这套机制看起来朴素但它保证了我的技术栈决策都是有据可循的——每当有人问我“为什么不用XX框架”我不是凭感觉回答而是能翻出当时的决策记录跟他说清楚当时在什么约束下选了现在的方案。这个习惯对个人成长的价值可能比技术本身还大。5.2 全景图自检的三个问题当你拿着自己的技术栈全景图怎么判断它的健康度我给自己定了三个自检问题你也可以直接用技术栈的“依赖宽度”是否过窄如果你的全景图里全是JavaScript生态Node、Vue、Electron、uniapp没有其他领域的技术树那你的技术栈抗风险能力是弱的。技术行业的周期性波动很真实一个领域萎缩时你的技能组合会很被动。反过来如果你在几个真正独立的领域都有“一剑封喉”的能力你的职业底盘就稳得多。你是否正在被一个“过期框架”锁住有些技术栈选项当年是对的拖着拖着就变成负债。比如一个项目用了3年前的CLI工具链维护成本已经高到影响交付那就应该果断升级或迁移而不是因为“改起来麻烦”而反复打补丁。全景图最大的价值之一就是让你发现这些“技术债务”什么时候到了该还清的时候。你最近一次主动学习新技术是“跟风”还是“解问题”我见过很多人学的技术栈很杂很多但问起来全是“觉得以后可能用得上”。其实最健康的学习方式是“项目驱动力为主预研为辅”——遇到实际问题时带着问题去找方案学到的东西会固着得最牢然后偶尔花一点时间做非功利性的技术预研保持视野的开阔性。这两者的比例我自己的底线是七三开。我在实际维护全景图的过程中发现最好的成长不是你的图变得越来越庞大而是你的图变得越来越“干净”——你能越来越清晰地解释每一个技术栈为什么在那里大胆地删掉那些“好像会用”的填充物。这听起来很简单但做到了你就是个真正有技术判断力的人了。6. 个人实操体会与一个小技巧最后分享一点很私人的体会。滚动整理技术栈这几年我最大的收获不是学会了很多新框架而是学会了“承认技术选型有适用边界”。做AGV的时候我曾因为坚持用高端方案被客户质疑做uniapp的时候我也曾为了“跨端理念”付出过不必要的适配成本做Electron的时候我更是在性能和安全之间反复拉扯。每个技术栈都是一把钥匙它能开哪扇门、开不了哪扇门必须实践过了心里才有数。至于小技巧我强烈建议你给自己的全景图加一个“废弃日期”字段。每在图上加一项技术就顺手写一个“下次审视时间”可以设半年后到期了就去问自己这个技术我还在用吗还推不推荐别人用给我自己淘汰的旧框架做个简单的“葬礼记录”写上它曾经解决过什么问题、后来为什么不用了——这个过程会帮你把经验真正内化而不是只是多认识了一个名词。技术栈是你跟这个世界打交道的方式也是你过去所有项目经验的浓缩。把这张图画清楚你就知道下一步路往哪走了。
返回列表