是否可以将 React 组件传递给 puppeteer?
Is it possible to pass a React Component to puppeteer?
我有一个带有一些 componentDidMount 逻辑的 React 组件:
export default class MyComponent {
componentDidMount() {
// some changes to DOM done here by a library
}
render() {
return (
<div>{props.data}</div>
);
}
}
是否可以通过 props 传递此组件,以便执行 componentDidMount() 中的所有内容,以某种方式操纵木偶以截取屏幕截图?沿着这些线的东西:
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()
,但我有一些复杂的登录逻辑,我想使用这样的快捷方式来避免,而是将所有需要的道具直接传递给组件?
我回答了这个问题。
让我们在这里尝试同样的方法。
安装 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
}
};
创建入口文件,
在这个文件上,不是直接挂载元素,而是将其导出到 window 以便我们稍后访问它。
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
现在,让我们创建一个人偶脚本。
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();
})();
当我们运行这个时,我们得到以下结果,
如果我们添加一个 componentDidMount()
调用,我们也可以这样做。但是如果我们想做更多的修改,我们必须让puppeteer脚本等待在其他问题中多次讨论过的。
假设我们现在有一个状态,一旦加载组件就会 return 一些东西。
class Hello extends React.Component {
state = {
jokes: null
};
componentDidMount() {
const self = this;
const jokesUrl = `http://api.icndb.com/jokes/random?firstName=John&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,但我会把它留给你。这是结果,
自己解决剩下的问题 :) ...
我有一个带有一些 componentDidMount 逻辑的 React 组件:
export default class MyComponent {
componentDidMount() {
// some changes to DOM done here by a library
}
render() {
return (
<div>{props.data}</div>
);
}
}
是否可以通过 props 传递此组件,以便执行 componentDidMount() 中的所有内容,以某种方式操纵木偶以截取屏幕截图?沿着这些线的东西:
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()
,但我有一些复杂的登录逻辑,我想使用这样的快捷方式来避免,而是将所有需要的道具直接传递给组件?
我回答了这个问题
安装 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
}
};
创建入口文件, 在这个文件上,不是直接挂载元素,而是将其导出到 window 以便我们稍后访问它。
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
现在,让我们创建一个人偶脚本。
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();
})();
当我们运行这个时,我们得到以下结果,
如果我们添加一个 componentDidMount()
调用,我们也可以这样做。但是如果我们想做更多的修改,我们必须让puppeteer脚本等待在其他问题中多次讨论过的。
假设我们现在有一个状态,一旦加载组件就会 return 一些东西。
class Hello extends React.Component {
state = {
jokes: null
};
componentDidMount() {
const self = this;
const jokesUrl = `http://api.icndb.com/jokes/random?firstName=John&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,但我会把它留给你。这是结果,
自己解决剩下的问题 :) ...