精华内容
下载资源
问答
  • 微信小程序断点调试.

    2018-08-07 14:30:05
    微信小程序断点调试,是相当重要的,因为微信的小程序的调试,本事不是太难。
  • 微信小程序断点调试

    千次阅读 2018-11-13 10:05:11
    做开发总会遇到bug,那么就需要调试,调试一般要么是打log,要么是断点调试,那么在微信小程序怎么断点调试呢?这和我们做Java或者Android 调试还是不一样的,微信小程序调试时先打开调试器,然后在Sources下,如图: ...

    做开发总会遇到bug,那么就需要调试,调试一般要么是打log,要么是断点调试,那么在微信小程序怎么断点调试呢?这和我们做Java或者Android 调试还是不一样的,微信小程序调试时先打开调试器,然后在Sources下,如图:

    然后找到你要调试的代码,比如你要调试的是app.js中代码:在你需要调试的地方左键就ok了.

     

    展开全文
  • 微信小程序--断点单步调试(转载)

    千次阅读 2018-08-15 20:48:00
    总有一些东西,想看看它跑起来的内容与我们编程时想的是否一致,于是就想到了能不能单步调试或者打出一些我们想要的变量的内容,以便我们做进一步的开发和调整,现在我就要介绍下微信小程序的一般用到的调试方法和...

    在微信小程序开始学习与开发的过程中,总有一些东西,想看看它跑起来的内容与我们编程时想的是否一致,于是就想到了能不能单步调试或者打出一些我们想要的变量的内容,以便我们做进一步的开发和调整,现在我就要介绍下微信小程序的一般用到的调试方法和打印日志以及看到变量里面的运行值。

            第一步:打断点,我们必须在我们想要它停下来的地方打上调试断点,点击微信开发者工具的左侧的“调试”tab,然后选择中间窗口中的”Sources”Tab,在Sources页中点开”Top”根节点,层层打开,找到自己想要调试的js文件,一般是打那种.js后面带[sm],如index.js[sm],非index.js,当然这里之后想修改内容不能在”调试”模式下修改,而要转到”编辑”模式,之前我老容易犯这个错误,打开index.js[sm]文件后,点击左边的行上的数字,就会由灰色背景变成蓝色背景,这样断点就打好了,(如果不想要了,也可以点击就会取消)如下图所示:

     

            第二步:运行,首先点击编译上面的那个运行小图标(或者快捷键ctrl+b),然后操作到你想要的那个界面或者触发某个动作,程序自动会跑到断点处代码,如下图所示:

     

            第三步:单步调试,按调试器窗口(debugger)的向下箭头(step into nextfunction call),一步步的可以往下调试,如果想跳到下一个断点,就按调试器窗口的类似于播放的小按钮,英文叫resume script execution,快捷键为F8或者Ctrl+\,如下图所示

     

    如果想看调试中变量的运行值怎么办?有许多办法,这里以查看上个界面传来的options变量值为例

    办法1:让鼠标指针放在变量上,他会有提示框,框内就是变量值,如下图所示

     

    办法2:使用console.log(options);打印出来,在console窗口可以查看,如下图所示

     

    方法3:在调试器窗口,点开Scope标签,然后再点开相应变量,如下图所示

     

    如果我们的断点过多或者不想它们调试了,怎么让它们失效呢?

    办法就是点击调试器窗口中的图标,英文叫deactive breakpoints(或者快捷键:Ctrl+F8)

    展开全文
  • 微信小程序-单步断点调试

    万次阅读 2017-11-17 16:50:54
    总有一些东西,想看看它跑起来的内容与我们编程时想的是否一致,于是就想到了能不能单步调试或者打出一些我们想要的变量的内容,以便我们做进一步的开发和调整,现在我就要介绍下微信小程序的一般用到的调试方法和...

    原文出处:http://blog.csdn.net/bright789/article/details/54709594 


    在微信小程序开始学习与开发的过程中,总有一些东西,想看看它跑起来的内容与我们编程时想的是否一致,于是就想到了能不能单步调试或者打出一些我们想要的变量的内容,以便我们做进一步的开发和调整,现在我就要介绍下微信小程序的一般用到的调试方法和打印日志以及看到变量里面的运行值。

    第一步:打断点,我们必须在我们想要它停下来的地方打上调试断点,点击微信开发者工具的左侧的“调试”tab,然后选择中间窗口中的”Sources”Tab,在Sources页中点开”Top”根节点,层层打开,找到自己想要调试的js文件,一般是打那种.js后面带[sm],如index.js[sm],非index.js,当然这里之后想修改内容不能在”调试”模式下修改,而要转到”编辑”模式,之前我老容易犯这个错误,打开index.js[sm]文件后,点击左边的行上的数字,就会由灰色背景变成蓝色背景,这样断点就打好了,(如果不想要了,也可以点击就会取消)如下图所示:

    第二步:运行,首先点击编译上面的那个运行小图标(或者快捷键ctrl+b),然后操作到你想要的那个界面或者触发某个动作,程序自动会跑到断点处代码,如下图所示:


    第三步:单步调试,按调试器窗口(debugger)的向下箭头(step into nextfunction call),一步步的可以往下调试,如果想跳到下一个断点,就按调试器窗口的类似于播放的小按钮,英文叫resume script execution,快捷键为F8或者Ctrl+\,如下图所示


    如果想看调试中变量的运行值怎么办?有许多办法,这里以查看上个界面传来的options变量值为例

    办法1:让鼠标指针放在变量上,他会有提示框,框内就是变量值,如下图所示


    办法2:使用console.log(options);打印出来,在console窗口可以查看,如下图所示


    方法3:在调试器窗口,点开Scope标签,然后再点开相应变量,如下图所示


    如果我们的断点过多或者不想它们调试了,怎么让它们失效呢?

    办法就是点击调试器窗口中的图标,英文叫deactive breakpoints(或者快捷键:Ctrl+F8)

    展开全文
  • 如何实现Source以及断点调试。 如何实现对网络记录的审查。 如何实现基于页面的数据审查。 基本通信关系结构概述 上图完整的表达了实现小程序调试技术各关键部分之间的关系。主要分为以下几个关键角色: 调试面板...

    本篇文章主要围绕小猴小程序调试技术第三版进行展开。

    在上一篇导读文章中提到,小猴小程序的调试部分从无到有一共经历了3个版本。本篇文章会详细描述面向开发者的调试功能是如何实现的。

    文章将会描述以下部分:

    • 调试实现的基本通信关系结构。
    • 如何实现完整的DOM审查能力。
    • 如何实现Console。
    • 如何实现Source以及断点调试。
    • 如何实现对网络记录的审查。
    • 如何实现基于页面的数据审查。

    基本通信关系结构概述

    image
    上图完整的表达了实现小程序调试技术各关键部分之间的关系。主要分为以下几个关键角色:

    1. 调试面板: devtools frontend。
    2. 调试中继服务。
    3. 渲染层页面栈。
    4. 逻辑层运行容器。

    调试面板

    调试面板与我们开发者经常所用的开发者工具保持一致。它来自于chrome devtools frontend项目,也就是被嵌在chrome浏览器中的开发者工具。

    不过在这里我采用的是2020年5月1日的版本。因为从这个版本之后,整个chrome devtools frontend全是用TS写的。这就导致调试起来非常不便,因为每次修改需要花费大约10分钟的时间编译后才能运行。

    这里有篇文章简要的描述了devtools frontend的概况:深入理解 Chrome DevTools

    chrome devtools frontend(后文简称为frontend)是通过WebSocket与外部保持通信的。内部传输的信息就是:Chrome Devtools Protocol。默认情况下,在启动frontend后,通过Url传给frontend一个调试源。类似于这样:http://localhost:8090/front_end/devtools_app.html?ws=127.0.0.1:9222/devtools/browser/2b82a4a4-c047-40f8-bbc4-7a09fdc501d3。

    那么调试源的信息从哪获得呢?通常执行JS代码的引擎都会暴露调试源信息给外部。例如Node:

    node --inspect-brk index.js
    Debugger listening on ws://127.0.0.1:9229/51d4ee96-3759-4e04-8b2a-c0fd8a1c5db2
    For help, see: https://nodejs.org/en/docs/inspector
    

    例如Electron:

    // 通过以下方式公布对外调试信息
    app.commandLine.appendSwitch("remote-debugging-port", 8820);
    app.commandLine.appendSwitch("remote-debugging-address", "http://127.0.0.1");
    // 通过以下方式获得调试信息,这个接口会返回Electron中所有进程的调试源
    http://127.0.0.1:8820/json/list
    // 返回结果:
    [
      {
        "description": "",
        "devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9333/devtools/page/547B0C088951191189B7878999BA8048",
        "id": "547B0C088951191189B7878999BA8048",
        "title": "127.0.0.1:8886/dashboard.html",
        "type": "page",
        "url": "http://127.0.0.1:8886/dashboard.html",
        "webSocketDebuggerUrl": "ws://127.0.0.1:9333/devtools/page/547B0C088951191189B7878999BA8048"
      },
      {
        "description": "",
        "devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9333/devtools/page/6457E98873E6402B53C79C8A374723F6",
        "id": "6457E98873E6402B53C79C8A374723F6",
        "title": "127.0.0.1:8886/main.html",
        "type": "page",
        "url": "http://127.0.0.1:8886/main.html",
        "webSocketDebuggerUrl": "ws://127.0.0.1:9333/devtools/page/6457E98873E6402B53C79C8A374723F6"
      }
    ]
    

    具体信息可以查看:https://www.electronjs.org/docs/latest/api/command-line-switches/#–remote-debugging-portport

    拿到了调试源后,就可以使frontend与调试源建立WS连接进行代码调试控制了。但这对于一款小程序开发者工具还远远不够,因为frontend只能连接一个调试源,而小程序实际是逻辑层与渲染层分离的,会有对应两个调试源。所以要不然只能调试逻辑代码,要不然只能进行DOM审查。所以怎么办呢?

    最开始的解决办法是弄两个frontend实例出来,各自与逻辑层、渲染层的调试源进行连接,再通过一种巧妙的方式在切换DOM或Console时切换这两个frontend。但终究感觉这种方式不是很正统,只是一种取巧的办法。

    于是调试中继服务诞生了。

    调试中继服务

    调试中继服务

    上图中的粉红色区域表示了调试中继服务。中继服务所做的事情有:

    • 承担与frontend的通信。
    • 承担与逻辑层、渲染层调试源的通信(DOM、Console、Source、Network、Memory等等)。
    • 承担与逻辑层运行容器的通信(页面数据审查)。

    所以中继服务所扮演的角色是消息分发与消息汇总,它来负责frontend与逻辑层、渲染层调试源的无缝通信。

    调试中继服务内部有一个WebSocketServer与两个WebSocketClient。WebSocketServer负责与frontend通信。两个WebSocketClient分别负责与逻辑层调试源、渲染层调试源通信。此外,它还持有逻辑层运行容器的引用,负责执行一些JS脚本,这在数据审查的功能上会用到。

    渲染层页面栈

    小程序是多页面的,这与常规的H5应用有很大的不同。所以渲染层页面栈就出现了。它负责所有页面的管理,比如进栈、出栈、销毁等等。而调试中继服务所连接的对象永远是页面栈栈顶的那个BrowserView所对应的调试源。

    逻辑层运行容器

    逻辑层运行容器主要的职责是负责逻辑代码的运行。在小程序代码编译完成后,会生成一个logic.js这样的文件。而为了创造一个干净的运行环境,在逻辑层的运行环境中增加了一个worker,以保证开发者写的逻辑代码无法获得外部的API,比如无法访问window或者document。

    好,介绍了以上四大关键角色之后,接下来会分别详细描述各个调试工具是怎么实现的。

    各调试工具的实现

    作为前端开发,我们经常用到的调试工具有:

    • Elements 用于DOM的审查与CSS样式的审查。
    • Console 用于在控制台看一些日志,执行一些脚本等。
    • Source 用于查看运行代码与控制调试的执行。
    • Network 用于查看所有的网络访问记录。包括资源、XHR、WS等。
    • Inspect 这个其实在浏览器中没有集成,但我们经常会使用的类似Vue-devtools与React-devtools这类调试工具。通过它可以查看页面所声明的data以及对data的一个修改。

    在开始讲各个面板之前,我先简单描述一下小程序开发者工具启动后为调试做了哪些准备工作:

    1. 小程序开发者启动。
    2. 启动调试中继服务。
    3. 启动frontend静态资源服务。
    4. 通知小程序开发者工具载入frontend。
    5. frontend与调试中继服务建立WS连接。
    6. 开始等待小程序的载入。

    小程序载入后中继服务又做了哪些事呢?

    1. 扫描Electron提供的所有调试源。
    2. 过滤出逻辑层调试源与渲染层调试源。
    3. 分别与逻辑层调试源、渲染层调试源建立WS连接。
    4. 发送frontend的消息给逻辑层调试源与渲染层调试源。
    5. 最后就是调试源与frontend的正常通信了。

    Elements

    Elements
    上图为Elements的一个实现图。因为直接使用的是chrome devtools frontend的源代码,所以这里的功能全部是不需要作改动的。但为了保证正常的功能使用,还是需要做一些额外的处理的:

    1. 因为frontend在开发者工具启动后就启动了。frontend在启动后会马上发消息给被调试源,在这里是由调试中继服务接收的。而此时还没有加载小程序代码,所以还不能做简单的转发。需要由中继服务先将消息做缓存处理,等到小程序加载完成后,再把消息分别发送给逻辑层调试源或渲染层调试源。如下图所示:
      Elements
    2. 因为小程序是多页面的,所以在打开新页面或返回时都需要刷新当前页面的DOM结构到Elements。Elements的DOM刷新这里遇到了一些挑战。因为页面栈的切换是外部的主动行为,所以我们需要怎么告知frontend该更新DOM了?这里就不得不深入去看frontend的源码,了解frontend初次是怎么去获得DOM信息的。再根据它内部的上下文看看怎么触发它再次请求DOM。浏览器的页面重定向功能给了我很大的启发。最终经过大量的摸索与实践,知道了可以通过以下方式触发DOM的更新:
    // 触发页面DOM更新
    frontendWS.send('{"method":"DOM.documentUpdated","params":{}}');
    

    但这就完了吗?不,问题还有。这时发现hover元素与修改样式的功能都不起作用了。于是又经过一轮的排查发现需要开启一些能力才可用:

    // 发送消息给渲染层
    viewWS.send('{"id":2,"method":"Page.enable"}')
    viewWS.send('{"id":3,"method":"Page.getResourceTree"}')
    viewWS.send('{"id":5,"method":"DOM.enable","params":{}}')
    viewWS.send('{"id":6,"method":"CSS.enable"}')
    viewWS.send('{"id":10,"method":"Overlay.enable"}')
    viewWS.send('{"id":11,"method":"Overlay.setShowViewportSizeOnResize","params":{"show":true}}')
    viewWS.send('{"id":25,"method":"Page.setAdBlockingEnabled","params":{"enabled":false}}')
    

    这时的时序图如下所示:
    Elements
    3. 因为中继服务的特殊性,所以需要识别哪些消息是传给渲染层调试源的,哪些是传给逻辑层调试源的。这里就需要对Chrome DevTools Protocol有个非常清楚的了解。
    基本逻辑如下:

    if (message.includes('DOM') || message.includes('CSS') || message.includes('Page') || message.includes('Overlay')) {
        // 发送给渲染层调试源
    } else {
        // 发送给逻辑层调试源
    }
    

    Console & Source & Network & Memory

    这4个tab都连接的是逻辑层调试源,所以没有Elements那么复杂。只需要保证frontend与逻辑层调试源的信息是畅通的就可以。不过需要做特殊处理的仍然有以下三点:

    1. 页面进栈出栈时都不得再次与逻辑层调试源建立连接。
    2. 重新载入小程序时逻辑层运行容器需要reload。
    3. 如果需要监听网络情况,则需要在逻辑代码中增加Ajax请求。
    4. 如果想要在Console中正确的执行某些脚本,需要选择worker的执行上下文。

    DataInspect

    以上5个部分总体来说逻辑并不复杂,只需要确保信息分发合理、顺序不乱、数据不丢失就可以了。但马上要介绍的数据审查就不是一般的复杂了。

    Elements

    数据审查类似于vue-devtools,主要提供针对页面上声明的data的查看与修改的能力。这项能力必须是从0到1独立开发出来。因为frontend并没有这种能力。

    摆在我面前的第一个问题就是:怎么在frontend上增加一个tab?

    在frontend上增加tab

    这个问题困扰了我很久。之前曾采用过chrome extensions的能力成功的在调试面板上增加了一个tab,但这种能力仅限于chrome浏览器自身或者使用electron自带的开发者工具才支持。而我们这里为了高度定制化,不得不采用了chrome devtools frontend的源码来做的。而frontend又不支持这种能力,于是踏上了frontend源码深度阅读之路。

    经过大量的调试+阅读,才知道了frontend是如何加载一个tab的。这里不得不说一下frontend各个tab的加载机制。

    在frontend根项目有这么一个文件:
    Elements

    在devtools_app.json中声明了需要frontend动态加载的所有模块。每个模块的名称代表了这个模块所在的目录。例如数据审查所在目录为inspect,而这里就需要增加声明{ "name": "inspect" }

    在配置了需要加载模块名称之后呢,接下来就需要了解这个模块是怎么加载的。在每个模块的根目录下需要定义一个名为module.json的文件。需要在这个文件中声明当前模块需要加载的资源信息以及tab所需要放置的位置以及tab所对应的面板等关键信息。例如inspect/module.json就是这么定义的:

    {
      "extensions": [
        {
          "type": "view",
          "location": "panel",
          "id": "inspect",
          "title": "Inspect",
          "order": 101,
          "className": "Inspects.InspectsPanel"
        }
      ],
      "dependencies": [
      ],
      "scripts": [],
      "modules": [
        "inspect.js",
        "inspect-legacy.js",
        "ElementsPanel.js"
      ],
      "resources": [
      ]
    }
    

    按照以上方式定义完成后,frontend就开始按照一定的顺序去加载并执行这些文件了。关键代码如下:

    // root/Runtime.js 部分关键代码
    _loadPromise() {
      ...
      const dependencies = this._descriptor.dependencies;
      const dependencyPromises = [];
      for (let i = 0; dependencies && i < dependencies.length; ++i) {
        dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPromise());
      }
      this._pendingLoadPromise = Promise.all(dependencyPromises)
                                     .then(this._loadResources.bind(this))
                                     .then(this._loadModules.bind(this))
                                     .then(this._loadScripts.bind(this))
                                     .then(() => this._loadedForTest = true);
      return this._pendingLoadPromise;
    }
    
    _loadModules() {
      if (!this._descriptor.modules || !this._descriptor.modules.length) {
        return Promise.resolve();
      }
      const namespace = this._computeNamespace();
      self[namespace] = self[namespace] || {};
      const legacyFileName = `${this._name}-legacy.js`;
      const fileName = this._descriptor.modules.includes(legacyFileName) ? legacyFileName : `${this._name}.js`;
      return eval(`import('../${this._name}/${fileName}')`);
    }
    

    通过以上方式便可以加载并运行在module.json中所声明的文件了。但目前尚未创建一个tab出来。还得继续往下走。

    在所有资源都加载完成后,正主出来了。main/MainImpl.js负责整个面板的构建。在这个构造函数内部会等待DOMContentLoaded事件执行完毕后触发其内部的_loaded方法执行。这个方法会触发整个frontend页面的实例化过程。

    // main/MainImpl.js 306
    _showAppUI(appProvider) {
      ...
      const app = (appProvider).createApp();
      ...
      app.presentUI(document);
      ...
    

    上面这部分代码会将整个面板挂载到document到,使页面上出现内容

    再接着会通过ui/InspectorView.js这个类去构造顶部的Tab。关键代码:

    export class InspectorView extends VBox {
      constructor() {
        super();
        ...
    
        // Create main area tabbed pane.
        this._tabbedLocation = ViewManager.instance().createTabbedLocation(
            Host.InspectorFrontendHost.InspectorFrontendHostInstance.bringToFront.bind(
                Host.InspectorFrontendHost.InspectorFrontendHostInstance),
            'panel', true, true, Root.Runtime.queryParam('panel'));
    
        ...
      }
    

    再通过动态实例化的方式去构造对应的面板类:

    _createInstance() {
      const className = this._className || this._factoryName;
      ...
      const constructorFunction = self.eval((className));
      ...
      // 实例化对应的类
      return new constructorFunction(this);
    }
    

    在这里我们在module.json中所定义的类为:Inspects.InspectsPanel。这个类的代码如下:

    import * as UI from '../ui/ui.js';
    
    export class InspectsPanel extends UI.Panel.Panel {
    
        constructor() {
            super('inspect');
    
            const vueRootNode = createElement('div');
            vueRootNode.id = 'vue-root'
            this.element.appendChild(vueRootNode);
    
            self.ee = new EventEmitter3();
    
            new Vue({
                render: createElement => createElement(inspect_frontend),
                el: vueRootNode
            })
    
        }
    
        /**
         * @return {!InspectsPanel}
         */
        static instance() {
            return /** @type {!InspectsPanel} */ (self.runtime.sharedInstance(InspectsPanel));
        }
    }
    

    InspectsPanel这个类就是数据审查Tab所对应的那个页面。frontend的所有页面都是拿项目内部定义的控件去写的,非常复杂,加之又没有参考文档,所以这里将要实现的逻辑交给Vue实现非常合适。

    在上面的代码中,Vue渲染了一个inspect_frontend。这个东西从哪来的呢?这是我单独写的一个Vue项目,再通过vue-cli构建出来一个库,被挂载到了全局。它其实就是个render函数。运行效果如下:

    Elements

    好,到这里就可以在frontend上通过Vue写具体的内容了。算是到达了一个关键的节点。接下来就是如何获得审查数据的部分了。

    审查数据获取过程

    为了完成基本的能力,需要有3个基本的功能:

    1. 获得小程序当前所有的页面。
    2. 获得小程序某个页面的页面数据。
    3. 修改数据。

    因为小程序的所有数据都是由逻辑层控制的,所以我只需要去逻辑层拿数据就可以了。虽然说起来很简单,但实际上拿的这个过程非常复杂。那么复杂在哪里呢?

    我简单说一下这个过程:

    1. 数据审查处通过ws发消息给中继服务。
    2. 中继服务收到消息后识别。
    3. 识别后通过执行JS脚本给逻辑层容器发送消息。
    4. 逻辑层容器收到消息后再通过postMessage发消息给worker。
    5. worker收到消息后执行相应的查询。
    6. worker的函数执行完之后,通过postMessage发消息给外部的逻辑层容器。
    7. 外部的逻辑层容器收到消息后通过IPC发消息给Electron。
    8. Eletron收到消息后交给中继服务进行识别。
    9. 中继服务识别后,找到对应的回调方法并触发。
    10. 回调方法通过WS再发消息给frontend。
    11. frontend再识别对应的回调给上层调用处。

    这里的逻辑可以参考第三版的运行关系图来看(下图红色箭头走向)。

    Elements

    这个完整的过程一共经历了9个对象,数据被一层层包装,再一层层的拆开。复杂程度可堪比计算机的网络层次了。

    查询页面数据

    当打开数据审查面板时,第一个需要知道的就是当前打开了哪些页面。如下图所示:

    Elements

    有了上小节所描述的过程后,在这里查询页面数据就比较简单了。在业务代码中只需要通过这样的方式查询即可:

    getPages() {
      sendMessage('getPages').then(res => {
        this.pages = res;
      }).catch(err => {
        console.error(err);
      })
    }
    

    而这个sendMessage是为了方便数据审查使用所抽象的公共方法:

    export function sendMessage(method, params) {
        return new Promise((resolve, reject) => {
            self.target._router.sendMessage("", "DataInspect", `DataInspect.${method}`, params, (error, result) => {
                if (error) {
                    console.error('Request ' + method + ' failed. ' + JSON.stringify(error));
                    reject(null);
                    return;
                }
                resolve(result);
            })
        })
    }
    

    第三行的self.target是frontend内部给上层业务使用的一个基本通信对象。它负责通过WS发消息给外部,再将收到的消息一一回调给上层对应的回调方法。

    self.target这种可以保证回调消息不乱的机制让我开了眼界。

    中继服务在收到消息后,进行简单的识别:

    // 数据审查消息分发
    if (message.includes('DataInspect')) {
        const dataInspectMessageBody = JSON.parse(message);
        if (dataInspectMessageBody.method === 'DataInspect.getPages') {
            // 获取所有的页面信息
            const result = await this.executeCodeInAdapter('globalVar.getRoutes()');
            this.sendMessageToDataInspect(dataInspectMessageBody.id, result);
        }
    }
    

    上面的executeCodeInAdapter就是负责去逻辑层容器执行指定脚本的。参数globalVar.getRoutes()就是定义在逻辑代码中的一个函数。它会被层层传递,最终到达worker中运行:

    // worker运行的adapter.js
    function dispatchMessage(message) {
        switch (message.type) {
            case 'loadCode':
                loadLogicFile(message.content);
                break;
            case 'runCode':
                const result = eval(message.content);
                syncExecuteResult({
                    executeResult: result,
                    executeExpression: message.content,
                    executeId: message.id,
                });
                break;
            default:
                console.warn(`未处理的消息类型,请关注: ${message.type}`);
                break;
        }
    }
    

    最终,globalVar.getRoutes()会被eval函数执行。并同步返回一个结果。而这个结果并不能同步返回给外面,还得通过一系列过程发送给外部,最终在electron中进行结果分发:

    onMessage({ type, args }) {
      // 同步执行结果
      if (type === 'syncExecuteResult') {
        const resolveCallback = this.resolveMap[args.executeId];
        if (resolveCallback) {
          resolveCallback(args.executeResult);
        }
      } else {
        // 其它通信消息,比如与渲染层的通信
        this.options.onMessage(messageObj);
      }
    }
    

    上面函数中的resolveMap就是保留了所有异步消息的回调方法,当有结果时,根据id取到对应的方法执行就可以了。

    上面的逻辑就是借鉴的frontend的self.target的逻辑。

    当resolveCallback被执行后,前文中的await this.executeCodeInAdapter('globalVar.getRoutes()')就会继续执行,并拿到结果。再通过sendMessageToDataInspect将结果通过WS返回给业务调用处。

    到此,查询所有页面的信息逻辑通路完成。

    查询指定页面的所有数据

    与查询页面数据类似,也是基于同样的通信机制。只是方法不同:

    sendMessage('getPageData', {
      pagePath: this.path // 页面路径,例如: pages/index/index
    }).then(res => {
      this.pageData = res;
    }).catch(err => {
      console.error(err);
    })
    
    设置指定页面的某项数据

    这与查询数据也一样:

    sendMessage('setPageData', {
      pagePath: path,
      data: {
        // 所编辑的key与value
        [this.editedKey]: value
      }
    }).then(res => {
      this.$emit('submit-edit');
      self.ee.emit('refresh');
    }).catch(err => {
      console.error(err);
    })
    

    发送消息虽然都相同,但发送消息后的逻辑却有一些不同。在发送编辑消息后需要重新查询当前页面的数据以及通知DOM更新。

    监听渲染层主动变更

    监听渲染层主动变更时什么意思呢。就是说,会有渲染层主动触发的行为。例如:点击checkbox,在输入框输入内容等等。这些情况下也需要实时反馈到数据审查面板上。前面的三种方式都是主动去逻辑层拿的过程,而这个却是渲染层主动触发的。所以怎么解决这个问题呢?

    我提供的思路是:

    1. 当开启数据审查面板时,主动向逻辑层注册一个方法。
    2. 当渲染层的数据发生变更时,会通过一系列过程告知逻辑层。
    3. 逻辑层再去触发第1步注册的那个方法,使得触发frontend中的回调。
    4. 当frontend收到回调后,再次进行注册。

    具体实现逻辑如下:

    1. 在frontend中主动注册。
      这一步和上面三种数据查询方式没什么不同。
    // 在frontend中主动注册回调
    sendMessage('registerRefreshEvent').then(this.getPageData);
    
    1. 在中继服务中主动注册。
      当中继服务收到主动注册指定后,做特别处理。将resolve方法对外暴露。
    // 中继服务消息分发
    if (dataInspectMessageBody.method === 'DataInspect.registerRefreshEvent') {
           // 注册数据更新回调
           console.info(`已注册数据更新回调`);
           await new Promise(resolve => this.refreshEventResolve = resolve);
           this.sendMessageToDataInspect(dataInspectMessageBody.id, "");
    }
    
    1. 中继服务主动注册方法到Electron。
    this._messager.registerEventCallback('-1', command => {
       if (command === 'refresh') {
           // 主动刷新
           console.info(`要求数据审查刷新`);
           this.refreshEventResolve();
       }
    })
    
    1. 等待被回调。回调的过程与上面的消息通信一致。从逻辑层收到渲染层消息后,主动发消息给Electron。Electron收到消息后,再回调中继服务。中继服务resolve刚刚正在等待的refreshEventResolve方法。
    2. 中继服务再发消息给frontend。

    这个过程的时序图如下:

    Elements

    上面讲了frontend中的消息是怎么通信的。接下来就说一说页面是怎么做的。

    数据审查UI

    Elements

    如上图所示。页面最开始的想法是完全自己写一个出来,但转念一想,这里的工作量应该不小,我为何不采用第三方UI组件呢?

    于是,最开始就用ElementUI来表示页面数据。这个很快就完成了。但具体的数据查看和编辑怎么做?

    因为数据查看涉及到树形结构审查还有各种编辑交互。所以感觉上这个工作量也很庞大。那么我可不可以将vue-devtools的代码拿过来一用呢?

    经过大量的代码转移与数据适配。虽然过程艰辛,但最终成功嫁接了过来。节省了相当大的工作量。这个过程没什么多说的,无非是需要对vue-devtools的源代码大量阅读。


    以上就是对调试中所有面板实现的一个详细介绍。最后来总结一下。

    image

    还是以上图做结尾说明。

    首先为了解决一个调试面板对两个运行容器的问题,引入了调试中继服务。中继服务负责消息的分发与汇总。只需要确保Chrome Devtools Protocal的协议正常的流转就可以完成基本的调试能力。另外还兼容了新页面载入与小程序重载的各种情况。

    但仅有以上还不够,因为数据审查需要从0实现。为了解决数据审查的问题,想办法从源码的角度上知道了如何增加一个tab,如何在frontend上增加一个面板。面板在这里用的Vue。数据审查所需要的数据经历了9个对象的流转才打通。对于UI方面,采用了ElementUI加vue-devtools的大量源代码。

    以上就是想要分享的有关小程序调试的所有内容。

    展开全文
  • 卡死原因, WAService.js文件太大, 渲染花费太多时间, 因此卡死, 想办法跳过这个文件即可, chrome调试工具内置功能可解决 ...Blackbox script 的作用是断点调试时跳过指定js文件, 这里我们...
  • 微信小程序如何设置断点 首先打开调式模式,在Sources中找到设置断点的行,并设设置断点。 快捷键补充: 1、F10 单步 2、F8 跳到下一个断点
  • 微信小程序里面的单步调试和变量查看

    万次阅读 多人点赞 2017-01-24 18:32:57
    总有一些东西,想看看它跑起来的内容与我们编程时想的是否一致,于是就想到了能不能单步调试或者打出一些我们想要的变量的内容,以便我们做进一步的开发和调整,现在我就要介绍下微信小程序的一般用到的调试方法和...
  • 下面对微信web开发者工具做个简单的介绍 ...这里是个小程序的模拟器,建议使用iphone6,原因后面会说到, 这里是小程序的一个功能区域啊,下面会一个逐一讲解,先看下一块, 这个区域是我们在调试的...
  • 今天编就为大家分享一篇利用Pycharm断点调试Python程序的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随编过来看看吧
  • php代码在调试时,经常是print_r或者var_dump来断点,但是当项目较为复杂的情况下,这么做效率就非常低下了,断点调试就非常好的解决了这个问题,这篇文章主要介绍了PhpStorm本地断点调试的方法步骤,感兴趣的伙伴...
  • 你是怎么调试 JavaScript 程序的?最原始的方法是用 alert() 在页面上打印内容,稍微改进一点的方法是用 console.log() 在 JavaScript 控制台上输出内容。嗯~,用这两种土办法确实解决了很多小型 JavaScript...
  • Dev C++如何设置断点调试程序源代码调试过程加入断点打开调试板点击调试按钮调试程序退出调试结 源代码 #include <stdio.h> int main() { /* 我的第一个 C 程序 */ printf("Hello, World! \n"); /*设置...
  • 微信小程序真机调试

    千次阅读 2020-12-29 23:45:41
    微信游戏在发布前,我们需要安装真机运行并且查看追踪其运行Log日志,以便查询排除隐藏的...Source是代码执行断点调试窗口 Console是运行Log日志输出窗口 以上是实现真机调试的过程。 另外,你会发现微信开发工具
  • 主要介绍使用Firebug、debugger、debugger在程序中加入断点调试等,但是这些调试技巧都不借助于浏览器之外的工具,其他浏览器主要是opera、safari、chrome和ie8,感兴趣的伙伴一起看看吧
  • 今天突然发现怎么调试的,没去看官方怎么说明。...在红点上鼠标右击,选择【同步断点到内置浏览器】就可以愉快地开始调试啦! 顺带说一句,CSDN是瞧不起Hbuilder么,文章标签都没有。。。 ...
  • Android studio断点调试

    2020-03-11 14:39:22
    下面会将debug模式和Attach模式的断点调试 好了开始写一个简单的调试程序,我们先来一个for循环 设置断点(点击红点位置添加或取消断点) 点击debug模式运行 查看调试面板 一、简单调试 1. ...
  • VisualStudio 断点调试详解

    千次阅读 2019-10-31 08:56:52
    本文详细告诉大家 VisualStudio 断点调试的功能和使用方法,本文使用的是最新 VisualStudio2019 的功能,也许在你看到这篇博客的时候这个版本已经过时
  • 新手小白编程利器!Debug 断点调试工具eclipse

    千次阅读 多人点赞 2021-08-03 18:08:34
    这就需要用到新手小白编程的利器——断点调试工具Debug了。 啥是Debug? 首先,对Debug做一个简单的描述: Debug:是供程序员使用的程序调试工具,它可以用于查看程序的执行流程,也可以用于追踪程序执行过程来调试...
  • QT断点调试

    千次阅读 2019-07-08 16:04:40
    QT设置断点调试: 1、知道哪里可能出问题 2、完全不清楚自己程序bug在哪 3、跳过肯定不会出错的地方 前言: 这个东西难道大家不都应该会吗??? 本人用Qt Creator编写Qt的,~ 当然是有大佬用记事本写Qt的hhhh ,...
  • 调试笔记--keil 断点调试小技巧

    千次阅读 2021-06-10 11:37:34
    调试笔记–keil+jlink断点调试小技巧
  • Debug---Eclipse断点调试基础

    千次阅读 2011-09-08 11:52:27
    1.进入debug模式(基础知识列表)1、设置断点 2、启动servers端的debug模式 3、运行程序,在后台遇到断点时,进入debug调试状态 ============================= 作用域 功能 快捷键 全局 单步返回 F7 全局 单步跳过 ...
  • PhpStorm本地断点调试

    万次阅读 多人点赞 2017-09-27 09:44:49
    1、断点调试php环境搭建 2、开始你的断点调试 3、断点调试的一些简单操作
  • 断点的源由:   INT 是Intel系列CPU的一个指令,可以让程序产生一个中断或者异常。程序中如果有中断或者异常发生了以后,CPU会中断程序的执行,去一个叫做IDT的部件查找处理这个中断(或者异常)的例程...
  • 配置VS Code 使其支持vue项目断点调试

    千次阅读 2020-11-02 22:29:03
    ...在平常开发过程中我们可能会借助 console.log 来排查,但是现在我们可以借助 VS Code 断点调试项目。 前置条件 浏览器:Chrome 编辑器:VS Code vscode扩展插件:Debugger for Chrome 项.
  • Idea 断点调试PHP

    千次阅读 2018-10-29 16:48:28
    老实说 我尝试过xdebug,但是说实话 没一次成功过 看来我还是 经验不足   简单的方法: ...3.把 右上角的电话打开,在需要调试的地方打上断点,然后点击右上角的绿色的三角按钮,就可以断点 调试
  • 小程序开发工具调试

    千次阅读 2018-08-17 22:44:51
    小程序开发过程中,调试是让程序跑起来的关键。 第一步:点击调试器 第二步:点击sources 第三步:在目录中找到需要调试的js文件,打开图中文件,注意不是后缀为js的文件,而是js?[sm]的文件 第四步: 在...
  • 2019-11-29-VisualStudio-断点调试详解

    千次阅读 2019-11-29 16:00:52
    title author date CreateTime categories VisualStudio 断点调试详解 lindexi 2019-11-29 08:41:17 +0800 2019-06-21 16:16:57 +0800 VisualStudio 本文详细告诉大...
  • 展示更多 的测试信息 1.设置 2.信息查看: 从上面也可以看出来 第一个箭头,打开新页面后,前一个页面被...查看小程序这些调试信息,就很容易,对我们程序的运行先后次序有个了解,什么方...
  • 我说实话,我从去年大三暑假,也就是2015.8月份开始接触Android,到现在已经工作10个月了,从eclipse 用到 AndroidStudio,但是几个月前我才知道用断点调试调试程序的,你造吗?有人问我那你之前是怎么过来的,还写了好几个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 77,366
精华内容 30,946
关键字:

小程序断点调试