Puppeteer/Chromium点击含$state.go的ng-click按钮不跳转路由问题排查
解决Puppeteer点击Angular ng-click按钮不跳转路由的问题
我之前也遇到过类似的Angular + Puppeteer的路由跳转问题,结合你描述的StackTrace is not defined和$digest already in progress报错,大概率是Puppeteer的原生点击事件和Angular的digest循环、路由系统没有正确配合导致的,下面给你几个针对性的解决方案:
1. 手动触发点击并同步Angular的Digest循环
Puppeteer的page.click()有时候不会正确触发Angular内部的事件绑定,我们可以直接在页面上下文里执行点击,同时手动处理Angular的digest,避免循环冲突:
const startBtn = ".button-selector"; await page.waitFor(startBtn); // 进入页面上下文执行点击+Angular状态同步 await page.evaluate((selector) => { const btn = document.querySelector(selector); btn.click(); // 获取Angular的根作用域,确保状态更新完成 const injector = angular.element(document.body).injector(); const $rootScope = injector.get('$rootScope'); // 只有当不在digest循环中时才手动触发,避免冲突 if (!$rootScope.$$phase) { $rootScope.$digest(); } }, startBtn); // 用networkidle0等待路由跳转完成(适配单页应用的静默状态) await page.waitForNavigation({ waitUntil: 'networkidle0' }); await page.waitForSelector(".new-selector");
2. 修复StackTrace is not defined报错
这个错误说明你的Angular应用代码里引用了全局的StackTrace对象,但Puppeteer的页面上下文默认没有这个对象。可以在执行点击前先注入一个模拟对象:
// 先注入模拟的StackTrace对象,避免报错 await page.evaluate(() => { window.StackTrace = { fromError: () => [] // 模拟核心方法,根据你的需求调整 }; }); // 再执行后续的点击和等待逻辑 const startBtn = ".button-selector"; await page.waitFor(startBtn); // ...(这里放上面的点击+digest代码)
3. 直接监听Angular UI-Router的状态变化
如果单页应用的路由跳转没有触发浏览器的navigation事件,page.waitForNavigation()就会失效。这时候可以直接监听Angular的$stateChangeSuccess事件:
const startBtn = ".button-selector"; const targetStateName = "your.target.state.name"; // 替换成你要跳转的目标状态名 await page.waitFor(startBtn); // 等待目标路由状态加载完成 await page.evaluate((selector, targetState) => { return new Promise(resolve => { const btn = document.querySelector(selector); const injector = angular.element(document.body).injector(); const $state = injector.get('$state'); const $rootScope = injector.get('$rootScope'); // 监听路由成功跳转事件 const unregisterListener = $rootScope.$on('$stateChangeSuccess', (_, toState) => { if (toState.name === targetState) { unregisterListener(); // 取消监听,避免内存泄漏 resolve(); } }); btn.click(); if (!$rootScope.$$phase) { $rootScope.$digest(); } }); }, startBtn, targetStateName); await page.waitForSelector(".new-selector");
这些方案的核心是让Puppeteer的操作和Angular的内部状态管理机制对齐,避免异步操作的冲突,毕竟单页应用的路由跳转和传统多页应用的页面刷新逻辑完全不同。
内容的提问来源于stack exchange,提问作者Ivan23
相关产品推荐
相关产品推荐

