能否将React组件传递给puppeteer?

5

我有一个React组件,其中包含一些componentDidMount逻辑:

export default class MyComponent {
    componentDidMount() {
        // some changes to DOM done here by a library  
    }

    render() {
        return (
            <div>{props.data}</div>
        );
    }
}

是否有可能通过props传递此组件,以便执行componentDidMount()中的所有内容,并以某种方式传递给puppeteer以便截取屏幕截图? 大致如下:

const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();

const html = ReactDOMServer.renderToString(<MyComponent data='' />); <-- but this skips the componentDidMount logic
await page.setContent(html);
await page.screenshot({ path: 'screenshot.png' });

我知道我可以使用page.goto(),但是我有一些复杂的登录逻辑,我希望通过这样的快捷方式避免它,而是直接将所有需要的属性直接传递给组件。


1
如果你想避免太多的webpack调试,可以考虑使用jest-puppe-shots npm包。我还没有使用过它,但是正在研究,看起来是一个不错的主意。 - Tope
1个回答

10

我在这里回答了这个问题 让我们在这里尝试相同的操作。

安装babel、webpack和puppeteer包。

{
  "name": "react-puppeteer",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "compile": "webpack",
    "build": "webpack -p",
    "start": "webpack && node pup.js"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.1",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "webpack": "^3.10.0",
    "webpack-dev-middleware": "^2.0.3"
  },
  "dependencies": {
    "puppeteer": "^0.13.0"
  }
}

准备 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
  }
};
创建入口文件,在该文件中,不要直接挂载元素,而是将其导出到窗口,以便稍后访问。
import React from 'react';
import { render } from 'react-dom';

class Hello extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// accept a name for example and a domNode where to render
function renderIt(name, domNode) {
  render(<Hello name={name} />, domNode);
}

window.renderIt = renderIt;

当我们运行webpack时,它将生成一个bundle.js文件。我们可以在puppeteer上使用它。

他们已经弃用了puppeteer上的injectFile函数,所以我们将重新激活它。这是一个示例repo,您可以通过yarn添加它。

https://github.com/entrptaher/puppeteer-inject-file

现在,让我们创建一个Puppeteer脚本。

const puppeteer = require('puppeteer');
const injectFile = require('puppeteer-inject-file');

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

当我们运行这个代码时,结果如下:

enter image description here

如果我们添加了 componentDidMount() 方法调用,我们也可以实现这个功能。但是如果我们想要进行更多的修改,我们就必须让 puppeteer 脚本等待执行,在其他问题中已经多次讨论过。

假设我们现在有一个状态,它将在组件加载完成后返回一些内容。

class Hello extends React.Component {
  state = {
    jokes: null
  };

  componentDidMount() {
    const self = this;
    const jokesUrl = `http://api.icndb.com/jokes/random?firstName=John&amp;lastName=Doe`;
    fetch(jokesUrl)
      .then(data => data.json())
      .then(data => {
        self.setState({
          jokes: data.value.joke
        });
      });
  }

  render() {
    if(!!this.state.jokes){
      return <p id='quote'>{this.state.jokes}</p>
    }
    return <h1>Hello, {this.props.name}</h1>;
  }
}

在 Puppeteer 中,我可以像这样等待元素:

  ...
  await injectFile(page, require.resolve('./bundle.js'));
  await page.evaluate(() => {
    renderIt("Someone", document.querySelector('div'));
  });
  await page.waitFor('p#quote');
  ...

我们可能需要 babel-preset-stage-2,但我会让你来决定。这是结果:

enter image description here

剩下的问题你自己解决吧 :) ...


感谢您的耐心等待!我现在意识到我一直试图避免使用webpack,但我想这是正确的方法,再次感谢! - blub
“John Norris?”我对这个笑话的有效性表示怀疑。 - chantey
我有点困惑。这一切都在后端(nodejs)上运行吗?你怎么能访问window对象? - saad-mb

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