淮城一只猫 · 奇技淫巧

Webview Vue 网页调试

淮城一只猫 撰文 5 分钟

Webview Vue 网页调试

Cap1 前言

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

Cap2 善用 window.alert 方法

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

Cap3 使用 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 程序依然并不满足调试要求,因为无法调试响应式数据。

Cap4 使用 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

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

Cap5 后续

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