[Front-end Foundation] Часто задаваемые вопросы JSBridge о веб-взаимодействии и нативном взаимодействии

JavaScript

Сегодня коротко поговоримJSBridge

Зачем говорить о jsbridge?

Нет причин

Отлично,JSBridgeНесмотря на то, что он относительно старый,JSBridgeПринцип также является знанием, которое в настоящее время необходимо понимать фронтенд-разработчику.

В настоящее время при разработке мобильных приложений для H5 взаимодействие с Native является обязательным.Native здесь включает в себя приложения в традиционном понимании и всевозможные сегодняшние апплеты Pit King. Обычно крупные компании инкапсулируют свой собственный набор js sdk для предоставления веб-разработчикам возможности взаимодействия с Native.Они могут иметь свои собственные имена, но все вместе они называютсяJSBridge.

JSBridgeJSBridgeПринциптыJSBridgeВзаимодействие с родным. В то же время, чтобы не видеть ненужного нативного кода, здесь, но более введеноJavaScriptCore APIДля реализации интерактивного способа вводится только оригинальная классическая схема.

Что такое JSBridge?

Проще говоря,JSBridgeЭто что-то среднее между страницей H5 и Native.Асинхронный двунаправленныйПо сути, ничем не отличается от наиболее распространенного метода связи HTTP, который мы вступаем в контакт с каждым днем.

Для лучшего опыта

Чтобы пользователь не знал, использует ли он веб-приложение или собственное приложение

для эффективности

搞开发的嘛,肯定都是为了提高工作效率,同时不降低太多用户体验的情况下,复用,复用这个复用那个,统一这个统一那个的。一款App产品要做好几个端,重复的页面扔给H5算了! Какой!需要一些奇特的功能?做个bridge接口吧!

Как проходит процесс коммуникации в JSBridge?

image

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

В нормальных условиях после нажатия звонка подойдет официант и заберет блюда.Иногда повара могут готовить N блюд одновременно и звонить в звонок N раз.В это время официанты будут очень преданы всем этим посуду, упакуйте и выбросьте.

Так как же это достигается?

Теперь пришло время перейти к сути, давайте рассмотрим основные принципы JSBridge.

  • Давайте начнем с общения из Интернета на родной:

Ранее мы узналиJSBridgeСуть H5 — это способ связи, поэтому здесь проще понять Суть вызова H5 Native — перехват запроса.

Когда мир H5 хочет общаться с внешним миром, ему нужно (и может только) отправить запрос, например, отправить простой запрос GET.

Мы можем придумать три способа отправить запрос:

  1. Запрос отправляется с помощью ярлыков атрибутов SRC, напримерiframe...
    const iframe = document.createElement('iframe')
    iframe.src = "xxx"

Этот метод также обычно используется крупными гибридными каркасами. Неоднократно отправляя большое количество сообщений не нужно беспокоиться о потере сообщений.

  1. Отправить запрос с Location.href
    location.href = "xxx"

Этот метод больше подходит для некоторых сценариев одноразового вызова, например, операция в H5 должна перейти на определенную страницу приложения, повторная отправка большого количества запросов таким образом приведет к потере сообщения запроса, и принимается только последний.

  1. Отправляйте запросы с помощью Ajax
    const url = 'xxx'
    fetch(url, { ... })
        .then()
        .catch()

Этот метод более хлопотный в написании, но производительность немного выше, чем у первых двух.

Знаменитый Cordova.js использует:

execIframe = document.createElement('iframe')
execIframe.style.display = 'none'
execIframe.src = 'gap://ready'
document.body.appendChild(execIframe)

iOS называетсяWebViewJavascriptBridge

const messagingIframe = document.createElement('iframe')
messagingIframe.src = 'https://__wvjb_queue_message__'
body.appendChild(messagingIframe)

уведомлениеCordovaWebViewJavascriptBridgeCordova

  • Требуется точечный параметр

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

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

execIframe.src = 'gap://ready?p1=v1&p2=v2&p3=v3…'

Но есть некоторые проблемы с этим подходом

  • Во-первых, его длина ограничена, хотя лимит очень большой, все-таки это будет скрытая опасность.

  • Во-вторых, если это временный интерфейс jsbridge для приложения вашей компании, вы действительно можете написать его так, но если он инкапсулирован как общий инструмент, он будет слишком тесно связан с бизнесом, если он будет реализован таким образом.

в типичномJSBridgeВ схеме реализации обработка параметров реализована следующим образом:

  • Прежде всего, всякий раз, когда JS в H5 должен вызывать Native, URL-адрес для отправки запроса является фиксированным, например, gap://ready

  • Во-вторых, определите глобальную переменную массива в окне с именем messageQueue, которое пусто при инициализации.Когда H5 нужно отправить сообщение в Native, он сначала создает объект и помещает все соответствующие параметры в этот объект, а затем помещает этот объект. вставляется в конец массива messageQueue, что объясняется в следующем коде:

const messageQueue = []
window.messageQueue = messageQueue

messageQueue.push(JSON.stringify({
	message: 'xxx',
	params: 'xxx'
}))
// 发一个请求,按一下铃,戳一下Native~~
execIframe.src = 'gap://ready'
  • Когда родной получаетgap://readyПосле запроса он знает, что в H5 есть новое сообщение, он выполнит магический код, войдет в WebView и упакует все данные в глобальную переменную массива messageQueue, определенного в окне, и очистит messageQueue. по одному. мы используемevalфункция действует как этот магический код, чтобы объяснить логику здесь:
// 当Native拦截到'gap://ready'请求后执行的magic code
const messageQueue = eval('window.messageQueue')
const messages = JSON.parse(messageQueue)
for (const message in messages) {
     doSomeThingWithMessage(message)
     …
}
eval('window.messageQueue = []')

так,Еду уносил официант.Сообщение было удалено Native

messageQueue.push(JSON.stringify({
	message: 'xxx',
	params: 'xxx',
	callBackName: 'xxx',
}))

这样,在Native执行完你需要的指令后会再次执行那段神奇的代码进入WebView的世界,执行定义在window上名为callbackName的方法,并把native执行的结果传给这个方法。 так:

const messageQueue = eval('window.messageQueue')
const messages = JSON.parse(messageQueue)
for (const message in messages) {
     const result = doSomeThingWithMessage(message)
     eval(`window[${message.callbackName}](${result})`)
     …
}
eval('window.messageQueue = []')

В то же время это также показывает, как Native отправляет сообщения в H5, просто выполните метод, определенный в окне, напрямую.

Конечно, чтобы стандартизировать код и гарантировать, что H5 не создаст callbackName случайным образом, Native не выполняет метод callbackName напрямую в окне, а будет вызывать метод callbackName, вызываемый грубо.handleMessageFromNativeЭтот метод представляет собой метод, заранее подготовленный H5 и определенный на окне.В этом методе обработка сообщения закрывается, и в нем вызывается метод callbackName на окне.После завершения выполнения вызывается метод callbackName. удалено из окна удалено, код всего процесса наверное такой:

// H5
function handleMessageFromNative (message) {
	if (typeof message.callbackName === 'function') {
		window[callbackName](message.result)
		delete window[callbackName]
	}
}

window.handleMessageFromNative = handleMessageFromNative

// Native
const messageQueue = eval('window.messageQueue')
const messages = JSON.parse(messageQueue)

for (const message in messages) {
     const result = doSomeThingWithMessage(message)
     
     const messageFromNative = JSON.stringify({
        result,
        callbackName: message.callbackName
     })
     
     eval(`window.handleMessageFromNative(${messageFromNative})`)
     …
}
eval('window.messageQueue = []')

Здесь также есть некоторые правила генерации callbackName, если вам интересно, вы можете перейти к соответствующемуисходный код.大概和jsonp的规则类似。

Затем, чтобы облегчить другим использование, организацию и упаковку описанного выше процесса, H5 и Native одновременно предоставляют два интерфейса, что выглядит следующим образом:

// H5与Native同时增加如下两个接口供对方使用:
// ≈ function addEventListener(eventName, callback)
function registerHandler (handlerName, block) {
    window.handlers[handlerName] = block
    …
}

// Web或Native调用对方接口的方式
// ≈ dispatchEvent(eventName, data, callback)
function callHandler (handlerName, message, callback) {
    window.handlers[handlerName](message)
    …
}

Это делает очень удобным использование, например, для реализации функции сканирования QR-кода:

// Native
// 注册了一个扫描二维码的方法
registerHanlder('scanQRCode', () => {
    // ...
    Camera.open().scanQRCode()
    // ...
})
// H5
// 调用扫描二维码的方法
callHanlder('scanQRCode', { type: 'qrcode' }, result => {
    console.log('扫码结果:', result)
})

Если хотите, можете обернуть его в Promise:

// 为H5封装好的bridge-sdk.js,在H5中使用
/**
 * 扫描二维码并返回结果
 * ...
 * @memberOf Camera
 * @async
 * @returns {Promise} 可以在then中接受扫码结果`result`,参数为 { code: 'xxxxxx' }
 * ...
 */
export async function scanQRCode () {
    return new Promise((resolve, reject) => {
        callHanlder('scanQRCode', { type: 'qrcode' }, result => {
            console.log('扫码结果:', result)
            resolve(result)
        })
    })
}

Ну а выше это классикаJSBridgeРеализация, это выглядит очень просто, и нет проблем совместимости.

Поскольку у Native есть волшебный код, есть ли более тщательный способ?

имеют! ! ! В Native есть еще один волшебный API, назовем его пока функцией defineFunc, который может напрямую внедрять код Native в WebView несущей H5 и вешать его на окно WebView.

// define 翻译过来大概就是下面的这个意思
function defineFunc (funcName, func) {
    const window = webView.window ... // 通过一些Native的API拿到WebView的window
    window[funcName] = func // 这里的func 是Native的func,执行的是纯Native的代码
}

// Native
defineFunc('callSomeNativeFunction', () => {
    // 这些是由Native的代码翻译成javascript的伪代码
    const file = io.readFile('/path/to/file')
    ...
    // 做一些H5做不到的事情
    file.write('/path/to/file', 'content')
    ...
})

Это принцип использования API, такого как JavaScriptCore в iOS для достижения взаимодействия, Android имеет аналогичный метод, и есть некоторые требования к версии системы, которые можно игнорировать. Здесь это обсуждаться не будет.

Какой? ? Native может свободно выполнять код в WebView? Эта ошибка сделана Native? Серьезно испугался!

H5: Боже, мы жили в виртуальном мире! ! ! На самом дне цепи презрения! !

Да! Напоминает Матрицу, что? не видел? Показать свой возраст?

насчет нас

公众号二维码