器→工具, 工具软件, 术→技巧, 研发

Chrome开发者工具入门教程

钱魏Way · · 20 次浏览

Chrome 开发者工具(DevTools)是一个强大的网页开发和调试工具,内置于 Google Chrome 浏览器中。它提供了多种功能,帮助开发者调试和优化网页。以下是一个详细的 Chrome 开发者工具教程,涵盖了主要功能和使用方法。

Chrome开发者工具综述

打开Chrome 开发者工具

有几种方法可以打开 Chrome 开发者工具:

  • 快捷键:
    • Windows/Linux:Ctrl + Shift + I 或 F12
    • macOS:Cmd + Option + I 或 F12
  • 右键菜单:
    • 右键点击网页,然后选择“检查”(Inspect)。
  • Chrome 菜单:
    • 点击浏览器右上角的三点菜单,选择“更多工具”(More tools),然后选择“开发者工具”(Developer tools)。

DevTools 面板概览

打开 DevTools 后,你会看到一个包含多个面板的界面。每个面板都有特定的功能。

以下是主要面板的概览:

  • Elements(元素):
    • 查看和编辑 HTML 和 CSS。
    • 检查和修改网页的 DOM 结构和样式。
    • 实时编辑 HTML 和 CSS,查看效果。
  • Console(控制台):
    • 执行 JavaScript 代码,输出日志信息。
    • 查看错误和警告信息。
    • 交互式调试和测试代码片段。
  • Sources(源代码):
    • 查看、编辑和调试 JavaScript 代码。
    • 设置断点,逐行调试代码。
    • 查看和管理网络请求和响应。
  • Network(网络):
    • 监控所有网络请求,查看请求和响应的详细信息。
    • 分析加载时间和性能瓶颈。
    • 查看和测试不同的网络条件。
  • Performance(性能):
    • 记录和分析网页的性能指标。
    • 查看页面加载时间、帧率和内存使用情况。
    • 优化渲染和资源加载。
  • Memory(内存):
    • 分析内存使用情况,查找内存泄漏。
    • 查看和比较内存快照。
  • Application(应用):
    • 查看和管理浏览器存储,如 Local Storage、Session Storage、IndexedDB 和 Cookies。
    • 查看和调试 Service Workers 和 Web Manifest。
  • Security(安全):
    • 查看网页的安全信息。
    • 检查 SSL/TLS 证书的有效性。

Chrome开发者工具模块详解

Elements 面板

查看和编辑 HTML 和 CSS:

  • 选择元素:点击页面上的元素或在Elements 面板中选择元素,查看其 HTML 代码和应用的 CSS 样式。
  • 编辑 HTML:双击元素标签或右键选择 “Edit as HTML”,直接修改 HTML 代码。
  • 编辑 CSS:在右侧的Styles 面板中,实时修改 CSS 属性值,查看效果。

示例:

<!DOCTYPE html>
<html>
<head>
  <style>
    .example { color: red; }
  </style>
</head>
<body>
  <div class="example">Hello, World!</div>
</body>
</html>
  • 打开Elements 面板,选择 <div class=”example”> 元素。
  • 在Styles 面板中,将 color: red; 修改为 color: blue;,页面上文字颜色会立即变化。

Console 面板

执行 JavaScript 代码:

  • 在控制台中输入 JavaScript 代码并按回车,立即执行代码。
  • 使用log() 输出调试信息。

示例:

console.log('Hello, World!');

查看错误和警告:

  • 控制台会显示 JavaScript 运行时错误和警告信息。
  • 点击错误信息,可以跳转到Sources 面板查看详细的错误位置和堆栈信息。

Sources 面板

查看和编辑 JavaScript 代码:

  • 在Sources 面板中导航到网页加载的所有脚本文件。
  • 点击文件名查看和编辑 JavaScript 代码。

设置断点和调试:

  • 在代码行号上点击,添加断点。
  • 使用顶部的调试控制按钮(继续、逐行调试、步入、步出)进行代码调试。

示例:

function sayHello() {
  const message = 'Hello, World!';
  console.log(message);
}

sayHello();
  • 打开Sources 面板,找到包含 sayHello 函数的文件。
  • 在log(message); 行设置断点。
  • 刷新页面,代码执行到断点处会暂停,可以逐行调试。

Network 面板

监控网络请求:

  • 打开Network 面板,刷新页面,查看所有网络请求。
  • 点击单个请求,查看请求头、响应头、载荷和预览。

分析性能瓶颈:

  • 查看各资源的加载时间、大小和 HTTP 状态。
  • 使用“过滤器”筛选特定类型的资源,如文档、脚本、样式、图片等。

模拟网络条件:

  • 在Network 面板顶部,可以选择不同的网络速度,如“慢 3G”或“快速 3G”,模拟不同的网络条件,查看页面加载性能。

Performance 面板

记录和分析性能:

  • 点击Performance 面板中的“录制”按钮,开始记录页面性能。
  • 执行某些操作后,点击“停止”按钮,查看性能分析结果。

查看性能指标:

  • 查看页面加载时间、脚本执行时间、渲染时间等。
  • 使用时间轴查看各阶段的性能情况,识别性能瓶颈。

Memory 面板

分析内存使用:

  • 点击Memory 面板中的“捕获快照”按钮,生成内存快照。
  • 查看和分析内存使用情况,找出内存泄漏和不必要的内存占用。

比较内存快照:

  • 生成多个内存快照,比较它们之间的差异,分析内存增长情况。

Application 面板

查看和管理存储:

  • 在Application 面板中,查看 Local Storage、Session Storage、IndexedDB 和 Cookies 的内容。
  • 添加、修改或删除存储项。

调试 Service Workers:

  • 查看注册的 Service Workers,调试离线功能。
  • 清除缓存和存储,重新加载 Service Workers。

Security 面板

检查网页安全信息:

  • 查看网页的 SSL/TLS 证书信息。
  • 检查是否存在混合内容(HTTP 和 HTTPS 资源混合使用)问题。

Chrome开发者工具深入

Chrome DevTools 是辅助开发者进行 Web 开发的重要调试工具,DevTools 是 Chromium 的一部分,可以作为独立项目被 Electron 等容器集成。DevTools 主要分为四部分:

  • Frontend:调试器前端,默认由 Chromium 内核层集成
  • Backend:调试器后端,Chromium、V8 或js
  • Protocol:调试协议
  • Message Channels:消息通道,包括:Embedder Channel、WebSocket Channel、Chrome Extensions Channel、USB/ADB Channel

Chrome DevTools Frontend 是一个 Web 应用程序,通过 WebSocket 与 Blink 的 C++ 后端通信。

关于 Chrome 开发者工具的详细使用可以看官方文档,本文不做赘述。Chrome DevTools Extensions 基于 Javascript、CSS、HTML 技术构建可以让用户根据业务需要为 DevTools 增强扩展功能项。当 Chrome DevTools 不能满足我们需求的时候,我们可以写一个 Chrome DevTools Extension,类似于 vue-devtools。

目前主流小程序平台针对小程序特有的技术特征,基于 Chrome DevTools Extensions 进行扩展及改造,赋能小程序开发的各种调试能力。

Chrome Extension

官方文档及示例如下:

因为 Chrome DevTools Extension 属于 Chrome Extension 中的一种特殊的拓展程序。在了解 Chrome DevTools Extension 之前,我们先简单的了解一下 Chrome Extension(Chrome 拓展)的内容。

我们经常说的 Chrome “插件”,其实不是真正意义上的 Chrome Plug-in,一般是指 Chrome Extension(简称“拓展”)。

  • 扩展(Extension),指的是通过调用 Chrome 提供的 Chrome API 来扩展浏览器功能的一种组件,工作在浏览器层面,使用 HTML + Javascript 语言开发。
  • 插件(Plug-in),指的是通过调用 Webkit 内核 NPAPI/PPAPI 来扩展内核功能的一种组件,工作在内核层面,理论上可以用任何一种生成本地二进制程序的语言开发,比如 C/C++、Delphi 等。比如 Flash player 插件,就属于这种类型。一般在网页中用<object> 或者 <embed> 标签声明的部分,就要靠插件来渲染。

Chrome 拓展一般包含如下几个组件:

  • Manifest
  • Background Script
  • UI Elements
  • Content Script
  • Options Page
  • DevTools

Chrome Extension 架构图:

Chrome 拓展的 JS 主要可以分为这 5 类:injected script、content-script、popup js、background js 和 devtools js,

JS种类 可访问的API DOM访问情况 JS访问情况 直接跨域
injected script 和普通 JS 无任何差别,不能访问任何扩展 API 可以访问 可以访问 不可以
content script 只能访问 extension、runtime 等部分API 可以访问 不可以 不可以
popup js 可访问绝大部分 API,除了 devtools 系列 不可直接访问 不可以 可以
background js 可访问绝大部分 API,除了 devtools 系列 不可直接访问 不可以 可以
devtools js 只能访问 devtools、extension、runtime 等部分API 可以 可以 不可以

Chrome DevTools Extensions

官方文档及示例如下:

Chrome DevTools 扩展程序的结构与 Chrome 其他任何的扩展程序一样:它可以具有背景页面(background),内容脚本(content-scripts)和其他选项,此外每个 Chrome DevTools 扩展都有一个 Chrome DevTools 页面,该页面可以访问 DevTools API。Chrome DevTools 扩展程序可以为 Chrome DevTools 添加新功能,可以添加新的 UI 面板和侧边栏,与检查的页面进行交互,获取有关网络请求的信息等。

Chrome DevTools Extension 架构图:

DevTools page 在 manifest.json 中注册,必须为一个 html 页面,对用户不可见,可调用以下 Chrome DevTools Extension 特有的 API:

Background Page 是常驻后台运行的 JS 脚本,拥有对 Extensions API 的全部调用权限,可以和 DevTools Page 进行 通信; Inspected Window 指当前DevTools 检测的 Web 页,可被 Background Page 注入内容脚本。

Chrome DevTools Protocol

Chrome DevTools Protocol 允许第三方对基于 Chrome 的 Web 应用程序进行调试、分析等,基于 WebSocket,利用 WebSocket 建立连接 DevTools 和浏览器内核的快速数据通道。基于 WebSocket 建立连接 DevTools 和浏览器内核的快速数据通道,DevTools Frontend 中的源代码(Connections.js):

/**
 * @param {function()} websocketConnectionLost
 * @return {!ProtocolModule.InspectorBackend.Connection}
 */
export function _createMainConnection(websocketConnectionLost) {
  const wsParam = Root.Runtime.queryParam('ws');
  const wssParam = Root.Runtime.queryParam('wss');
  if (wsParam || wssParam) {
    const ws = wsParam ? `ws://${wsParam}` : `wss://${wssParam}`;
    return new WebSocketConnection(ws, websocketConnectionLost);
  }
  if (Host.InspectorFrontendHost.InspectorFrontendHostInstance.isHostedMode()) {
    return new StubConnection();
  }

  return new MainConnection();
}

该协议把操作划分为不同的域(domain),比如 DOM、Debugger、Network、Console 和 Timeline 等,可以理解为 DevTools 中的不同功能模块。

每个域(domain)定义了它所支持的 command 和它所产生的 event。每个 command 包含 request 和 response 两部分,request 部分指定所要进行的操作以及操作说要的参数,response 部分表明操作状态,成功或失败。command 和 event 中可能涉及到非基本数据类型,在 domain 中被归为 Type,比如:’frameId’: <FrameId>,其中 FrameId 为非基本数据类型。

基于 Chrome Debugging Protocol 的 Client 端有常见几种:

基于 HTML5 标准的 WebSocket 或 Node ws 库

// 建立连接
var ws = new WebSocket('ws://localhost:9222/devtools/page/A12A4B08-E5AF-4A84-A86A-A1C86E731D7F"');
// 调用 Command
ws.onmessage = function(event) {
    console.log(event.data);
  // 获取数据:{"method": "Page.loadEventFired", "params": {"timestamp": 1402317772.874949}}
};
ws.send('{"id": 1, "method": "Page.navigate", "params": {"url": "http://www.github.com"}}');

基于 Node chrome-remote-interface 库

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

const target = { port: '', ws: '' };

const client = await CDP({
  port: target.port,
  target: target.ws,
  local: true
});
client.on('event', (message) => {
  console.log(message);
});

const { Runtime, Page } = client;
Runtime.enable();
Page.enable();
const data = await Runtime.evaluate({
    expression: 'document.documentElement.outerHTML'
});
console.log(data.result.value);

Electron 集成 DevTools

setDevToolsWebContents

Electron 官方文档 contents.setDevToolsWebContents(devToolsWebContents),在 renderer 层建立两个 WebView,将 Simulator 和 DevTools 建立联系。

<webview id="simulator" src="https://zhaomenghuan.js.org"></webview>
<webview id="devtools"></webview>
const simulatorView = document.getElementById('simulator');
const devtoolsView = document.getElementById('devtools');
simulatorView.addEventListener('dom-ready', () => {
  const simulatorContents = simulatorView.getWebContents();
  const devtoolsContents = devtoolsView.getWebContents();
  simulatorContents.setDevToolsWebContents(devtoolsContents);
  simulatorContents.openDevTools();
});

这种方法 devtools WebView 加载的地址是:

chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/@7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33/&can_dock=&toolbarColor=rgba(223,223,223,1)&textColor=rgba(0,0,0,1)&experiments=true

在 Electron 2.x 版本中上述的方法生效,通过 setDevToolsWebContents 方法,我们将两个 WebView 建立了联系,可以实现之间的交互,但是在 Electron 6.x 版本上测试发现居然不生效。通过 github issues 找到一种新的解决办法,使用 BrowserView 代替 WebView 尝试了一下:

// main process
let mainWindow;
let devtoolsView;
// Workbench Window
  mainWindow = new BrowserWindow({
    width,
    height,
    useContentSize: false,
    titleBarStyle: 'hidden',
    webPreferences: {
      webSecurity: false,
      nodeIntegration: true,
      webviewTag: true
    }
  });
  mainWindow.maximize();
  mainWindow.loadURL(winURL);

  // Devtools View
  devtoolsView = new BrowserView();
  mainWindow.setBrowserView(devtoolsView);
  devtoolsView.setBounds({
    x: 330,
    y: 101,
    width: width - 330,
    height: height - 101
  });
  
  ipcMain.on('initialized', (event, message) => {
  const container = webContents.getAllWebContents().find((item) => {
    return item.getURL().includes(message);
  });
  if (container) {
    container.setDevToolsWebContents(devtoolsView.webContents);
    container.debugger.attach();
    container.openDevTools();
  }
});

// renderer process
const simulatorView = document.getElementById('simulator');
  simulatorView.addEventListener('dom-ready', () => {
  ipcRenderer.send('initialized', simulatorView.src);
});

再回头看了看官方 v8.0.2 最新文档 警告提示不要使用 WebView 标签,建议使用 iframe 和 BrowserView 替代。

Electron的 webview 标签基于 Chromium webview,后者正在经历巨大的架构变化。这将影响 webview 的稳定性,包括呈现、导航和事件路由。 我们目前建议不使用 webview 标签,并考虑其他替代方案,如 iframe 、Electron的 BrowserView 或完全避免嵌入内容的体系结构。

这里的 BrowserView 相对 WebView、BrowserWindow 有什么区别呢?Electron 中 WebView 是 DOM 层级结构的一部分,BrowserView 位于操作系统窗口层次结构。BrowserView 与 Chrome 浏览器标签页类似,可以作为一个子窗口,它的位置是相对于父窗口。另外相对 WebView 而言,Chrome 浏览器标签页的错误修复很快,BrowserView 比 WebView 更容易解决一些错误,且 BrowserView 比 WebView 运行更快。

重要结论:通过 Electron setDevToolsWebContents 方法,可以使用任何 WebContents 在其中显示 devtools,包括 BrowserWindow,BrowserView 和 webview 标签,这种方式适合 IDE 进行本地 Web 化模拟调试,模拟器与调试器建立联系。

Remote Debugging

基本原理

什么是远程调试?远程调试可以让您从自己的开发计算机上检查 Android 设备上运行的页面,当然开发本地的页面也可以通过远程调试的方式实现。

远程调试的交互流程:

Electron 支持在 app 模块的 ready 事件触发之前使用 app.commandLine.appendSwitch 添加 Chrome 命令行参数:

// 主进程 main.js
const { app } = require('electron');
// 远程调试
const port = await getPort();
process.env.EMP_REMOTE_DEBUGGING_PORT = port;
app.commandLine.appendSwitch('remote-debugging-port', `${port}`);
app.commandLine.appendSwitch('remote-debugging-address', 'http://127.0.0.1');

app.on('ready', () => {
  // ...
})

可以通过  /json 或 /json/list 获取所有可用的 WebSocket 目标地址。

返回一个数组,里面是所有可以远程调试的页面,其中包含以下字段信息:

  • description:页面信息描述
  • devtoolsFrontendUrl:调试 URL 地址
  • id:页面 ID
  • webSocketDebuggerUrl:WebView Debug Server 的 WebSocket 地址

获取远程 WebView DevTools Frontend 地址:

function getTargetWebViewDevtoolsFrontendUrl(url) {
 	return fetch(`http://127.0.0.1:9222/json`)
    .then(res => res.json())
    .then(res => {
      const target = res.find(
        child => child.type === 'webview' && child.url === url
      );
      return target.devtoolsFrontendUrl;
    });
}

然后加上 remote-debugging-address 和 remote-debugging-port 就是 DevTools Frontend 完整的地址,然后可以使用 WebView、BrowserView 展示出来,如下:

http://127.0.0.1:9222/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/eac1573a-c713-4421-ad64-3e07fb20f034

这个地址相当于两部分组成,一个是 Inspector(调试器),由 Chromium 默认提供集成,也可以进行修改 Chrome DevTools Frontend 源代码进行自定义集成,ws 是 Chromium 内核层为目标 WebView 生成的唯一的 websocketDebuggerUrl。目前 Electron 限制,WebView 集成 DevTools Frontend 前端页面不能加载自定义拓展。

真机调试

真机调试整体流程如下:

Android 平台真机调试

对于 Android WebView 调试,打开 Chrome://inspect 可以显示每一个连接上的设备,以及它们打开了的浏览器标签和启用调试的 WebViews,如下图:

基于 ADB 调试 Android WebView 的原理如下:

$ adb shell cat /proc/net/unix | grep --text _devtools_remote
0000000000000000: 00000002 00000000 00010000 0001 01 28940245 @stetho_org.js.emp.engine.sample:emp_devtools_remote
0000000000000000: 00000002 00000000 00010000 0001 01 28942398 @webview_devtools_remote_14277
$ adb forward tcp:9223 localabstract:webview_devtools_remote_14277

webview_devtools_remote 后面带的 _14277 这种是对应的 pid,可以使用 adb forward 命令将本地 9223 端口映射到远程设备的 unix domain socket(webview_devtools_remote_8208),这样就可以在本地访问到 Android WebView Debug Server。

其中 devtoolsFrontendUrl 是 Chrome 云服务器提供,根据 Android WebView 集成的 Chromium 不同版本加载不同的前端地址,4cd8c034a5b41cc6da41e00e42d9aadfaa34932b 和 WebKit Version 对应。

iOS 平台真机调试

iOS 设备可以连接数据线借助 Safari 或者按安装 Safari Technology Preview 远程调试。如果想使用 Chrome 调试,可以使用 RemoteDebug iOS WebKit Adapter 进行代理。

关于 iOS 平台调试权限的问题,开发阶段,用开发者账号 build 出来的 app 可以很方便的调试, 但是苹果应用商店中的 app 使用 Distribution 签名,无法直接打开 webview 的远程调试。 因此我们通过替换 ipa 包签名方式改成开发者的签名实现远程调试。

定制 Inspector

Electron 支持 Chrome DevTools 扩展程序,可增强开发工具调试流行 web 框架的能力。

Electron 主进程通过 API 管理 DevTools Extension:

  • addDevToolsExtension(path)
  • removeDevToolsExtension(name)
  • getDevToolsExtensions()

经过测试发现 setDevToolsWebContents 模式下 DevTools 面板可以加载自定义 DevTools Extenson,但是远程调试模式下自定义 DevTools Extenson 无法加载。目前主流小程序平台调试器的自定义面板如 Wxml、Swan Element、AppData、Storage 等都是通过 Chrome DevTools Extension 实现,都是改造 devtools-frontend 项目。

可以通过 preload 或者注入 JS Script 可以实现对 DevTools 前端界面进行定制:

// 隐藏默认面板
const tabbedPane = window.UI.inspectorView._tabbedLocation._tabbedPane;
tabbedPane.closeTab('elements');
tabbedPane.closeTab('timeline');
tabbedPane.closeTab('resources');

// 新增 Connection
const capabilitiesForPageFrameTarget = () => {
  return window.SDK.Target.Capability.Browser | window.SDK.Target.Capability.DOM | window.SDK.Target.Capability.DeviceEmulation |
    window.SDK.Target.Capability.Emulation | window.SDK.Target.Capability.Input | window.SDK.Target.Capability.JS |
    window.SDK.Target.Capability.Log | window.SDK.Target.Capability.Network | window.SDK.Target.Capability.ScreenCapture |
    window.SDK.Target.Capability.Security | window.SDK.Target.Capability.Target | window.SDK.Target.Capability.Tracing |
    window.SDK.Target.Capability.Inspector;
};
const createPageFrameConnection = (params: any) => {
  const onDisconnect = (message: any) => {
    console.log('', '-onDisconnect-' + message);
  };
  let wsConnection = new window.SDK.WebSocketConnection(url, onDisconnect, {
    onMessage: params.onMessage,
    onDisconnect
  });
  wsConnection.isPageFrame = true;
  return wsConnection;
};
window.SDK.targetManager.createTarget('page-frame', 'Page', capabilitiesForPageFrameTarget(), createPageFrameConnection, null);

参考链接:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注