evaluate — это метод, который используется в Puppeteer для
выполнения JavaScript-кода внутри контекста браузера. В отличие от
обычного кода Node.js, который выполняется в процессе Node, код,
переданный в evaluate, выполняется внутри браузера, в
контексте страницы. Этот метод предоставляет гибкость в тестировании,
однако обладает рядом ограничений и особенностей, которые важно
учитывать.
Метод page.evaluate() позволяет запускать функцию,
принимающую аргументы, внутри контекста страницы. Это означает, что код
выполняется непосредственно в браузере, а не в Node.js, что позволяет
манипулировать DOM, взаимодействовать с элементами страницы, извлекать
данные и выполнять другие операции, доступные только в контексте
браузера.
Пример:
const result = await page.evaluate(() => {
return document.title;
});
console.log(result); // Выведет название страницы
В этом примере код внутри evaluate выполняется
непосредственно в браузере, и результат его выполнения передается
обратно в Node.js.
Одной из ключевых особенностей evaluate является то, как
данные передаются между контекстами — между Node.js и браузером. Всё,
что передается в evaluate, сериализуется и отправляется
через протокол DevTools. Это означает, что передавать можно только
данные, которые могут быть сериализованы в JSON.
Что передать можно:
Что передать нельзя:
Пример с передачей объекта:
const result = await page.evaluate((data) => {
return data.message;
}, { message: "Hello, Puppeteer!" });
console.log(result); // Выведет "Hello, Puppeteer!"
Асинхронность: В отличие от обычных
JavaScript-операций, код внутри evaluate выполняется
синхронно. Это означает, что промисы и асинхронные операции (например,
запросы к серверу, работа с таймерами) не могут быть использованы
напрямую внутри этой функции. Если необходимо использовать асинхронные
операции, рекомендуется использовать подход с await и
асинхронными функциями, но только внутри самого вызова
evaluate. Например:
const result = await page.evaluate(async () => {
const response = await fetch(&
const data = await response.json();
return data;
});
Отсутствие доступа к Node.js API: Код, выполняемый
внутри evaluate, не имеет доступа к Node.js API, таким как
модули файловой системы (fs), сеть, процессы и т. д. Он
работает исключительно в контексте браузера. Это ограничивает
возможность манипуляций с серверной частью, что важно учитывать при
построении тестов.
Безопасность контекста: Код внутри
evaluate выполняется в контексте страницы и имеет доступ
только к тому, что доступно в браузере (DOM, глобальные объекты, окна).
Это важно с точки зрения безопасности, так как тесты и другие операции
не могут нарушить изоляцию окружения.
Изоляция контекста: Каждое выполнение
evaluate происходит в отдельном контексте. Это значит, что
изменения, сделанные на одной странице или в одном контексте
evaluate, не будут видны в других контекстах, если не
переданы явно. Важно помнить об этом при разработке сложных тестов, где
требуется работа с множеством страниц.
Метод evaluate предоставляет мощный инструмент для
манипуляций с DOM. Можно извлекать информацию о текущем состоянии
страницы, взаимодействовать с элементами, изменять их и так далее.
Пример получения значения из поля ввода:
const inputValue = await page.evaluate(() => {
return document.querySelector('input[type="text"]').value;
});
console.log(inputValue); // Выведет значение из текстового поля
Важно помнить, что весь код внутри evaluate выполняется в
контексте браузера, и взаимодействие с DOM будет иметь тот же эффект,
что и при обычной работе с браузером. Например, вызов
document.querySelector будет искать элемент в текущем DOM,
и если страница была изменена динамически, результат будет зависеть от
текущего состояния страницы.
Можно использовать сторонние библиотеки внутри evaluate, но
важно понимать, что они должны быть загружены в контекст страницы. Это
можно сделать, например, с помощью добавления скриптов через
page.addScriptTag() или встраивания библиотек с
использованием <script>.
Пример с использованием библиотеки:
await page.addScriptTag({ url: 'https://cdn.jsdelivr.net/npm/lodash' });
const result = await page.evaluate(() => {
return _.join(['Hello', 'world'], ' ');
});
console.log(result); // Выведет "Hello world"
Сложные вычисления или повторяющиеся вызовы evaluate могут
существенно повлиять на производительность тестов. Каждый вызов требует
сериализации и передачи данных между Node.js и браузером, что может быть
дорогостоящим в плане времени. Для повышения производительности следует
минимизировать количество вызовов evaluate, если возможно,
и избегать больших объектов и сложных вычислений внутри него.
Минимизация передачи данных: Вместо того чтобы передавать сложные объекты, лучше работать с минимальными данными, такими как индексы, идентификаторы и другие идентифицируемые сущности.
Объединение нескольких операций в одном вызове: Вместо
того чтобы делать несколько отдельных вызовов evaluate,
можно объединить несколько операций в одном вызове для снижения нагрузки
на протокол DevTools.
Использование кеширования: Для получения повторяющихся данных можно кешировать результаты в переменные или использовать методы для работы с сохраненными состояниями, чтобы избежать лишних запросов.
Отладка кода внутри evaluate может быть сложной из-за того,
что ошибки могут быть трудно воспроизводимы в контексте браузера.
Рекомендуется использовать console.log внутри
evaluate для вывода данных о текущем состоянии:
const result = await page.evaluate(() => {
console.log('Executing code inside browser');
return document.title;
});
Это позволит увидеть, что происходит внутри браузера, и упростит процесс отладки.
Метод evaluate в Puppeteer — мощный инструмент для работы с
браузером, однако его использование требует внимательности к
ограничениям передачи данных, особенностям асинхронности и
взаимодействию с DOM. Для эффективной работы с Puppeteer важно понимать,
как передавать и обрабатывать данные, минимизировать накладные расходы и
оптимизировать тесты для обеспечения высокой производительности.