news 2026/10/20 9:50:47

Playwright 8种定位策略详解:从角色定位到测试ID

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Playwright 8种定位策略详解:从角色定位到测试ID

1. 为什么Playwright的定位方式值得重新理解

只要是写过UI自动化脚本的人,都逃不过“定位元素”这一关。早年用老一代自动化框架的时候,大家习惯了把希望寄托在CSS选择器和XPath身上,页面一改结构,脚本跟着崩,维护成本全堆在定位表达式上。用Playwright这段时间,我最大的感受是:它的定位思路从一开始就换了方向——不是让你想尽办法去“够到”某个DOM节点,而是让你站在用户视角,描述“这个元素长什么样、在页面里扮演什么角色、用户怎么称呼它”。这套定位器(Locator)机制,配上智能等待和严格模式,直接把自动化脚本的稳定性拉高了一个档次。

这篇文章会把Playwright里最常用的8种定位策略逐一拆开讲,包括适用场景、参数细节、背后逻辑,以及我在实际项目里踩过的坑。不管你是刚接触Playwright,还是从其他自动化框架迁移过来,仔细看完这篇,定位这块基本就能拿捏住了。

1.1 从“选择器思维”到“定位器思维”

老框架时代,写定位基本是这么个过程:打开DevTools,右键复制XPath,粘到脚本里,跑一次,能过就算完。这种做法的毛病很明显——复制出来的选择器往往又长又脆,CSS类名改一下、DOM层级动一下,脚本就废了。而且原始选择器完全不表达“这个元素是干嘛的”,三个月后回来看脚本,满屏的div > div > .form-item:nth-child(2) > input,根本不知道当时在定位什么。

Playwright的定位器不一样。它不追求“唯一命中某个节点”,而是要求你描述元素的特征:角色(button、link、checkbox)、可访问名称(按钮文字、label文本)、占位提示、测试ID等等。定位器自带等待机制,元素没出现就反复尝试,直到超时。加上严格模式的约束——一旦一个定位器同时匹配到多个元素,直接报错而不是闷头操作第一个,这在很大程度上逼着你写出更精准的定位条件。

我见过不少人从老框架转过来之后,依然习惯性用page.locator('#app > div > form > input')这种写法,然后抱怨Playwright怎么老超时。其实问题不在工具,在于思路没换过来。Playwright的定位器设计初衷就是让你少依赖脆弱的DOM结构,多依赖用户能感知到的内容。

1.2 官方推荐定位优先级与整体设计思路

Playwright官方文档里给了一个定位策略的推荐顺序,这个优先级值得反复琢磨:

  1. 角色定位(getByRole)——最贴近用户感知,可访问性最好
  2. 标签定位(getByLabel)——表单元素最优解,天然关联label
  3. 占位符定位(getByPlaceholder)——输入框辅助提示
  4. 文本定位(getByText)——需要按可见文本找元素时
  5. 替代文本定位(getByAltText)——图片类元素
  6. 标题定位(getByTitle)——带title属性的元素
  7. 测试ID定位(getByTestId)——前端配合加标记,最稳定但需要约定
  8. CSS选择器与XPath定位——兜底方案,迁移过渡期用

这个优先级背后的逻辑很简单:越靠前的定位方式,越贴近“人类理解页面的方式”,越不依赖具体DOM实现。比如一个登录按钮,它的角色就是button,名称就是“登录”,只要前端不把标签换成别的角色,这个定位器就一直有效。而CSS路径一旦某层div被包多了一层,就全完了。

2. 8种定位策略逐个拆解

2.1 8种定位器总览

在Playwright里,定位器API统一返回Locator对象。先看一张总览表,把每个API的语法、匹配依据和典型场景梳理清楚,后面再逐个展开。

定位策略方法示例匹配依据典型场景
角色定位getByRole('button', { name: '提交' })ARIA角色 + 可访问名称按钮、链接、标题、输入框
标签定位getByLabel('用户名')label元素或aria-labelledby表单输入类控件
占位符定位getByPlaceholder('请输入手机号')placeholder属性无label的输入框
文本定位getByText('保存成功')元素内可见文本按钮、div、提示信息、列表项
替代文本定位getByAltText('产品宣传图')img的alt属性图片、地图区域
标题定位getByTitle('帮助文档')title属性或svg title图标按钮、提示链接
测试ID定位getByTestId('submit-btn')>// 按按钮文字定位 await page.getByRole('button', { name: '提交' }).click(); // 按链接文字定位 await page.getByRole('link', { name: '了解更多' }).click(); // 定位输入框(注意此时不是用getByLabel而是role定位) await page.getByRole('textbox', { name: '用户名' }).fill('testuser'); // 多级菜单展开后用级联定位 await page.getByRole('button', { name: '用户中心' }).click(); await page.getByRole('menuitem', { name: '个人资料' }).click();

name参数支持正则表达式,这在处理动态数字、动态文本时非常有用:

// 包含用正则,匹配到“共10条记录”、“共32条记录”等 await page.getByRole('button', { name: /确[认定]/ }).click(); // 精确匹配某文本 await page.getByRole('heading', { name: '欢迎回来', exact: true }).toBeVisible();

关于getByRole,有几个细节必须知道:

  • 它不仅能定位button、link,还能定位heading、checkbox、radio、menuitem、dialog等大量ARIA角色
  • 如果页面里有多个相同角色且名称一致的元素,必须配合严格模式进一步收窄范围
  • hidden的元素默认不会被匹配到,除非显式设置includeHidden: true
  • 对自定义封装的组件,只要前端设置了正确的ARIA属性,getByRole照样能定位到

实际项目中,自定义组件最容易出问题。比如某些公司内部封装的下拉框,外层是div,内层是input,前端如果没写role="combobox",用getByRole就找不到。这时候要么推动前端补ARIA属性,要么退而求其次用其他策略。我的经验是:优先推动前端补ARIA属性,这对无障碍和自动化双赢。

2.3 getByLabel与getByPlaceholder:表单类元素的首选

表单是UI自动化绕不开的领域。getByLabel专门用来定位带label标签的控件,HTML里写了<label for="username">用户名</label>这种结构,Playwright会自动建立关联。哪怕label和input离得很远,只要for和id对应上,就能定位。

await page.getByLabel('用户名').fill('zhangsan'); await page.getByLabel('同意用户协议').check(); await page.getByLabel('手机号').fill('13800138000');

这里的优势非常明显:你描述的是用户看到的文字,而不是input的属性或DOM位置。label文字改了脚本跟着改,但至少比CSS路径好维护得多。

不过有个坑要提醒:如果页面上没有显式的label,或者label写得不规范(比如用span+onclick模拟label),getByLabel就抓瞎。此时可以看看有没有aria-labelledby属性,如果有,getByLabel也能通过关联的id找到。如果两者都没有,就退到getByPlaceholder。

getByPlaceholder的定位依据是input或textarea上的placeholder属性:

// 搜索框 await page.getByPlaceholder('请输入关键词').fill('自动化测试'); // 密码输入框 await page.getByPlaceholder('至少8位,含字母和数字').fill('pass1234');

注意:placeholder在某些浏览器上点击后可能会被遮罩层盖住,或者被前端JS清空。如果定位到了但fill不进去,先看看是不是有自定义点击事件干扰。另外,placeholder本身是提示性文字,它不是label的替代品。无障碍规范里建议每个输入控件都有可见的label,placeholder只能算辅助。团队在推动前端规范时,最好别让开发者用placeholder替代label。

从我的实操经验看,表单自动化有一个优雅的进阶写法:把label和组合条件一起用。比如一个表单有多个“用户名”输入框(注册和登录页共存),可以先用getByRole('dialog')收窄范围再定位。

const dialog = page.getByRole('dialog', { name: '登录' }); await dialog.getByLabel('用户名').fill('zhangsan'); await dialog.getByLabel('密码').fill('abc123'); await dialog.getByRole('button', { name: '登录' }).click();

这种写法脚本表达得很清楚:在登录弹窗里,填用户名、密码、点登录。别人看代码一眼就懂在干什么。

2.4 getByText、getByAltText与getByTitle:文本与属性的互补

getByText按元素包含的可见文本定位。这里要特别强调“可见文本”四个字——如果文本被CSS隐藏(display:none或visibility:hidden),默认匹配不到。

// 定位包含“保存成功”的任意元素 const saveTip = page.getByText('保存成功'); await expect(saveTip).toBeVisible(); // 精确定位按钮文本 await page.getByText('删除', { exact: true }).click(); // 用正则匹配更灵活 await page.getByText(/订单号[::]\d+$/).click();

用getByText时候最容易踩的坑是范围过宽。比如页面上有“取消”两个字的地方可能不止一处——按钮上有、菜单里有,一不小心就匹配到多个元素触发strict mode报错。解决方式是尽量配合exact或角色一起用,或者先收窄范围再定位:

// 在订单列表中,找到“查看详情”那一行 const orderRow = page.locator('tr').filter({ hasText: '订单号' }); await orderRow.getByRole('button', { name: '查看详情' }).click();

getByAltText的定位依据是元素的alt属性,最典型的就是img:

// 定位产品图片 await page.getByAltText('夏日遮阳帽产品图').click(); // 定位图片里的上传区域 const uploadArea = page.getByAltText('上传区域占位图');

alt属性的作用和可访问名称有点像。前端如果图片只是装饰性的,alt应该写成空字符串;如果承载信息,alt就是图片的说明。自动化测试里用alt定位图片元素,语义清晰且稳定。但有一种情况要小心:懒加载图片的alt在加载前后可能存在,但图片进入视口前可能不在DOM中。遇到这种场景,直接用测试ID或父级容器定位会更稳。

getByTitle定位依据是元素的title属性,这个属性常见于图标按钮,用户鼠标悬停时会出现提示文字:

// 定位“删除”图标按钮 await page.getByTitle('删除').click(); // svg图标内嵌title时的定位 await page.getByTitle('刷新').hover();

getByTitle有个要注意的点:React组件里的title有时是动态生成的,比如tooltip提示内容根据状态变化。如果title属性在hover前不存在,定位就会超时。所以优先用它定位静态title的元素,动态提示类的最好不要依赖title做自动化。

2.5 getByTestId:测试专用标识的兜底方案

最后说说getByTestId。这是我最推荐的兜底策略——当所有语义化定位都搞不定的场景,直接在元素上添加data-testid属性,定位器用它做唯一标识:

// 默认匹配data-testid属性 await page.getByTestId('submit-btn').click(); await page.getByTestId('user-avatar').hover();

><button>// 在playwright.config.js或者初始化时设置 test.use({ testIdAttribute: 'data-testid' }); // 或者自定义 playwright.config 里配置 testIdAttribute: 'data-my-test-id'

这样如果公司统一用>// CSS选择器 const rows = page.locator('.table-body .table-row'); await rows.first().click(); // XPath定位 const cell = page.locator('xpath=//div[@class="user-card"]//span[contains(text(),"高级会员")]'); // 等价写法 const cell2 = page.locator('//div[contains(@class, "user-card")]//span[contains(text(),"高级会员")]');

但我的态度是:能用语义化定位器就用语义化定位器,CSS和XPath只在必要时用。原因不是性能,而是可维护性。页面重构时,CSS类名是最容易被改掉的;XPath里写层级,多包一层div就全废。它们适合解决“眼前”的问题,但不适合成为长期维护的主力。

3. 复杂场景下的定位组合与实操要点

3.1 链式定位与范围收缩

实际页面上,一个元素可用的特征往往是多样且交叠的。比如页面上有多个“编辑”按钮,分别对应不同行数据。此时直接用getByRole('button', { name: '编辑' })必然报strict mode违规,因为匹配到了多个。正确做法是先定位容器,再在容器内搜索。

// 先定位包含特定用户名的行,再在行内点击编辑 const row = page.locator('tr').filter({ hasText: 'zhangsan@example.com' }); await row.getByRole('button', { name: '编辑' }).click();

这种写法在老框架里要写一串复杂的父子层级XPath,在Playwright里则逻辑非常清晰:“在张三所在的那一行,点击编辑按钮”。

链式定位还有一个变体是定位器之间的组合。定位器不是一次性表达式,而是可复用的对象:

// 定义可复用的定位器 const loginDialog = page.getByRole('dialog', { name: '登录' }); const usernameInput = loginDialog.getByLabel('用户名'); const passwordInput = loginDialog.getByLabel('密码'); const submitButton = loginDialog.getByRole('button', { name: '登录' }); // 使用时,走完流程 await usernameInput.fill('testuser'); await passwordInput.fill('abc123'); await submitButton.click();

链式定位在处理弹窗、折叠面板、标签页这类“局部页面空间”时非常有用。先把范围收缩到弹窗内,之后所有定位都从弹窗这个定位器出发,自然避免了和其他区域同名元素的冲突。

3.2 filter、and、or:让筛选条件更精确

Playwright定位器支持在已有基础上做二次筛选,常见的是filter()、and()和or()方法。

filter()可以按文本、子元素等条件过滤,适合列表场景:

// 在所有商品项里,筛选名称含“保温杯”的那一项 const cupItem = page.locator('.product-item').filter({ hasText: '保温杯' }); // 同时按文本和子元素筛选 const soldOutItem = page.locator('.product-item').filter({ hasText: '已售罄', has: page.getByText('保温杯') });

and()表示同时满足两个条件:

// 定位同时带有“默认地址”和“北京市”文本的节点 const beijingDefault = page.getByText('默认地址').and(page.getByText('北京市'));

注意,and()适用于同一个元素同时满足多个条件的情况,和filter()在文字匹配上有些重叠,但我用得最多的是locator('选择器').and(locator('选择器'))这种精简写法。

or()表示满足任意一个条件即可,这在处理“可能有两种文案”的场景下很实用:

// 提示文本有时是“保存成功”,有时是“操作成功” const tip = page.getByText('保存成功').or(page.getByText('操作成功')); await expect(tip).toBeVisible();

这种“或”逻辑比传统的XPath//span[contains(text(),"保存成功") or contains(text(),"操作成功")]写起来更清晰。

组合条件还有一个好处:避免为了适应多状态去写超复杂的XPath。Playwright的哲学是用多个可读的小条件组合,而不是一个大而全的表达式。

3.3 iframe与Shadow DOM中的定位

大部分自动化脚本在遇到iframe时都要额外处理一步。Playwright里frame元素也能被定位,然后在其内部使用同样的定位器API。

// 定位iframe元素 const frameLocator = page.frameLocator('#payment-iframe'); // 在iframe内部定位输入框 await frameLocator.getByLabel('持卡人姓名').fill('张三'); await frameLocator.getByPlaceholder('卡号').fill('4111 1111 1111 1111');

这里的关键是frameLocator,它返回的是一个frame作用域下的定位器,用法和普通定位器几乎一致。它解决了自动化里最头疼的跨域iframe和动态iframe问题,只需要CSS选择器能定位到iframe本身,内部就能用语义化定位器继续操作。

Shadow DOM也是现代前端架构里常见的结构。Playwright对Shadow DOM的处理方式是:普通CSS选择器无法穿透,但locator('选择器')配合>>穿透语法可以做到:

// Playwright支持css=... >> internal=... 这种穿透写法 const shadowInput = page.locator('my-custom-element >> input');

如果Shadow DOM内部元素没有语义化特征,可以先用CSS穿透进去,再用语义化定位器。但说句实在话,Shadow DOM里的元素定位始终比较复杂。如果团队技术选型允许,优先建议给这些元素加data-testid,能省很多事。

3.4 动态列表、弹窗与表格场景实战

真实业务系统里,最让人头疼的是表格和动态列表。数据一变,行数变、内容变、按钮位置变。用Playwright写这类场景,我的核心原则是:永远不要依赖绝对位置和固定索引,而是用文本或内容特征来锁定目标行。

比如一个订单管理表格,每行有订单号、客户名、状态、操作按钮。定位“某个订单号对应的删除按钮”:

const orderRow = page.locator('tr').filter({ hasText: 'DD20240001' }); await orderRow.getByRole('button', { name: '删除' }).click();

这里的tr是CSS选择器,hasText过滤出目标行。如果担心“订单号”也可能出现在其他行里(比如关联订单的展开行),再加一层筛选:

const orderRow = page.locator('tr').filter({ hasText: 'DD20240001', has: page.locator('td').nth(1) // 锁定列位置 });

弹窗场景前面已经覆盖了,核心是先定位dialog容器,再在内部操作。动态列表场景要额外注意虚拟滚动——如果表格用了虚拟滚动,未渲染的行根本不在DOM里,直接locator('tr').filter()是找不到的。此时需要先滚动到目标区域:

// 先滚动列表容器触发渲染 await page.locator('.virtual-scroll-container').evaluate(el => el.scrollTo(0, el.scrollHeight)); await page.getByText('目标数据').click();

这类问题不是定位器本身能解决的,它属于渲染机制的范畴。要能分辨“定位问题”和“渲染问题”,排查方向会快很多。

4. 常见问题与排查技巧实录

4.1 超时与定位失败

Playwright的定位器自带等待机制,默认超时30秒(可在配置或调用时修改)。定位超时的报错信息非常典型,它会显示出重试期间对DOM的多次快照,并列出可能的匹配项。

最常见的超时原因有三个:

  • 元素在DOM里但不可见且不可操作。比如前端加了遮罩层、弹窗未完全展开、元素被CSS动画挡住。此时检查popper或modal的层级状态
  • 元素在iframe或Shadow DOM里,定位器没穿进去。检查是否用了frameLocator或者穿透语法
  • 定位条件里的名称不准确。比如按钮文本实际是“立即提交”,脚本里写的是“提交”。用正则或模糊匹配可以解决

处理建议:先打开页面手动验证元素存在,再用page.locator('...').count()查看匹配数量。定位器的报错信息里其实给出了很多线索,要认真读,而不是盲目加时间。

4.2 strict mode违规与多个元素匹配

Playwright默认开启严格模式,意思是定位器一旦匹配到多个元素,操作直接抛错。这个设计初看有点烦,但实际帮了大忙——它逼你写更精准的定位条件,而不是赌第一个匹配的就是想要的。

遇到strict mode违规,我的排查套路是:

  • 先用count()确认匹配了几个元素
  • 用all()拿到所有匹配项,逐个打印文本或属性,确认差异
  • 再根据差异补充定位条件,比如加name、加filter、收窄范围到父容器
// 排查技巧:打印所有匹配项的文本 const matches = page.getByRole('button', { name: '编辑' }); console.log(await matches.allTextContents());

这一步非常提升定位效率。很多时候你以为只有一个按钮,实际页面上有多个相同文案的按钮,打印文本后才发现它们的父容器或上下文不同。

4.3 动态元素与加载时序问题

动态渲染的评论区、实时通知、图表加载,都可能导致元素在定位时存在,但操作时又被重新渲染。Playwright的自动等待能解决大部分时序问题,但偶尔也会遇到“定位到了但点击无响应”的怪事。

我的经验是从两个方向排查:

  • 元素是否被遮挡?用click({ trial: true })先做可点击性校验,只检查不实际操作
  • 元素是否在点击瞬间被替换?比如前端用React重新渲染,原来的DOM节点被替换成新节点。定位器在你调用click的那一刻,会和新的DOM重新匹配。如果操作没生效,多半是事件监听绑定在旧节点上
// trial模式:只检查是否可点击,不真正点击 await page.getByRole('button', { name: '提交' }).click({ trial: true });

还有一种更隐蔽的情况:组件库(比如下拉树、穿梭框)在选项加载后会修改内部结构。此时定位器可能匹配到旧的选项节点。处理方法是等选项完全加载后再操作,或者用expect(option).toBeVisible()先断言。

4.4 问题速查表

症状可能原因排查方向
定位超时,元素始终找不到元素在iframe或Shadow DOM内使用frameLocator或穿透语法
定位器匹配到多个元素多个元素文本或角色相同加filter、收窄范围、用exact
元素存在但点击无效被遮挡、动画拦截、节点被替换用trial检查可点击性,确认渲染状态
getByRole定位不到自定义组件组件缺少ARIA角色属性推动前端补role属性,或改用testid
getByText匹配到隐藏文本匹配了display:none元素使用exact或角色限定
动态列表里找不到目标项虚拟滚动未渲染目标项先滚动容器再定位
定位器能命中但不稳定特征选择过于宽泛用and/or/filter组合精确化

这张表基本覆盖了我日常在群里被问到最多的问题。很多所谓的“定位难题”,本质上是没分清“元素在不在DOM里”“元素能不能被操作”“元素是不是唯一的”这三件事。

5. 一些值得长期坚持的定位习惯

写到这里,正文基本到了收尾阶段。最后聊聊我个人的体会和习惯。

第一,所有定位器尽量从“用户如何描述这个元素”出发。一个按钮,用户叫它“提交订单”,就用getByRole('button', { name: '提交订单' })。你需要猜测的是业务语义,而不是DOM结构。

第二,定位器要“短小可读”。写完之后读一遍,如果一句话读不懂在定位什么,就说明它还应该再拆分。比如page.locator('.list').filter({ hasText: '张三' }).getByRole('button', { name: '删除' }),读起来就是“在张三这行删掉记录”,特别清楚。

第三,测试ID不是洪水猛兽,但要克制使用。只在语义化定位确实搞不定的场景用,并且和前端团队约定好命名规范和埋点范围。用得好,它是定海神针;用得烂,代码里全是测试标记,也难看。

第四,如果页面结构频繁变化,优先思考是不是前端组件本身不规范,而不是不断加固定位器。多和前端沟通,推动组件语义化,对测试和可访问性都有长远好处。

我刚开始用Playwright时,也习惯性写CSS路径,后来被一次改版狠狠教育了一顿——几十个脚本一夜之间全红,排查到凌晨才意识到是前端把类名体系彻底重构了。那时候才真正理解定位器设计的意义:你在定位器里维护的是“用户看到的界面”,而不是“程序员实现的细节”。界面文案有时也会变,但变化的频率和规律,比DOM结构要可预期得多。

希望这篇关于8种定位策略的实战拆解能帮到你。如果你在实践中遇到什么有意思的定位问题,或者有更妙的解法,欢迎多交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 9:49:23

数字转中文大写金额:小数与超大数值的精度处理实战

1. 这个数字转中文大写的需求到底难在哪第一次接到“数字转中文大写&#xff0c;还要带小数&#xff0c;最大支持到 999999999999999.9999”这个需求时&#xff0c;我第一反应是&#xff1a;不就是把阿拉伯数字换成壹贰叁肆吗&#xff0c;写个映射表循环一下不就完了。结果真正…

作者头像 李华
网站建设 2026/10/20 9:47:59

C语言学习路线:从指针、内存到工程实践,避开新手踩坑指南

开学C语言&#xff0c;其实没那么可怕&#xff1a;一个老学长踩坑无数后的正经学习路线每年九月初&#xff0c;我都能在论坛和群里看到一大批新生发出同一种灵魂拷问&#xff1a;"C语言到底怎么学&#xff1f;""听课能听懂&#xff0c;一写就废怎么办&#xff1…

作者头像 李华
网站建设 2026/10/20 9:46:08

Spring Boot宠物美容预约系统开发实战:排期冲突与状态机设计

接到这个“springboot191宠物美容护理预约系统”的项目&#xff0c;光看名字就知道是给宠物美容店做的一个预约管理后台。这类系统市面上看着不少&#xff0c;但真正拆开揉碎去做的过程中&#xff0c;你会发现它不是简单的增删改查&#xff0c;牵扯到美容师排班、宠物档案、护理…

作者头像 李华
网站建设 2026/10/20 9:45:20

STM32寄存器位操作实战:从GPIO配置到LED控制

如果你打开某颗常见STM32芯片的参考手册&#xff0c;翻到GPIO那一章&#xff0c;看到满页的寄存器位图&#xff0c;第一反应多半是“头大”。但如果你又恰好翻过几个老工程师写的驱动代码&#xff0c;看到满屏& ~(1 << 4)、| (1 << 11)这种表达式&#xff0c;可…

作者头像 李华
网站建设 2026/10/20 9:45:00

Spring AI Alibaba停更后Java大模型接入平滑迁移指南

1. 项目概述&#xff1a;当一个热门技术组件突然“断更”&#xff0c;Java生态的真实应对逻辑“Spring AI Alibaba已停更了&#xff0c;Java还有希望吗&#xff1f;”——这句话最近在几个Java技术群和社区里反复刷屏&#xff0c;语气里带着点猝不及防的错愕&#xff0c;还有一…

作者头像 李华
网站建设 2026/10/20 9:43:27

2024国赛A题板凳龙MATLAB代码与论文复现资源

简介&#xff1a;这份资源是2024年全国大学生数学建模竞赛A题的完整论文与Matlab源代码&#xff0c;面向计算机、电子信息工程、数学等专业的大学生&#xff0c;可用于课程设计、期末大作业、毕业设计及竞赛备赛。压缩包共43个文件&#xff0c;以41个.m脚本为核心&#xff0c;另…

作者头像 李华

关于博客

这是一个专注于编程技术分享的极简博客,旨在为开发者提供高质量的技术文章和教程。

订阅更新

输入您的邮箱,获取最新文章更新。

© 2025 极简编程博客. 保留所有权利.