使用puppeteer一鍵啓動本地開發環境

背景

puppeteer是一個通過Devtools協議來提供操控chrome/chromium瀏覽器的高階API的NodeJS庫

我負責的一個項目的啓動本地開發環境是這樣的:使用npm run dev指令運行webpack-dev-server服務。暴露出訪問地址:http://localhost:1314。然後登陸部署到內部項目環境下的應用。獲取到賬戶cookie的entrance鍵值對。然後訪問本地的開發環境地址,通過document.cookie語法寫入相應的cookie的entrance鍵值對。刷新本地開發頁面。此時纔可正常訪問相應的服務端接口。

看到puppeteer庫的功能,嘗試以上過程通過puppeteer庫的自動化功能實現,以達到一鍵啓動本地開發環境的目的。

過程

我的打算是通過puppeteer在當前的chrome瀏覽器中進行相關的操作。puppeteer.connect(options)將 Puppeteer 添加到已有的 Chromium 實例。但是options參數中的browser.wsEndpoint字段表示一個瀏覽器 websocket 端點鏈接,該鏈接是由browser.wsEndpoint()獲取。

正常不能獲取到正常運行的瀏覽器的wsEndpoint

How do I access the browser target?
The endpoint is exposed as webSocketDebuggerUrl in /json/version. Note the browser in the URL, rather than page. If Chrome was launched with --remote-debugging-port=0 and chose an open port, the browser endpoint is written to both stderr and the DevToolsActivePort file in browser profile folder.

HTTP Endpoints: If started with a remote-debugging-port, these HTTP endpoints are available on the same port.

因此,我決定通過puppeteer.launch語法開啓一個新的瀏覽器實例,然後在這個新的瀏覽器上進行自動化操作。相關的啓動參數如下:

const browser = puppeteer.launch({
    executablePath: 'C:\\chrome.exe',
    headless: false, // 關閉無頭模式
    ignoreHTTPSErrors: false, // 在導航期間忽略 HTTPS 錯誤
    args: ['--start-maximized', '--disable-extensions-expect=D:\\vue-devtools'] // 最大化啓動,開啓vue-devtools插件
    defaultViewport: { // 爲每個頁面設置一個默認視口大小
        width: 1920,
        height: 1080
    }
})

後續是將背景節中的描述的操作使用puppeteer實現出來,代碼如下:

// run-browser.js
const main = async () => {
    // ... 啓動瀏覽器實例的代碼
    const page = await browser.newPage()
    await page.goto('http://inner.develop.net/index.html/#home')

    await page.click('.to-login-btn') // 打開登陸彈窗
    await page.type('input.phone', '18888888888', { delay: 20 }) // 輸入賬戶
    await page.type('input.pwd', '123456', { delay: 20 }) // 輸入密碼
    await page.click('button.login') // 點擊登陸按鈕
    await page.waitForNavigation({ waitUntil: 'networkidle0' }) // 等待頁面導航結束

    const entrance = await page.evaluate(() => document.cookie.split(';')[0].split('=')[1]) // 獲取頁面cookie中的entrance值

    const devPage = await browser.newPage()
    await devPage.goto('http://localhost:1314') // 導航到開發頁面
    await devPage.waitForNavigation({ waitUntil: 'networkidle0' })
    // 設置開發頁面的cookie
    await devPage.evaluate((entranceValue) => {
        document.cookie = `entrance=${entrance}`
    }, entrance)
    // 刷新頁面
    await devPage.reload()
}

使用node run-browser.js指令運行,效果很好。但又出現一個問題:我開啓本地開發環境,先要運行npm run dev啓動webpack-dev-server服務,還要運行npm run browser執行run-browser腳本文件,並沒有之前說好的一鍵啓動呀。

這時,我的做法是利用npm的腳本鉤子特性,有如下代碼:

{
    "scripts": {
        "dev": "webpack",
        "postdev": "node run-browser.js"
    }
}

結果並沒有得到預期結果,細思原因,猜測npm run dev指令在啓動webpack本地服務後,並沒有釋放當前的node進程執行權限,因爲它需要時刻監聽項目文件以及時編譯文件。而運行postdev拿不到node執行環境,所以纔會毫無反應。

我想到node有一個語法spawn,可以在當前node進程中生成一個子進程。於是,我在node執行完npm run dev指令後,再使用spawn語法手動執行node browser指令。現在的問題是我如何確認webpack本地服務執行結束的時機。

最終我在webpack-dev-servergit倉庫中找到了解決方案:

// webpack-dev-server/test/cli/cli.test.js
    cp.stdout.on('data', (data) => {
      const bits = data.toString();
      const portMatch = /Project is running at http:\/\/localhost:(\d*)\//.exec(
        bits
      );

      if (portMatch) {
        runtime.cp.port = portMatch[1];
      }

      if (/Compiled successfully/.test(bits)) {
        expect(cp.pid !== 0).toBe(true);
        cp.kill('SIGINT');
      }
    });

我只要在初次監聽到data事件中正則匹配到Compiled successfully,則說明webpack本地服務開啓成功,所以最後的腳本代碼是這樣的:

const { spawn } = require('child_process')

const npmDev = spawn('npm', ['run', 'dev']) // 啓動本地webpack服務

npmDev.on('data', (data) => {
    const bits = data.toString()

    if (/Compiled successfully/.test(bits) && process.env.WEBPACK_DEV_STATUS !== 'initialed') {
        spawn('npm', ['run', 'browser']) // 運行run-browser.js腳本
        // 設置WEBPACK_DEV_STATUS狀態,以避免在修改項目文件後,webpack編譯成功後觸發`npm run browser`指令
        process.env.WEBPACK_DEV_STATUS = 'initialed'
    }
})

結語

這下,終於可以真的一鍵啓動本地開發環境了,開心!

另,這個功能實現過程中踩的幾個坑:

  1. 考慮如何將Puppeteer 添加到已有的 Chromium 實例,結果:很難做,還是打開一個新的瀏覽器實例來的好。
  2. 啓動的瀏覽器窗口沒有最大化,也沒有最重要的vue-devtools插件,結果:使用啓動參數:args: ['--start-maximized', '--disable-extensions-expect=D:\\vue-devtools']
  3. npm啓動webpack服務不能使用 npmpost鉤子,結果:使用spawn語法替代。
  4. 如何確認webpack本地服務執行結束的時機,結果:監聽data事件,正則匹配到Compiled successfully(看項目源碼,笑!)。

參考鏈接

  1. puppeteer 中文文檔
  2. browser endpoint
  3. child_process 模塊中文文檔
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章