Схема внешнего интерфейса на основе QWebChannel

Vue.js

Эта статья одновременно опубликована вмой блог


Автор ищет возможности.Если есть подходящая возможность внутреннего продвижения, пожалуйста, отправьте личное сообщение.Почтаили оставить сообщение


Недавно в своей работе я столкнулся с некоторымиQtРазработка настольных гибридных приложений. Эта статья объединит часть моего собственного опыта разработки и расскажет о нем с точки зрения внешнего интерфейса.QWebChannelсуществуетclientПрирода инстанцирования сторон и способыQWebChannelР ГVue.jsПохож на интерфейсный фреймворк.

Сначала вы должны быть в состоянии полностью понятьJS модель цикла событийа такжеконтекст выполненияа такжестек контекста выполнения, позжеQWebChannelИнтеграция возьмет суть выполнения кода в качестве отправной точки для обсуждения реализации.QWebChannelИнтеграция с интерфейсными фреймворками. Хотя эта статьяVue.jsЭто пример, но он не ограничивает используемый вами интерфейсный фреймворк.Поняв принцип, читатели могут попробовать использоватьReactилиAngularПодождите, пока интерфейсная структура будет реализованаQWebChannelинтеграция.

Введение в технологию встроенных веб-страниц Qt

в текущемv5.xВ версии есть две реализации следующих гибридных приложенийDOC:

  1. Qt WebViewЭтот модуль ужеv5.5в одеялеустарел, и являетсяQt WebEngineзаменятьDOC,API. Раньше он в основном использовался на мобильных терминалах и не включал полныйwebстек браузера при использовании родногоAPI(то есть с использованием движка браузера на нативной стороне), реализованного вQMLСпособ отображения веб-страниц в приложении. Автор разрабатываетQtПри смешивании приложенийC++Коллеги используютv5.6.2(На момент написания этой статьи последней версией являетсяv5.13.1), поэтому эта реализация гибридного приложения не обсуждается.

  2. Qt WebEngine, что само по себе обеспечиваетwebдвигатель дляQtВстраивайте произвольный веб-контент в приложение. Этоне зависеть отвнешнийWebРеализация гибридного применения двигателя также является наиболее простым способом. Стоит отметить, чтоQt WebEngineосновывается наChromiumПроект реализован, поэтому не содержит некоторыхGoogleТакже вGoogle Chromeфункции, реализованные наChromiumПроэктИсходная библиотека восходящего потоканайти вChromiumа такжеGoogle Chromeразница.

дляclientсерединаJSПо сути,Qt WebEngineв основном обеспечиваетJSхост-среда (runtime)—Chromiumпод проектv8двигатель. Также вQtкоторый предоставилwebМеханизм рендерингаChromiumв проектеblink.

Общение с JS в Qt v5+

в пониманииQtПосле среды интеграции предусмотрен внешний интерфейс.QtпредставилQt WebChannel(именуемый в дальнейшемQWebChannel) Концепция чего-либо. Это должно быть реализовано при условии, что выполнение кода на каждом конце не может быть затронуто.Qtзаканчиваться вclientконец бесшовныйдвустороннийкоммуникация.

QWebChannelпредоставлено вServer(C++приложение) иclientконец(HTML/JS) возможность одноранговой связи. ЧерезclientконецQWebChannelвыпускатьQObjectизпроизводный объект, который реализуется вclientсторона, с которой можно легко читатьQtконец公共插槽а такжеQObjectиз属性值а также方法. В течение всего процесса связи нет необходимости в какой-либо ручной сериализации входящих параметров. всеQtконец属性обновить,signalтриггер, будетавтоматический и асинхронныйОбновить доclientконец.

  • QObjectдаQtЯдро объектной модели в . Основная особенность этой модели называетсяsignalа такжеslotмеханизм связи объектов.

QWebChannel клиента

существуетQtзавершение реализацииQWebChannelПросто импортируйте соответствующийQtмодуль. реализоватьclientконецQWebChannel, необходимо ввестиQtофициально предоставленqwebchannel.jsgithub,officialизJSбиблиотека#. Цель этой библиотеки состоит в том, чтобы инкапсулировать рядКоммуникационный интерфейсИ метод сериализации информации при передаче информации.

для разных целейWebsite существуют разные способы импорта статических файлов:

  1. QWebEngineЛокализованные сайты в: Черезqrc:///qtwebchannel/qwebchannel.jsПредставлять.

  2. Удаленноwebсайт, официальныйqwebchannel.jsскопировать в цельwebна сервере.

Перед реализацией связи необходимо создать экземплярQWebChannelобъект и передать объект для передаточной функции (называетсятранспортный объект) и функцию обратного вызова. однаждыQWebChannelзавершить создание экземпляра иРазместить объектЕсли доступно, будет вызвана функция обратного вызова, предоставленная в предыдущем экземпляре. Когда вызывается функция обратного вызова, канал устанавливается.

Пример кода выглядит следующим образом:

import QWebChannel from './qwebchannel.js'

/**
 * @description window.qt.webChannelTransport 可用 WebSocket 实例代替。
 * 经实践发现,Qt 向全局注入的 window.qt 仅有属性 webChannelTransport,并且该对象仅有
 * 两个属性方法:send 和 onmessage
 * send 方法用于 js 端向 Qt 端传输 `JSON` 信息
 * onmessage 用于接受 `Qt` 端发送的 `JSON` 信息
 */
new QWebChannel(window.qt.webChannelTransport, initCallback)

в примере кодаwindow.qt.webChannelTransportто естьtransportобъект иinitCallbackвQWebChannelЗавершите создание экземпляра и получитеQtФункция обратного вызова, которая будет вызываться только после публикации объекта на стороне. Когда вызывается функция обратного вызова,Разместить объектдолжны быть доступны и содержать всеQtобщая информация на терминале, такая как属性,方法, за которым можно следитьcpp signalи другая информация.

транспортный объект

Вообще под транспортом подразумеваются объекты window.qt.webChannelTransport (Qt сбоку через v8templatesв глобальную среду) или экземпляр WebSocket

указано вышеtransportобъект#Реализован минималистичный интерфейс передачи информации (interface). Этовсегдадолжно бытьsendобъект метода,sendфункция (функциональное позиционирование этой функции может быть аналогичноWebSocket.send) будет передавать строковыйJSONинформацию и отправить ее наQtконецQWebChannelAbstractTransportобъект. Кроме того, когдаtransportОбъект принимает завершение отQtинформация на стороне должна называтьсяtransportобъектonmessageАтрибуты. По желанию вы можете использоватьWebSocketдля реализации интерфейса (т.е.transportобъект).

  1. согласно сВторой абзац официального документаописывать,onmessageКогда функция вызывается, оназадача макросавызывается вместо того, чтобы вызываться после обертывания функцией источника микрозадачи (какPromise.thenобернутая функция обратного вызова).

  2. #Note that all communication between the HTML client and the QML/C++ server is asynchronous. все вclientа такжеQML/C++Связь между сервисами естьасинхронныйиз.

    в официальномqwebchannel.jsвидимый в56 строка также65 строка также75 строк,когдаtransportобъект получает отQtинформация на стороне, позвонюonmessageметод, поэтому этот метод по существу являетсяпарсер сообщений. Этим методом вJSконецраспределениеразличные видыQtсообщение, которое будет вызываться после инициализацииQWebChannelФункция обратного вызова, определенная в callback. Это тожеQtконец иJSконецПрирода асинхронной связи. После отправки каждого сообщения функция отправки сообщения выходит из стека контекста выполнения и не блокирует текущую очередь задач, чтобы дождаться ответа на сообщение (task queue).

Обратите внимание, что однаждыtransportКогда объект доступен,JSизQWebChannelОбъект должен быть создан. еслиWebSocketреализации, а это означает, что вsocketизonopenдолжен быть создан в обратном вызовеQWebChannelобъект. в официальномQWebChannelнапример, на основеWebSocketосуществленный. Позже будет описано, что нетWebSocketКак добитьсяQtконец иclientСлучайная асинхронная связь.

Обратный вызов создания экземпляра QWebChannel

однажды перешел кQWebChannelКогда вызывается функция обратного вызова конструктора, это указывает, чтоchannelсоздание экземпляра завершено, и всеQtопубликованоРазместить объектМожет пройтиchannel.objectsНедвижимостьJSКлиентский доступ. Обратите внимание, что всеJSклиент иQML/C++Связь между сервисами естьасинхронныйиз. свойства могут бытьJSКонец кэшируется. Кроме того, помните, что только можно преобразовать вJSONизQML/C++Тип данных будет должным образом сериализован или обратная последовательность, таким образом,JSКлиентский доступ.

Здесь в анализе исходного кода позже можно сделать вывод, что:QWebChannelСмысл создания экземпляра асинхронного обратного вызова заключается в реализации чего-то вродеTCPна этапе заключения договоратрехстороннее рукопожатие. для обеспеченияQtконец иclientКанал связи терминала обычно доступен.

interface Channel {
  objects: {
    [contextKey: string]: any
  }
}

new QWebChannel(window.qt.webChannelTransport, (channel: Channel) => {
  // 所有发布于 Qt 的发布对象都在 channel.objects 下
  // 值得注意的是,必须提供一个上下文名称,将共享信息挂载到 channel.objects[上下文]
})

Стоит отметить, что вclientпередатьQtПри публикации объекта его необходимо совместить сclientВся информация, совместно используемая клиентом, монтируется на один или несколькоchannel.objectsПод пространством имен нельзя монтировать напрямую вchannel.objectsВниз. который:

  • Qtсторона: webchannel.cpp

    
    // WebBridge 类包含了一些与 JS 共享信息
    class WebBridge: public QOject
    {
        Q_OBJECT
        public slots:
        void invokedByClient()
        {
            QMessageBox::information(NULL,"invokedByClient","I'm called by client JS!");
        }
    };
    
    WebBridge *webBridge = new WebBridge();
    QWebChannel *channel = new QWebChannel(this);
    channel->registerObject('context', webBridge);
    view->page()->setWebChannel(channel);
    
  • clientсторона: мост/init.ts

    interface Channel {
      objects: {
        context: {
          [contextKey: string]: any
        }
        [namespaceKey: any]: {
          [key: string]: any
        }
      }
    }
    
    new QWebChannel(window.qt.webChannelTransport, (channel: Channel) => {
      const qtContext = channel.objects.context
      // 此时 qtContext 包含了 Qt 端 context 命名空间下所有与 client 端共享的信息
    })
    

Изучите сущность QWebChannel на стороне клиента

Согласно предыдущему заявлению,QWebChannelэкземпляр существуетФункция асинхронного обратного вызова. тогда дляИсследованиев каком видевозможностьприходи и уходиVue.jsИнтеграция в другие фреймворкиQWebChannelизопубликовать контейнер объектови избегайтеQWebChannelконтейнер опубликованных объектовchannel.objects(включает всеpublished objects- отQtобщая информация на стороне) напрямую выставлена ​​в глобальной среде. будет обсуждаться нижеQWebChannelизПуть инициализации (создание экземпляра + асинхронный обратный вызов)исследовать гору черезQWebChannelОпубликовано изCppизРазместить объект.

существуетJSбоковая инициализацияQWebChannelКогда существует следующая логика для запускаQWebChannelЭкземпляр:

import QWebChannel from './qwebchannel.js'

new QWebChannel(window.qt.webChannelTransport, initCallback)

В приведенном выше коде в глобальной средеqt.webChannelTransportОбъект описан вышетранспортный объект. Объект создаетсяQtконец черезC++код, внедренный вclientв глобальном окружении терминала. На практике обнаруживается, что объект находится вQt v5.6.2При внедрении в версию включены только следующие два метода:

// TS types
interface MessageFromQt {
  data: {
    type: number
    [dataKey: string]: any
  }
}

declare global {
  interface Window {
    qt: {
      webChannelTransport: {
        send: (data: any) => void
        onmessage: (message: MessageFromQt) => void
      }
    }
  }
}

qt.webChannelTransport инъекция

распечатайте приведенный выше кодsendметода видно, что тело функции не родноеJSсинтаксический код, ноv8 templates. В результате исследований было установлено, что вQtWebEngineизоткрытый исходный код, показываятранспортный объектКак внедряется в глобальную среду. В целях сохранения преемственности темы статьи данная статья не корректна.C++Код расширен и интерпретирован.Если читателям интересно, они могут комбинироватьQtцитируется вChromiumИ заголовочные файлыv8изосновная концепциятак же кактип документаинтерпретировать.

Интерпретация в одном предложении: По существуQtWebEngineс помощьюv8Один экземпляр выборкиJSглобальный объект, затем в глобальномglobalКрепление на объектqtобъект и его подчиненныеwebChannelTransport.

существуетздесьЧитатели могут найти официальнуюChromiumсклад, и вGithubможно найти наChromiumзеркальный репозиторий. Кроме того, упомянутые выше заголовочные файлы в основном сосредоточены вginа такжеthird_party/blinkпапка.

функция onmessage во время инициализации

в пониманииtransportПосле введения вещества объекта,transportвторой метод в объектеonmessageФункция может быть просмотренаqwebchannel.jsОбнаружение исходного кода, мы создаем экземплярQWebChannelтолько что загруженSOURCE.

function QWebChannel(transport, initCallback) {
  // some code is here

  var channel = this
  this.transport = transport // qt.webChannelTransport 或 一个 WebSocket 实例

  // 注册 onmessage 函数以用于接受来自 `Qt` 端的 JSON 消息
  this.transport.onmessage = function(message) {
    var data = message.data
    if (typeof data === 'string') {
      data = JSON.parse(data)
    }
    switch (data.type) {
      case QWebChannelMessageTypes.signal:
        channel.handleSignal(data)
        break
      case QWebChannelMessageTypes.response:
        channel.handleResponse(data)
        break
      case QWebChannelMessageTypes.propertyUpdate:
        channel.handlePropertyUpdate(data)
        break
      default:
        console.error('invalid message received:', message.data)
        break
    }
  }
  // some code is here
}

в каждом экземпляреQWebChannel, глобальная среда будетqt.webChannelTransportподняться наQWebChannelпримерtransportв свойствахSOURCE. и экземплярsendметод странспортный объектизsendспособ подключения. вызывающий экземплярsendметодПриродавыше, чтобы позвонитьтранспортный объектизsendспособQtтерминал для отправки сообщения. во время звонкатранспортный объектизsendметод по существу вызывается передQtвнедряется в глобальную средуv8 template, чтобы добитьсяQtотправлено изJSНовости.

function QWebChannel(transport, initCallback) {
  // some code is here
  var channel = this
  this.transport = transport

  this.send = function(data) {
    if (typeof data !== 'string') {
      data = JSON.stringify(data)
    }
    // 即是调用 qt.webChannelTransport 或 WebSocket 实例的 send 方法
    channel.transport.send(data)
  }
  // some code is here
}

трехстороннее рукопожатие

существуетqwebchannel.jsСледующие функции экземпляра существуют вexecупаковатьтранспортный объектизsendметод, какQtспособ отправки сообщенийодин. После отправки сообщения соответствующие функции обратного вызова сохраняются, и эти функции обратного вызова будут храниться в экземпляре экземпляра.execCallbackв свойствах.

this.execCallbacks = {} // 所有的回调函数容器
this.execId = 0
this.exec = // ... 后文将对此做必要分析

Если читатели заинтересованы, они могут углубиться в исходный код, чтобы узнать, отслеживает ли онC++собственность по-прежнемуsignalнеобходимо уведомить через эту функциюQtконец.

// Qt signal 处理函数
this.handleSignal = //...

// Qt 消息处理函数,如通信初始化时的三次握手就是该函数来处理的。
this.handleResponse = // ...

// Qt 属性更新的处理函数
this.handlePropertyUpdate = // ...

Затем в зарегистрированном экземпляреexecПосле метода 3 экземпляра впоследствии регистрируются для обработки изQtФункция обратного вызова для сообщения.

QWebChannelЗавершающим этапом создания экземпляра является реализацияQtканал связиинициализацияSOURCE, похожий наTCPпротокол三次握手wiki. Целью этого шага является обеспечение доступности канала связи.

// 1. 调用前文所述的 exec 实例方法,通知 Qt 端初始化通信通道
// 2. 设定一个回调用于接受 Qt 端的初始化通道响应
channel.exec({ type: QWebChannelMessageTypes.init }, function(data) {
  for (var objectName in data) {
    // 创建信息载体 —— client 端的 QObject
    var object = new QObject(objectName, data[objectName], channel)
  }
  // now unwrap properties, which might reference other registered objects
  for (var objectName in channel.objects) {
    channel.objects[objectName].unwrapProperties()
  }
  if (initCallback) {
    // 调用初始化的回调函数
    initCallback(channel)
  }
  // 3. 发送第三次握手信息
  channel.exec({ type: QWebChannelMessageTypes.idle })
})
  1. первое рукопожатие:существуетclientсоздалinitсообщение и отправить наQtтерминал, для уведомленияQtКонечная точка начинает инициализировать канал связи и возвращает объект публикации (если есть).

    1. существуетclientконецexecCallbacksВ контейнере, если есть функция обратного вызова ответа, сначала регистрируем функцию обратного вызова ответа, которая реализована следующим образом:

      this.exec = function(data, callback) {
        if (!callback) {
          // if no callback is given, send directly
          channel.send(data)
          return
        }
        if (channel.execId === Number.MAX_VALUE) {
          // wrap
          channel.execId = Number.MIN_VALUE
        }
        if (data.hasOwnProperty('id')) {
          console.error(
            'Cannot exec message with property id: ' + JSON.stringify(data)
          )
          return
        }
        data.id = channel.execId++
        // 在 execCallbacks 容器中注册响应回调函数
        channel.execCallbacks[data.id] = callback
        // 根据前文分析,本质调用的是 qt.webChannelTransport.send 方法 来向 Qt 通信
        channel.send(data)
      }
      
    2. отправить послеinitсообщение для инициализации канала связи дляQtконец, осознатьпервое рукопожатие. Новостиbodyдля:

      {
        // QWebChannelMessageTypes 是源码顶部的配置对象
        type: QWebChannelMessageTypes.init
      }
      
  2. второе рукопожатие:Qtконец должен ответить на этоinitсообщение, еслиclientТерминал обычно может приниматьQtОтветное сообщение от терминала выполнит описанную выше регистрацию в атрибуте экземпляра.execCallbacksСоответствующая функция обратного вызова в контейнере.

    вызвать первымonmessageфункция (согласноПреамбула, все ответы даныonmessageобработки и диспетчеризации задач), которые затем будут обрабатываться соответствующимиchannel.handleResponseФункция обработчика для обработки ответа.

    this.handleResponse = function(message) {
      if (!message.hasOwnProperty('id')) {
        console.error(
          'Invalid response message received: ',
          JSON.stringify(message)
        )
        return
      }
      channel.execCallbacks[message.id](message.data)
      delete channel.execCallbacks[message.id]
    }
    

    Здесь мы можем видеть, что раньшеinitперед отправкой сообщенияexecCallbacksзарегистрирован в предыдущемinitОбратный вызов для ответа на сообщение. в методе экземпляраhandleResponse, удалит полезную нагрузку из ответа и передаст ее в обратный вызов ответа для завершениявторое рукопожатие. и после вызова обратного вызова ответа в контейнереexecCallbacksудалить только что законченный вызов и выйтистек контекста выполненияфункция обратного вызова.

  3. третье рукопожатие: глубоковторое рукопожатиеОбратный вызов ответа, видимыйSOURCE:

    function(data) {
      for (const objectName in data) {
        var object = new QObject(objectName, data[objectName], channel)
      }
      // now unwrap properties, which might reference other registered objects
      for (const objectName in channel.objects) {
        channel.objects[objectName].unwrapProperties()
      }
      if (initCallback) {
        // 调用 new QWebChannel 时传入的回调函数
        initCallback(channel)
      }
      // 第三次握手发送
      channel.exec({type: QWebChannelMessageTypes.idle})
    }
    

    Когда функция выполняется, она сначала принимаетQtОтветная информация терминала, создатьclientконецQObjectдля достиженияCppконецQObjectотслеживание. в инстанцированииQObject, серииmethodкарта,signalмонитор,propertyнастройки монитора.

    в существованииinitCallbackпри звонкеinitCallbackфункция. Примечательно, что здесьinitCallbackсоздается экземпляр функцииQWebChannel, передана вторая функция обратного вызова. позвони в это времяinitCallbackчас,QtконецQObjectуже сclientконец черезвторое рукопожатиедобиться синхронизации.

    наконец,clientконецQtконецтретье рукопожатиепросьба сообщитьQtстороне, все опубликованные объекты уже находятся вclientОкончание синхронизации завершено, и в это времяclientКанал связи терминала входитidleПериод — ожидание сообщения, которое будет отправлено или отправлено.

Интеграция QWebChannel с Vue.js

Интегрируйте как плагин Vue.js

вот с помощьюVue.jsизМеханизм плагинареализовать паруQWebChannelэлегантная интеграция. выставить модуль наружуQWebChannelэкземпляр, а при создании экземпляраQWebChannelОбратный вызов инициализацииchannel.objectsзарегистрироваться наVueпрототип, что делает егоVueизСвойства прототипа. Этот метод позволяет избежатьchannel.objectsВсе опубликованоQtИнформационный объект на стороне просачивается в глобальную.

  • _utils/index.ts
import Vue from 'vue'

export const isQtClient = (function() {
  return navigator.userAgent.includes('QtWebEngine')
})()

export const bus = new Vue({})

export function assert(condition: any, msg: string) {
  // falsy is not only 'false' value.
  if (!condition)
    throw new Error(msg || `[ASSERT]: ${condition} is a falsy value.`)
}

const __DEV__ = process.env.NODE_ENV !== 'development'

Приведенный выше код находится в_utils.jsТри служебные функции в .

function описывать
isQtClient используется для определения того, является лиQtизQWebEngineокружающая обстановка. Если среда разработки браузера будет имитироватьqt.webChannelTransportОбъекты используются для предотвращения ошибок.
bus ОдинVueэкземпляр, который будет использоваться вVueРеализовано на прототипеАсинхронное крепление.
assert Функция утверждения

следующий вbridge/init.tsустановлен вQWebChannelПроцесс создания экземпляра:

  • bridge/init.ts
import Vue from 'vue'
import QWebChannel from './qwebchannel' // 另有 qwebchannel.d.ts 声明文件
import { assert, isQtClient, bus, __DEV__ } from './_utils'
import dispatch from './index'

declare global {
  interface Window {
    qt: {
      webChannelTransport: {
        send: (payload: any) => void
        onmessage: (payload: any) => void
      }
    }
  }
}

export default {
  install(Vue: Vue) {
    if (!__DEV__) {
      assert(
        window && window.qt && window.qt.webChannelTransport,
        "'qt' or 'qt.webChannelTransport' should be initialized(injected) by QtWebEngine"
      )
    }

    // 用于在浏览器端开发时,模拟 `Qt` 的注入行为
    if (__DEV__ && !isQtClient) {
      window.qt = {
        webChannelTransport: {
          send() {
            console.log(`
              QWebChannel simulator activated !
            `)
          }
        }
      }
    }

    new QWebChannel(window.qt.webChannelTransport, function init(channel) {
      const qtContext = channel.objects.context

      // 官方示例直接在此,将整个 channel.objects 对象注册到全局对象上,这里并不推荐这样做。

      /**
       * @description 这里笔者采用的方法是注册到 Vue 的原型对象上,实现在任意子组件中都可访问 `Qt` 的所有发布在 context 下的发布对象。
       */
      Vue.prototype.$_bridge = qtContext

      /**
       * @description 此处时调用了 Cpp 的同名方法 onPageLoaded
       * @destination 用于通知 Qt 端 client 的 Vue.js 应用已经初始化完成
       * @analysis 后文将会分析为什么此处回调可表示 Vue.js 应用已经完成初始化
       */
      qtContext.onPageLoaded('', function(payload: string) {
        dispatch(payload)
        console.info(`
          Bridge load !
        `)
      })

      // 若有需求,可继续在此注册 C++ signal 的监听回调函数
      // qtContext.onSignalFromCpp.connect(() => {})
      // 以上注册了一个回调函数用于监听名为 onSignalFromCpp 的 signal
    })
  }
}

В приведенном выше примере кода главное, что нужно сделать, это:

  1. в текущемQtВ среде браузера создайте экземплярclientконецQWebChannelэкземпляр используется сQtконецасинхронныйкоммуникация.
  2. существуетQWebChannelв обратном вызове создания экземпляра будет исходить изQtВсе опубликованные объекты на стороне регистрируются наVueэкземпляр, так что любойVueДоступ в компоненте экземпляраQtопубликованный объект.

Анализ входного файла проекта Vue.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

import '@/plugins/bridge' // 其中包含 bridge 异步挂载

Vue.config.productionTip = process.env.NODE_ENV === 'development'

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

В сочетании с анализом цикла событий

период, термин имея в виду
модель цикла событий HTML living standard
задача макроса HTML living standard, ECMA

пройти черезVueисходный код (или любойVueПрименяемый火焰图(flame chart)) виден при начальном создании экземпляраVueвремя (исключая обновление данных -Vue.jsобновление данныхасинхронныйобновлено), даСинхронизироватьсоздать экземпляр. затем объединитеJS 事件循环模型только еслиsrc/main.tsфайл (скриншот1) полностью выполняется и завершает работустек контекста выполнения, выполнит следующий宏任务HTML living standard, ECMA. В настоящее время,onmessageТолько обратный вызов может стать следующей незавершенной записьюстек контекста выполненияиз宏任务.

В общих чертах вышеизложенное основано наVueСоздание экземпляра (без обновления данных)Синхронизироватьиз宏任务Эта сущность,QWebChannelсоздать экземпляр функции обратного вызоваinitCallback долженвVueВыполняется после создания экземпляра. График пламени ниже10хорошо видноVueизСинхронизироватьпроцесс инициализации.

тогда потому что./src/main.tsСам входной файл представляет собоймодуль, тогда выполнение модуля,Webpackобернуть это какфункция, то создается контекст выполнения. на основеExecution context 模型ECMA, что эквивалентно./src/main.tsКод в коде не выполняется полностью и выходитстек контекста выполнения, последующие宏任务(task)всегда только в宏任务队列 (task queue)вместо помещения в стек контекста выполнения. Вышеизложенное объясняет, почему инстанцированиеQWebChannelфункция обратного вызова, переданная вдолженвVueинициализацияПозженазывается.

bridge-flame-chart

После объединения всего вышеперечисленного анализа нетрудно выйти:

  1. initCallbackвсегда вnew Vueназывается после.
  1. на основеJSизмодель цикла событий,существуетinitCallbackкогда звонили,routerЖдатьvueВ соответствии с описанной ранее функция должна быть доступна.
  1. комбинировать1, по крайней мере, не ранееVueсоздание экземпляра завершено, иinitCallbackперед вызовом (т.трехстороннее рукопожатиедо того, как завершится второе рукопожатие), срабатываетsignalЖдатьQtкоммуникация.

FAQ

  • Почему бы не использовать в гибридных приложенияхURLобщаться?

    1. как можно нижеC++Связь между конечным и передним концами, позволяющая избежать ручной сериализации параметров и сращивания строк. При наличии вложенных объектов параметровJSON.stringifyСложность значительно ниже, чем у рукописных функций сериализации.

    2. URLдлина ограничена, за пределамиURLПосле ограничения длины последующие параметры будут отброшены. В то же время именно поэтому не должно бытьHTTP GETПричина слишком большого количества параметров в запросе.

References

Qt mirrors