迄今爲止最好的Webview遠程調試神器:devtools-pro

Devtools-Pro

A web remote debugging tools, based on Chrome DevTools.

image

🎉 Features

  • 基於 Chrome DevTools
  • 基於 WebSocket 遠程調試
  • 可擴展,支持自定義插件

📦 Installation

npm i -g devtools-pro
# OR
yarn global add devtools-pro
複製代碼

命令行配置項

devtools-pro -h
# or
dp -h
複製代碼
Options:
  -h, --help      Show help                                            [boolean]
      --plugins   Add plugins                                            [array]
      --config    Provide path to a devtools configuration file e.g.
                  ./devtools.config.js     [string] [default: "devtools.config"]
  -o, --open      Open browser when server start       [boolean] [default: true]
      --https     Use HTTPS protocol.                                  [boolean]
  -p, --port      Port to use [8899]                                    [number]
      --verbose   Displays verbose logging            [boolean] [default: false]
      --hostname  Address to use [0.0.0.0]                              [string]
  -v, --version   Show version number                                  [boolean]
複製代碼

配置文件devtools.config.js

爲了方便項目統一配置,DevTools-pro 支持配置文件,能夠在項目中建立一個名爲devtools.config.js的文件,支持的配置項以下:css

  • logLevel:日誌級別,支持silent verbose
  • port:server 端口號,默認 8899
  • hostname:默認 0.0.0.0
  • plugins:配置插件,下面介紹
  • https:server 默認是 http 的,若是要啓用 https,能夠設置https=true,或者使用此字段配置nodejs/https 模塊相關配置,例如:
https:{
    key: fs.readFileSync('/path/to/server.key'),
    cert: fs.readFileSync('/path/to/server.crt'),
    ca: fs.readFileSync('/path/to/ca.pem'),
}
複製代碼

開發

  1. clone
mkdir devtools-pro
git clone git@github.com:ksky521/devtools-pro.git devtools-pro
複製代碼
  1. 安裝依賴 & 初始化
yarn
# 初始化:將chrome-devtools-frontend/front_end複製出來
sh init.sh
複製代碼
  1. 開始開發
yarn dev
複製代碼

訪問:html

  • home:localhost:8080/home.html
  • 調試頁面 demo:localhost:8080/demo.html
  • inspector:localhost:8989/inspector.html
  • backend.js: localhost:8080/backend.js

原理

DevTools-pro 是基於chrome-devtools-frontend進行開發的,經過自建 WebSocket 通道實現 Frontend 和 Backend 的通訊。前端

DevTools 主要由四部分組成:vue

  • Frontend:調試器前端,默認由 Chromium 內核層集成,DevTools Frontend 是一個 Web 應用程序;
  • Backend:調試器後端,Chromium、V8 或 Node.js;在這裏咱們主要是引入的 backend.js
  • Protocol:調試協議,調試器前端和後端使用此協議通訊。 它分爲表明被檢查實體的語義方面的域。 每一個域定義類型、命令(從前端發送到後端的消息)和事件(從後端發送到前端的消息)。該協議基於 json rpc 2.0 運行;
  • Message Channels:消息通道,消息通道是在後端和前端之間發送協議消息的一種方式。包括:Embedder Channel、WebSocket Channel、Chrome Extensions Channel、USB/ADB Channel。

這四部分的交互邏輯以下圖所示:node

簡單來講:被調試頁面引入 Backend 後,會跟 Frontend 創建鏈接;在 backend 中,對於一些 JavaScript API 或者 DOM 操做等進行了監聽和 mock,從而頁面執行對應操做時,會發送消息到 Frontend。同時 Backend 也會監聽來自於 Frontend 的消息,收到消息後進行對應處理。react

插件開發

DevTools-pro 是能夠經過插件增長功能的,好比:git

  • 增長 devtools 面板,例如集成 san-devtools、vue-devtools、react-devtools 等到 devtools-pro 中
  • 主動在頁面觸發 Chrome DevTools Protocol(CDP),接收/發送數據,例如將一些特殊的請求或者信息經過 CDP 發送到 devtools frontend 中展現
  • 其餘腦洞大開的想法

插件能夠發佈一個 NPM 包,而後在項目下的devtools.config.js中經過plugins進行添加,一個 plugins 是一個 NPM 包,由如下三部分組成:github

  • frontend:調試器前端,即 Chrome DevTools 的 module,按照 Chrome-Devtools-Frontend 寫法進行定義,也可使用 iframe 進行嵌入
  • backend:調試器後端,即被調試頁面的引入的 js 實現
  • middleware:即 Koa 的中間件,用於加強 server 實現

這三部分根據本身插件的實際功能進行開發,並不是都包含。三部分的定義是在 NPM 包的package.jsondevtools字段,相似:web

{
    name: 'js-native-monitor',
    version: '1.0.0',
    main: 'index.js',
    // ....
    devtools: {
        // middleware
        frontend: {
            name: 'jsna_monitor',
            type: '', // remote/autostart
            dir: 'frontend'
        },
        // backend字段,該文件內容會被merge到backend.js中
        backend: 'index.js',
        // middleware
        middleware: 'middleware.js'
    }
}
複製代碼

Frontend

Frontend 是徹底符合的chrome-devtools-frontend的模塊,package.json中的devtools.frontend包含配置有:chrome

  • name:名字,訪問hostname:port/devtools/${name}/** 則自動轉發到這裏,優先級高於內置和 chrome-devtools-frontend/front_end 文件,若是 name 是 chrome-devtools-frontend/front_end 已經存在的則優先級高於 chrome-devtools-frontend
  • type:可選值:autostartremote,含義參考 Chrome DevTools 具體實現;
  • dir:指定文件夾目錄

dir 文件夾中的重要文件是模塊描述文件module.json,經過文件夾下的 module.json 配置文件進行定義,配置文件有如下幾個屬性:

  • scripts:模塊中包含的 JavaScript 文件數組,這裏的路徑名稱是相對於 module.json 的位置;
  • skip_compilation:相似於腳本,可是 Closure Compiler 不會對這些文件進行類型檢查;
  • resources:模塊使用的非 JavaScript 文件數組;
  • dependencies:模塊使用的其餘模塊的數組;
  • extensions:具備 type 屬性的對象數組。 擴展能夠經過運行時系統查詢,並能夠經過任何模塊中的代碼進行訪問。類型包括 "setting"、"view","context-menu-item"。例如能夠按以下方式註冊出如今設置屏幕中的設置:
{
  "extensions": [
    {
      "type": "setting",
      "settingName": "interdimensionalWarpEnabled",
      "settingType": "boolean",
      "defaultValue": false,
      "storageType": "session",
      "title": "Show web pages from other dimensions"
    },
    ...
  ]
}
複製代碼

DevTools Frontend 經過 Module 和 Extension 機制爲 Application 增長了「插件化」的能力,而後經過配置進行靈活的組裝。

應用舉例

咱們應用作多的多是添加一個面板,例如我要添加一個js-native的面板,則module.json內容以下:

{
    extensions: [
        {
            // 類型
            type: 'view',
            // 位置
            location: 'panel',
            id: 'jsna_monitor',
            // 面板顯示文字
            title: 'jsNative monitor',
            order: 110,
            // 啓動className
            className: 'JSNAMonitor.JSNAMonitor'
        }
    ],
    // 依賴
    dependencies: ['platform', 'ui', 'host', 'components', 'data_grid', 'source_frame', 'sdk'],
    scripts: [],
    // 資源
    modules: ['jsna_monitor.js', 'jsna_monitor-legacy.js', 'JSNAMonitor.js'],
    resources: ['jsna.css']
}
複製代碼

此部分能夠參考@ksky521/js-native-monitor實現。

Backend

當被調試的頁面引入hostname:port/backend.js時,backend 的文件會被合併到backend.js中輸出。這裏提供了全局命名空間$devtools,它的定義在./src/runtime.js中。後面通訊部分會詳細介紹

通訊

在原來的 CDP 基礎上,爲了方便開發插件開發,DevTools-pro 提供了兩種 Backend 和 Frontend 插件的通訊方式:CDP 事件自建 WebSocket

CDP 事件

在 Backend 中,提供了一個全局命名空間$devtools,能夠經過下面方法進行事件註冊。

// backend中代碼
$devtools.registerEvent('PluginName.method', data => {
    const result = '處理完的返回數據';
    console.log(data);
    //...
    return result;
});
// frontend插件中,發送命令給backend
runtime.bridge.sendCommand('PluginName.method', {}).then(a => console.log(111, a));
// 輸出:111,處理完的返回數據
// -> frontend發送數據以後,會獲得一個Promise,獲得的數據是backend的事件處理函數直接返回的數據。
複製代碼

注意:推薦事件命名上採用跟 CDP 一致的方式,即以.間隔,以此來防止命名衝突,形成事件相互覆蓋。

自建 WebSocket

DevTools-pro 自己自帶 WebSocket 服務,因此能夠在 Backend 中使用$devtools.createWebsocketConnection(wsUrl)建立一個 WebSocket 連接:

// backend代碼
const channelId = $devtools.nanoid();
// -> 這裏注意路徑必須是/backend/開頭
const wsUrl = $devtools.createWebsocketUrl(`/backend/${channelId}`);
const ws = $devtools.createWebsocketConnection(wsUrl);
ws.on('message', event => {
    // message
});
// 發送數據
ws.send('hi~');
// ws連接創建成功
ws.on('open', onOpen);
複製代碼

在 Frontend 插件中,須要利用 ChannelId 創建一條相同的 MessageChannel,這時候應該經過 CDP 事件將 channelId 由 Backend,發送的 Frontend:

// backend
$devtools.sendCommand('PluginName.channelId', channelId);
複製代碼

而後在 Frontend 插件中:

runtime.bridge.registerEvent('PluginName.channelId', channelId => {
    const wsUrl = `/frontend/${channelId}`;
    const ws = new WebSocket(wsUrl);
    ws.onmessage = event => {
        console.log(event.data);
    };
    ws.send('i am ready');
});
複製代碼

Middleware

middleware 的定義是在server/Server.js,接受 3 個參數middleware(router, logger, serverInstance)

  • routerkoa-router的實例;
  • loggerconsola對象,有logger.loglogger.infologger.debug等方法;
  • serverInstance是 Server 類實例

應用舉例

給 server 添加 router:

// middleware.js
module.exports = router => {
    router.get('/hi', ctx => {
        ctx.body = 'world';
    });
};
複製代碼

其餘腦洞

自動化測試

咱們能夠啓動 DevTools-pro 以後,經過chrome-remote-interface連接 WebSocket,而後經過發送 CDP 命令,進行自動化測試。

const CDP = require('chrome-remote-interface');

CDP(
    {
        target: 'ws://localhost:8899/frontend/TDBmn-IDKkaIV98iW20Qh'
    },
    async client => {
        const {Page, Runtime} = client;
        await Page.enable();
        const result = Runtime.evaluate({expression: 'window.location.toString()'});
        console.log(result);
    }
);
複製代碼

Vue-DevTools、San-DevTools 等集成

咱們能夠在 frontend 的 module 中,添加一個 iframe 面板:

export class SanDevtoolsPanel extends UI.VBox {
    constructor() {
        super('san_devtools');
        this.registerRequiredCSS('san_devtools/san_devtools.css', {enableLegacyPatching: false});
        this.contentElement.classList.add('html', 'san-devtools');
    }
    wasShown() {
        this._createIFrame();
    }
    willHide() {
        this.contentElement.removeChildren();
    }
    _createIFrame() {
        this.contentElement.removeChildren();
        const iframe = document.createElement('iframe');
        iframe.className = 'san-devtools-frame';
        iframe.setAttribute('src', '/san-devtools.html');
        iframe.tabIndex = -1;
        UI.ARIAUtils.markAsPresentation(iframe);
        this.contentElement.appendChild(iframe);
    }
}
複製代碼

而後在 Frontend 嵌入的頁面中,能夠直接創建本身的 WebSocket 連接直接跟 Backend 進行通訊。

開發插件相關資料

相關文章
相關標籤/搜索