React与Google Chrome的Puppeteer

6

我正在尝试使用Chrome Puppeteer在我的Node.js环境中呈现React组件,但是遇到了以下问题:

尝试渲染React组件

  • logging element gives me in the headless chrome console: console.log(element) => <div id="test-wrapper"></div>
  • testWrapper in the terminal console.log(testWrapper) => {}

    puppeteer.launch().then(async browser => {
    
        const page = await browser.newPage();
    
        const testDocumentPath = path.resolve('./lib/components/util/testDocument.html');
        await page.goto(`file://${testDocumentPath}`);
    
        const testWrapper = await page.evaluate((selector) => {
            const element = document.querySelector(selector);
            console.log(element);
    
            return element;
        }, '#test-wrapper');
    
        console.log(testWrapper);
    });
    

So trying to do …

ReactDOM.render(
    <div>{':)'}</div>,
    testWrapper
);

...显然导致错误(node:90555)未处理的Promise拒绝警告:未处理的Promise拒绝(拒绝ID:1):不变违规:_registerComponent(...):目标容器不是DOM元素。

我觉得即使我成功获取了DOM元素,我在这里还缺少一些东西来注入React应用程序。


测试文档(testDocument.html)中有什么内容?它是否拥有呈现React应用程序的所有前提条件?如果是这样,您应该在evaluate函数中呈现它,而不是在现在console.log(testWrapper)之外。 - tomahaug
也许可以创建一个简洁的代码库来重现这个错误,这样有助于检查问题。 - Adi Prasetyo
1个回答

4
.evaluate 不返回 DOM 元素。而且,您正在尝试修改不同上下文中的元素。浏览器窗口中的页面和您在 nodeJS 中拥有的上下文完全不同。
以下是处理 React 和 Puppeteer 的另一种方法。 首先,我有一个入口文件,在其中将该函数导出到 window 。
通过这样做,我可以轻松地从浏览器上下文中访问它。您可以导出它并尝试使用 expose-loader 等工具。我将使用 webpack 进行构建。
import React from 'react';
import { render } from 'react-dom';

function Hello() {
  return <h1>Hello from React</h1>;
}

function renderIt(domNode) {
  render(<Hello />, domNode);
}

window.renderIt = renderIt;

在webpack配置中,
const webpack = require('webpack');

const loaders = [
  {
    test: /\.jsx?$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    query: {
      presets: ['babel-preset-es2015', 'babel-preset-react'],
      plugins: []
    }
  }
];

module.exports = {
  entry: './entry.js',
  output: {
    path: __dirname,
    filename: 'bundle.js',
    libraryTarget: 'umd'
  },
  module: {
    loaders: loaders
  }
};

现在每当我运行webpack时,它都会为我创建一个bundle.js文件。现在让我们来看一个puppeteer文件,
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto('https://github.com');
  await page.addScriptTag({ path: require.resolve('./bundle.js') });
  await page.evaluate(() => {
    renderIt(document.querySelector('div.jumbotron.jumbotron-codelines > div > div > div > h1'));
  });
  await page.screenshot({ path: 'example.png' });
  await browser.close();
})();

正如您所看到的,我正在使用之前向窗口公开的 renderIt 函数。当我运行它时,这就是结果,

enter image description here

甜!来自React的问候 :)
哦!如果由于CORS问题而无法执行页面上的脚本,您可以使用旧的injectFile函数进行注入,直到他们修复addScriptTag函数的问题或从injectFile中删除弃用。
/**
 * injects file to puppeteer page context
 * @param  {Object} page     context where to execute the script
 * @param  {String} filePath path of specific script
 * @return {Promise}         Injects content to page context
 */
const fs = require('fs');

async function injectFile(page, filePath) {
  let contents = await new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) return reject(err);
      resolve(data);
    });
  });
  contents += `//# sourceURL=` + filePath.replace(/\n/g, '');
  return page.mainFrame().evaluate(contents);
}

// usage: await injectFile(page, require.resolve('FILE PATH'));
// export it if you want to keep things seperate

那么,如何正确处理puppeteer和react呢?据我所知,我有两个选择:a)将渲染react页面的url传递给puppeteer;或者b)将react组件作为html(ReactDOMServer.renderToString(element))传递给puppeteer。我有一些复杂的登录逻辑需要避免,因此不希望选择a)。使用选项b),我遇到了一个问题,即componentDidMount中所需的内容不再在服务器端运行(无法移动到componentWillMount)... 有什么建议吗? - blub
你究竟想要做什么? - Md. Abu Taher
2
已经更新了实际答案,并提供了真实的工作示例。 - Md. Abu Taher
感谢您详细的回答!我的目标是:在Puppeteer中截取自己的React页面,但由于复杂的登录要求,我想通过将组件渲染为HTML(使用ReactDOMServer.renderToString(element))并将其传递给Puppeteer(await page.setContent(html))来简化操作,但正如我所说,我遇到了componentDidMount()不运行的问题...所以我希望有更多的技巧可以解决这个问题 :/ - blub
请发布一个新问题:是否可以将React组件传递给Puppeteer? - blub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接