Статус-кво веб-просмотра апплета
Сцена взаимодействия (прыжка) страницы h5 в апплете
- h5 переходит на исходную страницу апплета (например: вызывает возможность выбора адреса апплета, а затем возвращает соответствующую адресную информацию на страницу h5)
- h5 переходит на страницу h5 собственной бизнес-линии (внутреннее взаимодействие со страницей, есть разные способы)
- h5 переходит на страницы h5 других бизнес-направлений (например, процесс транзакции, связанные страницы могут быть предоставлены другими бизнес-направлениями)
основные болевые точки
- После выполнения соответствующих операцийСтатус страницы необходимо обновить, текущие общие методы обновления следующие:
- Первый: передать параметры через URL-адрес (например, добавить __isonshowrefresh=1 к URL-адресу, чтобы указать веб-просмотру обновляться, когда он снова отображается), соединить параметры, которые будут переданы в URL-адрес, и повторно открыть URL-адрес.
- Второй тип: вам нужно перейти на новую страницу для обновления данных (например: страница заказа - страница выбора адреса - новая страница заказа)
- Первое решение не имеет проблем в работе, но оно приведет к обновлению страницы.Если операция со страницей сложна, ее необходимо обновить несколько раз.
- Во второй схеме опыт работы вперед лучше, чем в схеме 1, но приводит к другой проблеме: операцияУровень прыжка слишком глубокий, особенно когда он вернулся, это было просто неприятно.
В апплете страница h5 открывает новый метод страницы
Давайте сначала посмотрим на распространенный способ перехода h5 через h5 в небольших программах:
- Способ 1: используйте location.href для прямого перехода. При возврате производительность каждой модели непостоянна. Некоторые обновляют страницу, повторно запускают js, а некоторые напрямую отображают предыдущий кеш.
- Способ 2: перейти через хэш маршрутизации, вернуться, чтобы вызвать изменение хэша, страница не обновляется, а рендеринг воспроизводится на уровне js.
- Способ 3. Перейти на страницу, чтобы открыть новое веб-представление, что эквивалентно тому, что каждая страница является независимым веб-представлением.
Мы используем метод 3 по следующим причинам:
- Эффект от открытия новой страницы ближе к прыжку между нативами (конечно, вновь открытая страница тоже будет перезагружать статические ресурсы, и с этим тоже есть другая проблема. Как только вы открываете 10 уровней, а потом открываете новый вебвью, возникает нет проблем.Ответили,это 10-слойный лимит апплета)
- Возвращенный опыт также ближе к родному, при этом гарантируется, что статус страницы унифицирован (некоторые не будут отображаться напрямую, а некоторые будут повторно выполнять js)
- Ссылка, полученная webview через this.src, является ссылкой на текущую страницу, потому что если страница сама перескочит через routing и location.href, то после смены ссылки страницы webview об этом не узнает, в этой схеме webview проходит через this.src Ссылка — это всегда ссылка на текущую страницу.
Потому что эта схема может достигать 10-слойного предела апплета. Поэтому рекомендуется добавить "вернуться на главную страницу", с помощью этой операции, чтобы сократить стек истории апплета
Вернуться на главную страницу Краткое описание программы
(Если вам не интересна эта часть, вы можете сразу ее пропустить)
wx.miniProgram.reLaunch({
url: '/pages/webview/bridge?url=项目首页地址'
})
Сначала объявите, что путь нашего веб-просмотра: /pages/webview/webview
/pages/webview/bridge — это транзитная страница со следующими функциями:
- эта страница неНе страница веб-просмотра, которая, наконец, открывает страницу h5, ноТранзитная страница.
- В основном используется для обработки возврата
- Логика страницы:
- Если это первый раз, перейдите к /pages/webview/webview, передайте URL-адрес и откройте h5 в обычном режиме.
- Если это не первый дисплей, это означает, что он возвращается из веб-просмотра и перенаправляется прямо на домашнюю страницу апплета.
Эта страница передачи: в основном гарантирует, что после того, как reLaunch достигнет определенной страницы h5, пользователи все еще могут щелкнуть, чтобы вернуться на домашнюю страницу апплета.
Это решение обычно используется в таких сценариях, как страницы h5 с несколькими бизнес-направлениями, встроенными в апплет.
Сценарий выпуска контента
Мы заходим на страницу выпуска с домашней страницы, а после завершения выпуска переходим на страницу сведений о продукте.
Итак, для нового пользователя весь процесс работы выглядит следующим образом:
- Главная (нажмите, чтобы опубликовать)
- Войдите на страницу публикации (выберите категорию публикуемого продукта)
- Войдите на страницу категории продукта (после завершения выбора)
- Введите идентификатор категории в URL-адрес и введите новую страницу публикации (выберите адрес)
- Войдите на страницу со списком адресов (если у нового пользователя нет адреса, нажмите, чтобы добавить адрес)
- Введите новую адресную страницу (после завершения добавления)
- Введите идентификатор адреса как URL-адрес и введите другую новую страницу публикации (нажмите «Опубликовать» после редактирования информации).
- Выпущено на странице успеха (нажмите, чтобы просмотреть сведения о продукте)
- Войдите на страницу сведений о продукте
Этот сценарий представляет собой одну и ту же страницу, и разные элементы содержимого на ней должны переходить на разные страницы для работы, а затем возвращаться к проблеме исходного состояния обновления страницы.
Если на странице сведений о продукте нет записи «вернуться на домашнюю страницу», пользователь хочет вернуться на домашнюю страницу. . . Нужно нажать "возврат" 8 раз ==!
После этого опыта я думаю, что у обычных пользователей не хватает смелости публиковать контент.
Конечно, есть еще один такой компромисс
Как указано в продукте, добавьте в соединение определенный флаг, например, добавьте в URL-адрес __isonshowrefresh=1.Когда webview открывает соединение, он будет читать этот параметр.Если есть, он будет перезагружаться каждый раз при onShow. URL, статус страницы обновляется путем обновления страницы.
Этот опыт тоже не из приятных, то есть сложная страница будет многократно обновляться.
утверждение
Решение, о котором я собираюсь рассказать, не находится на стадии обдумывания, оно уже работает в сети.
Друзья, которые хотят увидеть эффект, могут искать в апплете WeChat:
«Zuanzhuan Second-hand Trading Network» — «0 юаней бесплатно» — (внизу) «отправить без дела, чтобы заработать звезды» — после входа на страницу выпуска
-
Классификация (переход h5, возврат после выбора содержимого, передача параметров предыдущему h5)
-
Адрес получения (перейти к выбору собственного собственного адреса, вернуться после выбора и передать параметры предыдущему h5)
Хорошо, давайте перейдем к сегодняшней теме
Реализация страницы h5 onShow и межстраничной связи в апплете
Первое, что приходит на ум, это реализация метода onShow, кто-то предложил использовать visibilitychange для реализации метода onShow.
Однако после расследования этот метод работает должным образом в ios, но в телефонах Android он не может быть запущен должным образом. Поэтому план был отвергнут мной.
Итак, следующая схема
Принцип введения
Это решение требует обработки как h5, так и веб-просмотра апплета.
Главная мысль:Используйте функцию хеширования веб-просмотра
- Апплет передает параметры через хэш, и страница не будет обновляться (это то же самое, что и браузер)
- h5 может захватывать последние параметры через хешрейт для пользовательской логической обработки.
- Наконец выполните window.history.go(-1)
Зачем выполнять window.history.go(-1)
Этот шаг является сутью всей программы:
- Поскольку изменение хэша приведет к тому, что длина стека истории веб-просмотра будет равна +1, пользователю необходимо вернуться к операции еще раз. Но этот шаг явно лишний.
- В то же время, после window.history.go(-1) параметры, добавленные веб-просмотром в хеш, будут удалены, и он также может быть гарантированно таким же, как и предыдущий URL-адрес.
Расширение схемы (межстраничная передача данных)
Еще один распространенный сценарий в мини-программах — позвонить в третью компанию (или в свою), и после завершения некоторых операций выбранные данные необходимо вернуть на предыдущую страницу.
Как в примере, упомянутом выше: страница публикации, вам нужно выбрать тип публикации, а затем вернуться, тип публикации страницы публикации частично обновлен
Конечно, некоторые студенты скажут: я могу использовать setInterval для мониторинга localStorage. После выбора контента на новой странице установите localStorage, а затем вернитесь к no.
Я сказал вотОбщее решение. Если страницы поддерживаются собственными бизнес-направлениями, конечно, вы можете их небрежно выкинуть.
Но как только речь идет о сторонних бизнес-направлениях, особенно о деловых вызовах на страницах с разными доменными именами, этот метод связи становится неудобным.
Итак, как мне справиться с моим планом, я подытожил картину
Давайте интерпретируем эту картинку:
- webview1 открывает страницу публикации, а h5 связывает событие hashchange (поскольку событие запускается, когда webview передает значение через хэш)
- Кэшируйте пользовательский метод onShow. Когда хешрейт запускается, ищите указанный параметр и запускайте его, если он существует.
- Пользователь щелкает Перейти на страницу выбора типа
- Это откроет новый экземпляр страницы webview2, откроет страницу выбора типа
- После завершения пользовательской операции вызовите wx.miniProgram.postMessage, чтобы отправить данные в веб-просмотр, и верните
- Поскольку веб-представление привязано к событию bindmessage, оно получит данные, отправленные h5, когда вернется.
- При этом полученные данные кэшируются в глобальном хранилище, webview2 уничтожается, а выполнение апплета возвращается
- Вернитесь из webview2 в webview1, затем сработает хук onShow webview1
- webview1 читает глобальное хранилище, извлекает параметры для отправки, объединяет хэш-часть ссылки h5 и повторно открывает ссылку.
- Хотя ссылка повторно открыта, страница не будет обновляться из-за изменения только хеш-части.
- Но это вызовет изменение хэша страницы h5.В это время вызывается пользовательский метод onShow, считываются параметры хэша и страница обновляется.
- После того, как страница h5 выполнит метод onShow, она вызовет window.history.go(-1) для восстановления стека истории.
Весь процесс такой
Подсказка кода:
Апплеты
Есть несколько аспектов, которые следует учитывать перед веб-просмотром апплета:
- Ради беспрепятственного доступа мы не можем придумать один размер для всех, и мы должны обеспечить доступ к существующим страницам без каких-либо изменений.
- Новые возможности должны отличаться дополнительными параметрами, такими как: обнаружение части запроса в url, с __isonshowpro=1, и последующая передача параметров через хэш.
- Измените исходную логику, чтобы при __isonshowpro=1 логика обработки хэша имела наивысший приоритет.
- Определение параметра, два символа подчеркивания добавляются впереди, цель состоит в том, чтобы разделить обычные параметры в URL-адресе.
<web-view wx:if="{{url}}" src="{{url}}" binderror="onError" bindload="onLoaded" bindmessage="onPostMessage"></web-view>
// 链接处理工具方法
import util from '@/lib/util';
// 全局数据存储操作类
import routeParams from '@/lib/routeParams';
const urlReg = /^(https?\:\/\/[^?#]+)(\?[^#]*)?(#[^\?&]+)?(.+)?$/;
let messageData = {};
export default class extends wepy.page {
data = {
// 页面展示次数
pageShowCount: 0,
// 页面url中query部分的参数对象
mQuery: {},
...
}
onShow(){
++this.pageShowCount;
// 获取其他页面经过操作后,需要传递给h5的参数
let data = routeParams.getBackFromData() || {};
// webview页面状态更新
if(this.pageShowCount > 1 && this.mQuery.__isonshowpro && this.mQuery.__isonshowpro === '1' || data.refresh){
// 获取需要传递给h5页面的参数
let refreshParam = data.refreshParam;
...
// 如果连接中带有需要处理onShow逻辑的参数(通过url的hash和h5交互,而不是刷页面)
if (this.pageShowCount > 1 && this.mQuery.__isonshowpro === '1') {
let [whole, mainUrl, queryStr, hashStr, hashQueryStr] = urlReg.exec(this.url);
// 在url的hash中加入新的参数
hashStr = (hashStr || '#').substring(1);
if (refreshParam) {
delete refreshParam.refresh;
}
const messageData = this.getNavigateMessageData();
// 将需要更新的参数传给页面hash
hashStr = util.addQuery(hashStr, Object.assign({
// onshow标志位
__isonshow: 1,
// wa主动触发hashchange标志位
// 其实目前通过__isonshow就可以判断是wa主动触发hashchange
// 设置该字段是为了明确功能,且以后扩展用
__wachangehash: 1,
// 时间戳刷新
__hashtimestamp: Date.now()
}, messageData, refreshParam));
this.url = mainUrl + queryStr + '#' + hashStr;
console.log('【webview-hashchange-url】', this.url);
// 这里要加个延迟,否则在webview返回到webview时,无法触发hashchange,应该是小程序bug
setTimeout(()=> {
this.$apply();
}, 50);
// 通过修改query参数,刷新webview
} else {
...
}
...
}
}
/**
* 获取需要发送的消息数据
*/
getNavigateMessageData(){
let rst = {};
for(let i in messageData){
/* message结构:
message: {
key: 'xx', // 消息名称
content: 'xx', // 消息内容
trigger: { // 触发条件
type: '', // 触发类型
- immediately 在下一次onshow或者打开页面中立刻触发,
- url 在找到指定h5链接时触发
content: '' // 条件内容
- type=immediately 时为空
- type=url 时候为h5链接地址
}
}
*/
const message = messageData[i];
const trigger = message.trigger || {};
// 立刻发送、路径触发
if(trigger.type === 'immediately' || trigger.type === 'url' && this.url.indexOf(trigger.content) > -1){
// 将key和content集合到一个对象中,便于hash直接设置
rst[message.key] = message.content;
// 消息通知后,从缓存中删除
delete messageData[message.key];
}
}
console.log('【webview-get-message】', rst);
console.log('【webview-message-cache】', messageData);
return rst;
}
/**
* 存储消息数据
*/
storeNavigateMessageData(message){
if(message && message.key){
console.log('【webview-store-message】', message)
// 通过key设置每一条消息名称
messageData[message.key] = message;
console.log('【webview-message-cache】', messageData);
}
}
methods = {
// 接收发送过来的消息
onPostMessage(e){
if(!e.detail.data)return;
const detailData = e.detail.data;
// 获取消息数据
let messageData = getValueFromMixedArray(detailData, 'messageData', true);
if (messageData) {
// 存储
this.storeNavigateMessageData(messageData);
}
...
}
}
...
}
Выше было много вещей, и я могу обобщить их в нескольких пунктах:
- Привязать событие bindmessage
- После получения сообщения со страницы, его нужно сохранить по определенным правилам (я сохранил по ключу)
- Когда веб-просмотр запускает хук onShow, он извлекает данные сообщения для отправки в соответствии с условием триггера (условием), переданным до этого.
- Вставьте данные в хэш-часть URL-адреса, добавьте уникальный флаг и перезагрузите URL-адрес.
терминал h5
Есть несколько моментов, которые следует учитывать при внесении изменений на стороне h5:
-
Лучше всего инкапсулировать эту логику взаимодействия
-
Сделать звонки бизнес-партнерам проще и удобнее
-
Здесь я недавно определил 2 метода
- onShow(callback)
- Описание: Это то же самое, что и апплет onShow hook, но он вызывается для h5.
- Параметры: callback метод обратного вызова
Пример: опубликуйте страницу, вам нужно выбрать категорию, и вам нужно обновить информацию о категории, когда вы вернетесь
import { isZZWA, onShow } from '@/lib/sdk' import URL from '@/lib/url' ... created () { if (isZZWA()) { onShow(() => { // 地址信息 const addressInfo = URL.getHashParam('zzwaAddress') console.log('addressInfo:', decodeURIComponent(addressInfo)) ... // 分类信息 const selecteCateInfo = URL.getHashParam('selecteCateInfo') console.log('selecteCateInfo:', selecteCateInfo) ... } else { ... } } ...
- serviceDone(data, condition)
- Описание: Дело окончено, а данные нужно передать на указанную страницу
- параметр:
- data Object Данные для передачи {key: 'xx', content: 'xx'}
- условие Строка|Число Условие срабатывания
- Строка указывает путь к URL-адресу. Когда веб-просмотр открывает указанный URL-адрес и запускает onshow, сообщение будет отправлено
- Число Возврат к указанному тесту, аналогичному history.go(-1), например: -1, -2
Пример: страница выбора типа
import { isZZWA, serviceDone } from '@/lib/sdk' // 类型选择点击 typeChooseClick (param, type) { ... if (isZZWA()) { // 需要返回的数据 const data = { key: 'selecteCateInfo', content: JSON.stringify({...}) } // 通过postMessage发送给小程序,-1表示返回上一页面 serviceDone(data, -1) } else { ... } }
- onShow(callback)
ок, посмотрим как реализован sdk на стороне h5
import util from './util';
class WASDK {
/**
* Create a instance.
* @ignore
*/
constructor(){
// hashchang事件处理
if('onhashchange' in window && window.addEventListener && !WASDK.hashInfo.isInit){
// 更新标志位
WASDK.hashInfo.isInit = true;
// 绑定hashchange
window.addEventListener('hashchange', ()=>{
// 如果小程序webview修改的hash,才进行处理
if (util.getHash(window.location.href, '__wachangehash') === '1') {
// 这块有个坑:
// ios小程序webview在修改完url的hash之后,页面hashchange和更新都可以正常触发
// 但是:h5调用部分小程序能力会失败(如:ios在设置完hash后,调用wx.uploadImg会失败,需要重新设置wx.config)
// 因为ios小程序的逻辑是,url只要发生变化,wx.config中的appId就找不到了
// 所以需要重新进行wx.config配置
// 这一步是获取之前设置wx.config的参数(需要从服务端拿,因为之前已经获取过了,这里从缓存直接取)
const jsticket = window.native && window.native.adapter && window.native.adapter.jsticket || null;
const ua = navigator.userAgent;
// 非安卓系统要重新设置wx.config
if (jsticket && !(ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1)) {
window.wx.config({
debug: false,
appId: jsticket.appId,
timestamp: jsticket.timestamp,
nonceStr: jsticket.noncestr,
signature: jsticket.signature,
jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ',
'onMenuShareQZone', 'onMenuShareWeibo', 'scanQRCode', 'chooseImage', 'uploadImage', 'previewImage', 'getLocation', 'openLocation']
})
}
// 触发缓存数组的回调
WASDK.hashInfo.callbackArr.forEach(callback=>{
callback();
})
// 执行返回操作(这一步是重点!!)
// 因为webview设置完hash参数后,会使webview历史栈+1
// 而实际并不需要这次多余的历史记录,所以需要执行返回操作把它去掉
// 即便是返回操作,也仅仅是hash层面的变更,所以不会触发页面刷新
// 用setTimeout表示在下一次事件循环进行返回操作。如果后面有对dom操作可以在当前次事件循环完成
setTimeout(()=>{
window.history.go(-1);
}, 0);
}
}, false)
}
}
/**
* hash相关信息
*/
static hashInfo = {
// 是否已经初始化
isInit: false,
// hash回调香瓜数组
callbackArr: []
}
/**
* 页面再次展示时钩子方法
* @param {Function} callback - 必填, callback回调方法, 回传参数为hash部分问号后面的参数解析对象
*/
@execLog
onShow(callback){
if (typeof callback === 'function') {
// 对回调方法进行onshow逻辑包装,并推入缓存数组
WASDK.hashInfo.callbackArr.push(function(){
// 检查是否是指定参数发生变化
if(util.getHash(window.location.href, '__isonshow') === '1'){
// 触发onShow回调
callback();
}
})
} else {
util.console.error(`参数错误,调用onShow请传入正确callback回调`);
}
}
/**
* 业务处理完成并发送消息
* @param {Object} obj - 必填项,消息对象
* @param {String} obj.key - 必填项,消息名称
* @param {String} obj.content - 可选项,消息内容,默认空串,如果是内容对象,请转换成字符串
* @param {String|Number} condition - 可选项,默认仅进行postMessage
* String - 可以传指定url的路径,当小程序webview打开指定的url或者onshow时,会触发该消息
* 也可传小程序path,这个为以后预留
* Number - 返回到指定的测试,类似history.go(-1),如: -1,-2
*/
@execLog
serviceDone(obj, condition){
if(obj && obj.key){
// 消息体
const message = {
// 消息名称
key: obj.key,
// 消息体
content: obj.content || '',
// 触发条件
trigger: {
// 类型 'immediately'在下一次onshow中立刻触发, 'url',在找到指定h5链接时触发,'path'在打开指定小程序路径时触发
type: 'immediately',
// 条件内容,immediately是为空,url是为h5链接地址,path是为小程序路径
content: ''
}
};
// 解析触发条件
condition = condition || 0;
// 如果是路径
if(typeof condition === 'string' && (condition.indexOf('http') > -1 || condition.indexOf('pages/') > -1)){
// 设置消息触发条件
message.trigger = {
type: condition.indexOf('http') > -1 ? 'url' : 'path',
content: condition
}
}
// 发送消息
wx.miniProgram.postMessage({
data: {
messageData: message
}
});
// 如果不是url或者path触发,则对conditon是否需要返回进行判断
if(message.trigger.type === 'immediately'){
// 查看是否需要返回指定的层级,兼容传入'-1'字符串这种类型的场景
try{
condition = parseInt(condition, 10);
}catch(e){}
// 保证返回级数的正确性
if(condition && typeof condition === 'number' && !isNaN(condition)){
this.handler.navigateBack({delta: Math.abs(condition)});
}
}
}else{
util.console.error(`参数错误,调用serviceDone方法,传入的对象中不包含key值`);
}
}
...
}
window.native = new Native();
export default native;
Это выглядит довольно много, и это можно суммировать в двух пунктах:
- Реализация метода onShow
- Привязать событие hashchange (это сделано для предотвращения повторных событий привязки)
- Кэшировать входящее пользовательское событие onShow в массиве.При срабатывании hashchange определяется, сработало ли оно по уникальным флагам __isonshow и __wachangehash
- Реализация метода serviceDone
- Обрабатывать поступающие данные
- Условия триггера для обработки данных: сразу указывает самый последний триггер onShow или указываете URL самостоятельно
- Отправить данные через wx.miniProgram.postMessage
хорошо, вся программа введена
Эпилог
Самый ранний план не совсем, но принцип тот же. Есть много проблем, обнаруженных в исходном решении в процессе, который я понял.
Так что я много преобразовывал и оптимизировал детали, поэтому я сформировал окончательный план выше.
Это решение представляет собой навязчивое преобразование, которое требует от каждой бизнес-стороны изменения собственного кода. Несмотря на определенную стоимость трансформации, преимущества пользовательского опыта очевидны.
ps: наш QA сказал, что "это намного проще в использовании" при тестировании
Уведомление:
При таком подходе следует помнить несколько вещей:
- Если вы общаетесь таким образом, вам нужно добавить __isonshowpro=1 в часть запроса URL-адреса текущей страницы, иначе он не будет общаться через хеш.
- При этом убедитесь, что страница вызывает метод onShow, иначе страница не обновится.
- Если стороннему предприятию необходимо передавать значения, ему необходимо использовать метод serviceDone для единообразной связи.
Ну вот и все на сегодня, учимся вместе