是否可以将 React 组件传递给 puppeteer? [英] Is it possible to pass a React Component to puppeteer?

查看:45
本文介绍了是否可以将 React 组件传递给 puppeteer?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有一些 componentDidMount 逻辑的 React 组件:

导出默认类 MyComponent {componentDidMount() {//对 DOM 的一些更改由库在此处完成}使成为() {返回 (<div>{props.data}</div>);}}

是否可以通过 props 传递这个组件,以便 componentDidMount() 中的所有内容都被执行,以某种方式操纵以截取屏幕截图?一些类似的东西:

const browser = await puppeteer.launch({ headless: true });const page = await browser.newPage();const html = ReactDOMServer.renderToString(<MyComponent data=''/>);<-- 但这会跳过 componentDidMount 逻辑等待 page.setContent(html);等待 page.screenshot({ path: 'screenshot.png' });

我知道我可以使用 page.goto(),但是我有一些复杂的登录逻辑,我想使用这样的快捷方式来避免这些逻辑,而是将所有需要的道具直接传递给组件?

解决方案

我在

如果我们添加一个 componentDidMount() 调用,我们也可以这样做.但是如果我们想要做更多的修改,我们必须让 puppeteer 脚本等待在其他问题中多次讨论过的内容.

假设我们现在有一个状态,它会在加载组件后返回一些内容.

class Hello 扩展 React.Component {状态 = {笑话:空};componentDidMount() {const self = this;constjokesUrl = `http://api.icndb.com/jokes/random?firstName=John&amp;lastName=Doe`;获取(笑话网址).then(data => data.json()).then(数据=> {self.setState({笑话:data.value.joke});});}使成为() {如果(!!this.state.jokes){返回<p id='quote'>{this.state.jokes}</p>}return 

你好,{this.props.name}

;}}

在puppeteer上,我可以等这样的元素,

<代码> ...等待注入文件(页面,require.resolve('./bundle.js'));等待 page.evaluate(() => {renderIt("Someone", document.querySelector('div'));});等待 page.waitFor('p#quote');...

我们可能需要 babel-preset-stage-2,但我会把它留给你.这是结果,

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

I have a React component with some componentDidMount logic:

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

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

Is it possible to pass this component with props so that everything in componentDidMount() gets executed, somehow to puppeteer in order to take a screenshot? Something along these lines:

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' });

I know I could use page.goto(), but I have some complex login logic that I would like to avoid with a shortcut like this and instead pass all the needed props just directly to the component?

解决方案

I answered this question here. Let's try the same here.

Install babel, webpack and puppeteer packages.

{
  "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"
  }
}

Prepare webpack config,

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
  }
};

Create entry file, On this file, instead of mounting the element directly, export it to window so that we can access it later.

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;

When we run webpack, it's going to produce a bundle.js file. We can use it on puppeteer.

They have deprecated the injectFile function on puppeteer, so we are going to resurrect it. Here is a sample repo for that, you can yarn add it.

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

Now, lets create a puppeteer script.

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();
})();

And when we run this, we get following result,

If we added a componentDidMount() call, we could have done that too. But if we are trying to do more modification, we have to make the puppeteer script wait for that which have been discussed many times in other questions.

Say we have a state now which will return something once component is loaded.

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>;
  }
}

On puppeteer, I can wait for the element like this,

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

We might need babel-preset-stage-2 but I'll leave that to you. And here is the result,

Figure the rest of the problem yourself :) ...

这篇关于是否可以将 React 组件传递给 puppeteer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆