在页面中执行 JavaScript(evaluate)
本教程共 59 篇 · 第 54 篇 · 更新于 2026-08-04 · 约 7 分钟阅读
本节目标:学完你能把一段 JS 丢进浏览器里执行,把结果或元素带回 Node 测试环境,绕开 Playwright API 够不着的地方。
Playwright 的 API 很全,但总有些事它没直接封装——比如读 document.location.href、调页面里已有的某个函数。这时候用 page.evaluate(),把 JS 函数送进浏览器跑,再把结果带回来。
两套环境不互通
你的测试跑在 Node.js 里,页面跑在浏览器里。它们是两台不同的「虚拟机」,互相看不见。所以 evaluate 里的函数能直接用 window、document 这些浏览器全局变量。
const href = await page.evaluate(() => document.location.href);
如果返回的是 Promise,或函数是 async 的,evaluate 会自动等它 resolve:
const status = await page.evaluate(async () => {
const response = await fetch(location.href);
return response.status;
});
不能直接用外面的变量
最常见的新手坑:把测试里的变量直接写进 evaluate 函数体。它运行在浏览器里,根本不认识这个变量。
// ❌ 错:浏览器里没有 data 这个变量
const data = 'some data';
const result = await page.evaluate(() => {
window.myApp.use(data);
});
正确做法是把它作为参数显式传进去:
// ✅ 对:把 data 当参数传
const data = 'some data';
const result = await page.evaluate(data => {
window.myApp.use(data);
}, data);
Warning记住铁律:测试环境的变量,必须当参数传进
evaluate,绝不能当闭包直接用。
参数能传什么
evaluate 只接受一个可选参数,它可以是「可序列化值」和「JSHandle(句柄)」的混合:
// 原始值
await page.evaluate(num => num, 42);
// 数组
await page.evaluate(array => array.length, [1, 2, 3]);
// 对象
await page.evaluate(object => object.foo, { foo: 'bar' });
拿到元素要用 JSHandle
想操作页面里的某个对象(而不是它的文本),先 evaluateHandle 拿句柄,再当参数传:
const button = await page.evaluateHandle('window.button');
await page.evaluate(button => button.textContent, button);
句柄也能用更顺手的 JSHandle.evaluate:
await button.evaluate((button, from) => button.textContent.substring(from), 5);
多个句柄可以塞进一个对象或数组传:
const button1 = await page.evaluateHandle('window.button1');
const button2 = await page.evaluateHandle('window.button2');
await page.evaluate(
({ button1, button2 }) => button1.textContent + button2.textContent,
{ button1, button2 }
);
Note解构时属性名要对得上,且整体要加括号——这是 JS 的语法要求,不是 Playwright 的规定。
在页面加载前埋脚本
有时你想在页面一开始加载就改点东西,比如把随机数固定住,方便测试可复现。用 page.addInitScript():
// mocks/preload.js
Math.random = () => 42;
import { test, expect } from '@playwright/test';
import path from 'path';
test.beforeEach(async ({ page }) => {
await page.addInitScript({ path: path.resolve(__dirname, '../mocks/preload.js') });
});
短脚本不想单建文件,直接传函数也行,还能带参数:
test.beforeEach(async ({ page }) => {
const value = 42;
await page.addInitScript(value => {
Math.random = () => value;
}, value);
});
Tip
addInitScript在每个页面导航前都会执行,适合放全局 mock 或测试数据。它和evaluate的区别是:一个在加载前埋,一个在加载后跑。
什么时候用它
别一上来就 evaluate。能用 Locator 和断言搞定,就用它们——更稳、更抗页面变动。只有这几种情况才值得掏出 evaluate:
- 读 Playwright 没直接暴露的页面内部状态。
- 调页面里已有的工具函数。
- 页面加载前就要改全局行为(配合
addInitScript)。
一句话:evaluate 是「翻窗户」的备用手段,能走正门(Locator/断言)就别翻窗。
小结
这章学了 page.evaluate() 把 JS 送进浏览器跑、再把结果带回来。核心铁律:Node 和浏览器是两套环境,测试里的变量必须当参数传进去,不能当闭包直接用。拿到元素要用 evaluateHandle 拿句柄。addInitScript 可以在页面加载前埋脚本,适合全局 mock。能用 Locator 搞定的别掏 evaluate,它是兜底手段,不是常规武器。