docs/development/debug.md
Hippy 是直接运行于手机的 JS 引擎中的,在 Android 上使用 WebSocket 通过 Chrome DevTools Protocol 与电脑上的 Chrome 进行通讯调试,而 iOS 上使用内置 的 JavaScriptCore 与 Safari 连接进行调试,在较新的 Hippy 版本 iOS 也可以使用 Chrome DevTools 进行调试。
Hippy 中运行的 JS 代码可以来源于本地文件(local file),或者远程服务地址(server)。
@hippy/debug-server-next 就是为了解决调试模式下终端模式获取调试用 JS 文件,以及将 Chrome DevTools Protocol 传输回调试器而诞生。
运行 git clone https://github.com/Tencent/Hippy.git
!> Hippy 仓库使用 git-lfs 来管理 so, gz, otf, png, jpg 文件, 请确保你已经安装 git-lfs。
项目根目录运行命令 npm install 安装前端依赖。
项目根目录运行命令 npx lerna bootstrap 安装前端每一个 package 依赖。(Hippy 采用 Lerna 管理多JS仓库)
项目根目录运行命令 npm run build 编译前端 SDK 包。
选择一个前端范例项目来进行编译,项目根目录运行 npm run buildexample [hippy-react-demo|hippy-vue-demo]。
我们推荐在终端代码中留一个后门,通过一定条件触发后进入调试模式,具体代码可以参考 iOS 和 Android,这里实现了一个 TestModule,当前端调用它的 debug 或 remoteDebug 方法时就会进入调试模式,而终端可以通过其它方式进入。终端打开 Hippy Debug 页面代码如下:
Android:
// 初始化 hippy 引擎
HippyEngine.EngineInitParams initParams = new HippyEngine.EngineInitParams();
// 可选:是否设置为 debug 模式,默认为 false。设置 true 为调试模式,所有 jsbundle 都将从 debug server 上下载
initParams.debugMode = true;
initParams.debugServerHost = "localhost:38989";
// 可选参数 Hippy Server 的 jsbundle 名字,默认为 "index.bundle"。debugMode = true 时有效
initParams.debugBundleName = "index.bundle";
iOS:
- (void)viewDidLoad {
// 开启调试
NSDictionary *launchOptions = @{@"DebugMode": @(YES)};
// 使用默认 http://localhost:38989/index.bundle
NSString *bundleStr = [HippyBundleURLProvider sharedInstance].bundleURLString;
NSURL *bundleUrl = [NSURL URLWithString:bundleStr];
HippyBridge *bridge = [[HippyBridge alloc] initWithDelegate:self
bundleURL:bundleUrl
moduleProvider:nil
launchOptions:launchOptions
executorKey:@"Demo"];
}
- (BOOL)shouldStartInspector:(HippyBridge *)bridge {
return bridge.debugMode;
}
- (NSURL *)inspectorSourceURLForBridge:(HippyBridge *)bridge {
return bridge.bundleURL;
}
Ohos:
let initParams = new EngineInitParams(this.libHippy!, this.abilityContext!, this.getUIContext())
// 是否设置为 debug 模式,默认为 false。设置 true 为调试模式,所有 jsbundle 都将从 debug server 上下载
initParams.debugMode = true;
initParams.debugServerHost = "localhost:38989"; // usb线调试时,这里设置localhost即可
// initParams.debugServerHost = "192.168.76.25:38989"; // 网络调试时,这里请设置正确 JS Server 的 IP
Hippy鸿蒙版本支持 JSVM 和 V8 两个JS引擎,JSVM 性能更好所以默认使用JSVM,不过可以自由切换。
Debug包默认打开了ENABLE_INSPECTOR宏,使得调试功能生效。即:Hippy/framework/ohos/build-profile.json5 里配置了:"arguments": "-DENABLE_INSPECTOR=true"
默认会使用JSVM进行调试,由于JSVM提供的调试接口限制,特别说明:
如需切换到V8进行调试,请修改:
由于鸿蒙上没有提供hdc实时监听手机拔插的接口,usb调试时需要先插着手机再启动调试server(启动调试server时会做手机端口到PC端口的映射)。 如果先启动了调试server,再插上手机,可以手动如下命令进行端口映射。
hdc rport tcp:端口 tcp:端口
有些公司网络会有限制,公司网络限制可能端口限制需要改端口,可能手机和电脑不同网,具体咨询IT部门。个人热点一般无限制。
修改端口需要改2个地方:
安装新一代调试工具: npm i -D @hippy/debug-server-next@latest
修改 Webpack 配置,添加调试服务地址,默认为 http://localhost:38989
module.exports = {
devServer: {
// 调试服务地址
remote: {
protocol: 'http',
// iOS 真机调试时配置为本地局域网 IP
// iOS 模拟器和安卓真机调试时配置为 localhost
// 远程调试时配置为远程服务器 IP 或域名
host: '192.168.1.100',
// 调试服务端口
port: 38989,
},
// 默认为 false,设为 true 调试服务支持多个工程同时调试,彼此之间不会干扰
multiple: false,
// 默认为 false,hippy vue 项目可以手动开启
vueDevtools: false,
// 默认 hot, liveReload 都为 true,如果只想使用 live-reload 功能,请将 hot 设为 false,liveReload 设为 true
hot: true,
liveReload: true,
client: {
overlay: false,
},
},
// ... other config
}
修改 package.json 中的启动编译命令。如果业务通过自定义 cli 启动,参考 打包编译 API 进行配置
{
"scripts": {
// -c 或 --config 提供 webpack config 配置路径
"hippy:dev": "node ./scripts/env-polyfill.js hippy-dev -c ./scripts/hippy-webpack.dev.js"
}
}
!> Node 17+ 不再支持 md4 hash,此处为了兼容 Webpack 的 hash 算法,暂时通过 env-polyfill.js 脚本判断环境来解决,若出现错误将 node ./scripts/env-polyfill.js 移除即可
运行 npm run hippy:dev 启动编译并按需开启用于 HMR 和 Live-Reload 的 Dev Server,编译结束后打印出 bundleUrl 和调试首页地址
<span id="config-bundle">粘贴 bundleUrl 并点击开始按钮</span>
使用调试器开始调试
Safari DevTools:在 Mac 上打开 Safari 的开发菜单(预置 -> 高级 -> 显示开发菜单),然后按下图指引开始调试。Safari 调试器支持 iOS 设备,支持 HMR & Live-Reload, Log, Sources, Memory 等能力。
Chrome DevTools:访问第 4 步打印的调试首页地址开始调试。Chrome 调试器支持 Android & iOS & Ohos 设备,支持 HMR & Live-Reload, Elements, Log, Sources, Memory 等能力。
如果不使用我们的调试主页,也可以主动在 chrome://inspect 打开 DevTools,首先确保 Discover USB devices 的复选框呈未选中状态,然后确保 Discover network targets 选中,并在右侧 Configure 按钮的弹窗中包含了 localhost:38989 调试服务地址,下方的 Remote Target 中应该会出现 Hippy debug tools for V8 字样,点击下方的 inspect 链接即可打开 Chrome 调试器。
尽管 Hippy 是前端框架,但依然运行在终端中,如果终端提供了后门可以直接链接调试服务,那可以直接用终端后门连接终端进行调试。
这里仅以官方范例为准,讲述如何进行调试。
!> 需要注意的是:官方范例为应用最新功能,将 @hippy/react 和 @hippy/vue 做了 alias 到 packages 目录,如果调试官方范例,需要先在 Hippy 项目根目录下运行 npm run build 编译前端 SDK;或者删除范例的 scripts 目录中对 packages 的 alias。
iOS 调试支持模拟器和真机两种方式,由于 JSBundle 和调试协议依赖网络传输,真机调试时需要确保手机与开发机在同一个局域网内,因此我们推荐使用模拟器调试。
HippyDemo.xcodeproj 工程文件,并点击运行,正常情况下应该可以启动模拟器,并运行之前内置的 Hippy 前端代码。examples 下的前端范例工程 hippy-react-demo 或者 hippy-vue-demo,通过 npm i 安装完依赖之后,使用 npm run hippy:dev 启动编译和调试服务。Command + R 或 Command + D 键调起 Reload 面板刷新如果
Command + D无法调起面板,可以点击Device->Shake强制调起 Reload 面板
iOS 真机调试只支持 XCode 编译的 App,并且 iOS 设备上需要开启 JavaScript 调试和 Web 检查器选项
确保 iOS 设备和调试服务处于同一局域网内
编译 App,粘贴 bundleUrl 并启动调试
!> 注意:真机调试时必须保证开发机和手机处于同一局域网内,否则会加载 JSBundle 失败。以下两种情况都不是同一局域网:
(a) 开发机和手机分别连接不同的网络环境;
(b) 开发机连接网线,手机连接 WiFi。
Android 使用了 adb 的端口映射功能,解决了真机到开发机的通讯问题。由于 ARM 模拟器运行效率问题,更加推荐使用真机进行调试。
具体流程:
Hippy Demo app。USB 调试模式 已经打开 -- 一般在关于手机页面里连续点击 Build 可以进入开发者模式,再进入开发者模式界面后打开 USB 调试模式。adb reverse --remove-all && adb reverse tcp:38989 tcp:38989 确保 38389 端口不被占用。npm i 安装完依赖之后,使用 npm run hippy:dev 启动编译和调试服务。Command + R 或 Command + D 键调起 Reload 面板刷新鸿蒙调试目前只支持网络调试(手机和JS Server在一个网络内,通过网络下载JS Bundle调试),数据线调试还在开发中。
Android SDK 最低支持版本 2.9.0
iOS SDK 最低支持版本 2.11.5
Hippy 实现了节点和属性从前端到终端的映射,可以在 Chrome DevTools 上进行 Elements 的可视化检查。
<video width="80%" controls preload="none"> <source src="../assets/img/elements-inspect.webm" type="video/webm"> Elements 可视化审查示例(您的浏览器不支持webm视频格式) </video>最低支持版本 2.12.0
!> 请勿在生产环境引入 HMR 相关的配置
当开发者修改了前端代码后,我们可以通过 Hot Module Replacement (HMR) 保留状态刷新组件视图,或通过 live-reload 重载业务实例,步骤如下:
安装热更新依赖
npm i @hippy/vue@^2.12.0
npm i -D @hippy/debug-server-next@latest @hippy/vue-loader @hippy/vue-css-loader
webpack 配置示例
const VueLoaderPlugin = require('@hippy/vue-loader/lib/plugin');
const vueLoader = '@hippy/vue-loader';
module.exports = {
devServer: {
// 默认 hot, liveReload 都为 true,如果只想使用 live-reload 功能,请将 hot 设为 false,liveReload 设为 true
hot: true,
liveReload: true,
client: {
// 暂不支持错误提示蒙层
overlay: false,
},
},
plugins: [
new VueLoaderPlugin(),
// add other plugin ...
],
module: {
rules: [
{
test: /\.vue$/,
use: [
vueLoader,
],
},
],
// add other loaders ...
}
}
package.json 配置:
{
"scripts": {
// -c 或 --config 提供 webpack config 配置路径
"hippy:dev": "node ./scripts/env-polyfill.js hippy-dev -c ./scripts/hippy-webpack.dev.js"
}
}
启动开发:npm run hippy:dev
如果安卓设备断连,需要手动用 adb 转发端口:adb reverse tcp:38989 tcp:38989。
iOS 的热更新:iOS 设备需要代理到开发机上,或处于同一网段,才能使用 HMR 能力。Webpack 配置修改如下所示,对于模拟器,本就和开发机处于同一网段,IP 写 localhost 就能访问到。
module.exports = {
devServer: {
host: '<your_ip_or_localhost_with_proxy>',
},
}
安装热更新依赖
npm i @hippy/react@^2.12.0
npm i -D @hippy/debug-server-next@latest @hippy/hippy-react-refresh-webpack-plugin react-refresh
webpack 配置示例
const ReactRefreshWebpackPlugin = require('@hippy/hippy-react-refresh-webpack-plugin');
module.exports = {
devServer: {
// 默认 hot, liveReload 都为 true,如果只想使用 live-reload 功能,请将 hot 设为 false,liveReload 设为 true
hot: true,
liveReload: true,
client: {
// 暂不支持错误提示蒙层
overlay: false,
},
},
plugins: [
new ReactRefreshWebpackPlugin({
// 暂不支持错误提示蒙层
overlay: false,
}),
],
module: {
rules: [
{
test: /\.(jsx?)$/,
use: [
{
loader: 'babel-loader',
options: {
sourceType: 'unambiguous',
presets: [
'@babel/preset-react',
[
'@babel/preset-env',
{
targets: {
chrome: 57,
ios: 9,
},
},
],
],
plugins: [
['@babel/plugin-proposal-class-properties'],
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-transform-runtime', { regenerator: true }],
// add react-refresh babel plugin
require.resolve('react-refresh/babel'),
],
},
},
],
},
// other loader ...
],
},
};
package.json 配置:
{
"scripts": {
// -c 或 --config 提供 webpack config 配置路径
"hippy:dev": "node ./scripts/env-polyfill.js hippy-dev -c ./scripts/hippy-webpack.dev.js"
}
}
执行 npm run hippy:dev 命令。
如果安卓设备断连,需要手动用adb转发端口: adb reverse tcp:38989 tcp:38989。
iOS的热更新:iOS 设备需要代理到开发机上,或处于同一网段,才能使用 HMR 能力。Webpack 配置修改如下所示,对于模拟器,本就和开发机处于同一网段,IP 写 localhost 就能访问到。
module.exports = {
devServer: {
host: '<your_ip_or_localhost_with_proxy>',
},
}
最低支持版本 2.13.7
支持调试 Vue 组件树、组件状态、路由、store、以及事件性能等
<video width="80%" controls preload="none"> <source src="../assets/img/hippy-vue-devtools.webm" type="video/webm"> Vue Devtools示例(您的浏览器不支持webm视频格式) </video>使用配置:
安装 vue devtools 依赖:
npm i @hippy/vue@^2.13.7 @hippy/vue-router@^2.13.7
npm i @hippy/debug-server-next@latest -D
开启 vue devtools
module.exports = {
devServer: {
remote: {
protocol: 'https',
host: 'devtools.qq.com',
port: 443,
},
// 默认为 false,开启后将通过 remote 字段指定的远程调试服务分发 vue 调试指令
vueDevtools: true
},
}
!> Vue Devtools 的配置会在业务运行时中注入调试代码,可能会有一定的性能影响,请勿在生产环境引入。
客户端最低支持版本 2.13.7
前端最低支持版本 2.14.0
支持调试 React 组件树、组件状态、路由以及性能等
<video width="80%" controls preload="none" preload="none"> <source src="../assets/img/hippy-react-devtools.webm" type="video/webm"> React Devtools示例(您的浏览器不支持webm视频格式) </video>使用配置:
安装 react devtools 依赖:
npm i @hippy/react@^2.14.0
npm i @hippy/debug-server-next@latest -D
开启 react devtools
module.exports = {
devServer: {
remote: {
protocol: 'https',
host: 'devtools.qq.com',
port: 443,
},
// 默认为 false,开启后将通过 remote 字段指定的远程调试服务分发 react 调试指令
reactDevtools: true
},
module: {
rules: [
{
test: /\.(jsx?)$/,
// 必须添加下面这一行,让 babel 忽略 react-devtools 插件
exclude: /@hippy\/hippy-react-devtools-plugin/,
use: [
{
loader: 'babel-loader',
options: {
sourceType: 'unambiguous',
presets: [
'@babel/preset-react',
[
'@babel/preset-env',
{
targets: {
chrome: 57,
ios: 9,
},
},
],
],
plugins: [
['@babel/plugin-proposal-class-properties'],
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-transform-runtime', { regenerator: true }],
require.resolve('react-refresh/babel'),
],
},
},
],
},
],
},
}
!> React Devtools 的配置会在业务运行时中注入调试代码,可能会有一定的性能影响,请勿在生产环境引入。
@hippy/debug-server-next 除了提供 bin 命令 hippy-debug 和 hippy-dev 进行调试构建,还提供了<span id="debug-server-api">接口供自定义的 CLI 工具封装时调用</span>,使用方法如下:
const { webpack } = require('@hippy/debug-server-next');
// 开始 webpack 编译,支持 HMR 等能力
webpack(webpackConfig, (err, stats) => {
// 处理 webpack 打包回调信息
});
本地调试存在两个痛点:
那么这些场景我们可以考虑使用远程调试,效果预览:
<video width="80%" controls preload="none"> <source src="../assets/img/remote-debug-demo.webm" type="video/webm"> 远程调试实例(您的浏览器不支持webm视频格式) </video>安装新一代调试工具: npm i -D @hippy/debug-server-next@latest
修改 Webpack 配置,添加 remote 字段来配置编译产物上传地址和调试服务地址(默认为 http://127.0.0.1:38989 )。考虑到安全因素,官方不提供公网的远程调试服务,你需要自己私有化部署。
module.exports = {
devServer: {
// 远程调试地址,需要配置为你的私有化调试服务地址
remote: {
protocol: 'https',
host: 'devtools.qq.com',
port: 443,
// 配置宿主 App 扫码加载的 scheme,如无需扫码,可不配置
qrcode: (bundleUrl) => {
// 必须指定业务 bundleName
const bundleName = 'QQGroupGameRank';
return `mqqapi://hippy/remoteDebug?bundleName=${bundleName}&bundleUrl=${encodeURIComponent(bundleUrl)}`;
}
},
client: {
overlay: false,
},
},
// other config ...
}
!> 配置远程调试时编译产物将上传远端,并在调试结束后删除。为以防万一,请确保代码中不含敏感信息(如密钥等)。
!> 远程调试时,publicPath 将被设为 ${protocol}://${host}:${port}/<hash>/,以区分不同的业务。
!> 业务加载远程 js bundle 分包时,如未配置 customChunkPath,将默认使用 publicPath 的地址,请确保远程分包也同时上传到调试服务器。
启动编译:npm run hippy:dev,编译结束后将打印调试信息:
其中打印三个字段表示:
remoteServerUrl 字段中宿主 App 设置 debugMode 为 true,并把前端 webpack 生成远程无线调试的 bundleUrl 传入,推荐宿主使用输入框或扫描二维码的方式传入。
Android:
// 初始化 hippy 引擎
HippyEngine.EngineInitParams initParams = new HippyEngine.EngineInitParams();
initParams.debugMode = true;
initParams.remoteServerUrl = ""; // 远程调试 bundleUrl
iOS:
- (void)viewDidLoad {
// 开启调试
NSDictionary *launchOptions = @{@"DebugMode": @(YES)};
NSString *bundleStr = ""; // 远程调试 bundleUrl
NSURL *bundleUrl = [NSURL URLWithString:bundleStr];
HippyBridge *bridge = [[HippyBridge alloc] initWithDelegate:self
bundleURL:bundleUrl
moduleProvider:nil
launchOptions:launchOptions
executorKey:@"Demo"];
}
- (BOOL)shouldStartInspector:(HippyBridge *)bridge {
return bridge.debugMode;
}
- (NSURL *)inspectorSourceURLForBridge:(HippyBridge *)bridge {
return bridge.bundleURL;
}
最低支持版本 2.13.1
| 平台 | HMR & Live-Reload | React Devtools | Vue Devtools | Elements | Log | Sources | Memory |
|---|---|---|---|---|---|---|---|
| Android | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| iOS | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
无论是 hippy-react 还是 hippy-vue 都将和终端通讯的信息进行输出,包含了前终端的节点操作、事件收发。这些日志对于业务调试其实很有帮助,可以让开发了解到前端框架是如何将代码转译成终端可以理解的语法。当遇到问题时应先检查框架通信日志,基本可以定位到大部分问题。
如果需要关闭日志,可以在 hippy-react 的 new Hippy 启动参数中增加 silent: true,或者 hippy-vue 项目的入口文件中,开启 Vue.config.silent = true;。