0x0 前言

转入前端的时候,就一直做 Hybrid App 开发,所以从 React Native 到 简单的 MuiApi Cloud 都尝试过,除了一些主流的开发方式,基本都有很好的调试工具,像 RN 都会有 ReactNativeDebugger 之类的工具。但由于公司需求,需要简单的 webview 网页开发的时候,RN 显然很笨重了,所以利用 Mui.js 真机框架去完成需求。最近公司有个小项目类似这样,不过 App 这一块是利用 .NetXamarin 技术构建的 App,然后里面一些方法需要端能力才能调用数据,所以这就造成无法从浏览器调试数据。其次我利用 VueCli 生成静态页面,比编写原生的 HTML 页面高效多了,但同样无法抓取到页面动态数据的抓取。

0x1 善用 window.alert 方法

这种方法算是调试中最傻最简单,不过在后面数据多的时候联调的时候就相对麻烦了,这里就不再描述。

0x2 使用 Webview Debug

Webview Debug 调试方法相对而言很好了,使用过程和网页调试的控制台一模一样,基本上没什么限制,对于硬件来讲只支持 Android 4.4 (KitKat)以及以上版本 环境下,并且使用 Chrome 浏览器来支持该功能。

只需要在 Android 环境下添加下面代码即可支持:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
    { WebView.setWebContentsDebuggingEnabled(true); }
}

如果你懒的话,也可以尝试使用 Xposed 框架支持 Webview Debug模式:WebViewDebugHook,可以利用虚拟机尝试,毕竟虚拟机包含 root 权限。

然后打开 chrome://inspect 地址会看到调试的设备,调试方法和网页调试一样,这里就不再描述了。

参考:

但对于 Vue.js 单应用页面来说,页面包含的数据是不好抓取的,变量名混淆,页面数据压缩等等,造就页面调试进一步困难,针对于这个现象也很简单,直接将生成文件编译成和 npm run dev 环境即可:

  • 修改 package.json,新增编译任务
"build:stage": "vue-cli-service build --mode staging",

新增编辑 .env.staging 文件:

NODE_ENV = development
 
# just a flag
ENV = 'staging'
 
# base api
VUE_APP_BASE_API = '/stage-api'
 
VUE_CLI_BABEL_TRANSPILE_MODULES = true

使用环境变量:https://cli.vuejs.org/zh/guide/mode-and-env.html

这样编译后的 dist 文件在控制台可以看到文件源文件,方便变量名打断点调试。不过在调试 Vue 程序依然并不满足调试要求,因为无法调试响应式数据。

0x3 使用 Vue.js devtools

这东西在大家眼里可能觉得只能在浏览器扩展使用,没想过能在 Webview 上使用,在 Chrome 或者 Firefox 浏览器中,里面扩展程序基本都是网页程序,只要是网页程序,相信有办法可以移植到 Webview 使用。

Github vue-devtools 介绍有段 Get standalone Electron app (works with any environment!) 内容吸引了我,虽说只能在 Electron 程序使用,但有办法可以在任何程序上使用,故点进去看介绍说明,就有了大概的思路。

在源码 shell-dev 使用他编译出几个 javascript 文件,注入 Vue 程序即可,现在需要解决的就是让它们能在 file:// 协议中正常运行,在 shell-dev/src/devtools.js

import { initDevTools } from '@front'
import Bridge from '@utils/bridge'
 
const target = document.getElementById('target')
const targetWindow = target.contentWindow
 
// 1. load user app
target.src = 'target.html'
target.onload = () => {
  // 2. init devtools
  initDevTools({
    connect (cb) {
      // 3. called by devtools: inject backend
      inject('./build/backend.js', () => {
        // 4. send back bridge
        cb(new Bridge({
          listen (fn) {
            targetWindow.parent.addEventListener('message', evt => fn(evt.data))
          },
          send (data) {
            console.log('devtools -> backend', data)
            targetWindow.postMessage(data, '*')
          }
        }))
      })
    },
    onReload (reloadFn) {
      target.onload = reloadFn
    }
  })
}
 
function inject (src, done) {
  if (!src || src === 'false') {
    return done()
  }
  const script = target.contentDocument.createElement('script')
  script.src = src
  script.onload = done
  target.contentDocument.body.appendChild(script)
}

上述代码的 inject 方法会注入失败,查看控制台发现是 iframe 通信跨域问题,因为是 file:// 导致的,所以这边修改成:

import { initDevTools } from '@front'
import Bridge from '@utils/bridge'
 
const target = document.getElementById('target')
const targetWindow = target.contentWindow
 
// 1. load user app
target.src = 'target.html'
target.onload = () => {
  // 2. init devtools
  initDevTools({
    connect (cb) {
      // 3. send back bridge
      cb(new Bridge({
        listen (fn) {
          targetWindow.parent.addEventListener('message', evt => fn(evt.data))
        },
        send (data) {
          console.log('devtools -> backend', data)
          targetWindow.postMessage(data, '*')
        }
      }))
    },
    onReload (reloadFn) {
      target.onload = reloadFn
    }
  })
}

直接注册 connect 方法,然后在 target.html 文件手动引入:

<script src="build/backend.js"></script>

然后打包文件在 webview 测试效果:

img_1140

img_1139

很显然成功了,这样很方便达到调试程序效果。

0x4 后续

在编译 Vue-Cli 要做到自动注入上面几个所需要的文件,并且判断编译环境,例如开发环境和生产环境,一般来说生成环境不需要引入 Vue DevTools 那几个文件,由于时间仓促,这边暂时没有示例代码,所以有时间再更新博文完善下!