4

試圖反應渲染與運行在我的Node.js環境我有以下問題chrome puppeteer 反應成分與谷歌鉻合金木偶

  • 記錄element讓我在無頭鉻控制檯:console.log(element) =><div id="test-wrapper"></div>
  • 在終端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); 
    }); 
    

所以試圖做...

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

...顯然會導致錯誤(node:90555) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Invariant Violation: _registerComponent(...): Target container is not a DOM element.

我覺得即使我設法讓我在這裏失去了一些東西的DOM元素注入反應應用程序。

+0

testDocument.html中有什麼?它是否具有呈現反應應用程序的所有先決條件?如果是這樣,你應該在評估函數中渲染它,而不是在它之外(console.log(testWrapper))現在。 – tomahaug

+0

可能會使極簡主義存儲庫重現錯誤,將會輕鬆檢查問題。 –

回答

2

.evaluate不返回一個dom元素。而且,您正在嘗試修改不同上下文中的元素。瀏覽器窗口中的頁面和nodeJS中的上下文完全不同。

以下是處理React和Puppeteer的不同方法。 首先,我有一個入口文件,我將函數導出到窗口。

通過這樣做,我可以輕鬆地從瀏覽器上下文訪問它。您可以實際導出它並嘗試使用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文件。現在,讓我們來操縱木偶的文件,

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

甜!你好,反應:)

哦!如果由於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 
+0

那麼對付木偶師並作出反應的正確方法是什麼?據我瞭解,我有兩個選擇:a)傳遞一個url來呈現一個反應頁面給puppeteer,或者b)傳遞一個反應組件作爲html(ReactDOMServer。renderToString(元素))給木偶。我有一些我想避免的複雜登錄邏輯,所以選項a)不是首選。使用選項b)我遇到了需要componentDidMount的問題,不再運行服務器端(不能移動到componentWillMount)....對此有何建議? – usagidon

+0

你想要做什麼? –

+1

那裏,用實際工作示例更新了實際答案。 –