详解小程序中h5页面onShow实现及跨页面通信方案

小程序webview的现状

h5页面在小程序中的交互(跳转)场景

  • h5跳转小程序native页面(如:调用小程序地址选择能力,然后返回对应的地址信息给h5页面)
  • h5跳转己方业务线的h5页面(内部页面交互,方式比较多样)
  • h5跳转其它业务线的h5页面(如:交易流程,相关页面可能有其他业务线提供)

主要痛点

在完成相关操作后, 页面状态需要更新 ,目前常见的更新方式有如下两种:

  • 第一种:通过url传参(如:url中加入__isonshowrefresh=1,告诉webview再次onshow时候刷新),把需要传递的参数拼接到url中,重新打开url。
  • 第二种:需要跳转到新的页面进行数据更新(如:下单页 - 地址选择页 - 新的下单页)

第一种方案,功能上没有问题,但会导致页面刷新,如果页面操作复杂,需要多次刷新

第二种方案,正向操作时体验比方案一好,但导致了另外一个问题:操作 跳转层级过深 ,尤其返回的时候简直让人崩溃。

小程序中,h5页面打开新页面方式

我们先来看下小程序中常见的h5跳h5的方式:

  • 方式1:直接用location.href跳转,返回时候各机型表现不一致,有的会刷页面,重新执行js,有的会直接展示之前的缓存
  • 方式2:通过路由hash跳转,返回触发hashchange,页面不刷新,js层面重现渲染
  • 方式3:跳转页面打开一个新的webview,相当于每个页面都是一个独立的webview

我们采用的是方式3,理由如下:

  1. 打开新页面时的效果更趋近于native间的跳转(当然新打开的页面也会重新加载静态资源,同时这也有另一个问题,一旦你打开10个层级后,再打开新的webview就没反应了,这个是小程序10层限制)
  2. 返回的体验也更趋近于native,同时保证页面状态统一(不会出现有的直接展示,有的会重新执行js)
  3. webview通过this.src拿到的链接即为当前页面链接,因为如果页面自行通过路由和location.href跳转,页面链接变更后,webview并不会知晓,这种方案,webview通过this.src拿到的链接始终是当前页面的链接。

由于这种方案可能会达到小程序的10层限制。所以在一些重要页面建议加入“ 回到首页 ”的操作,通过这个操作来缩短小程序历史栈

回到首页方案简述

(如果不感兴趣这部分可以直接略过)

wx.miniProgram.reLaunch({
 url: '/pages/webview/bridge?url=项目首页地址'
})

先声明,我们webview的路径是/pages/webview/webview

/pages/webview/bridge是个中转页,有如下特点: 该页面并 不是最终打开h5页面的webview页 ,而是一个 中转页

主要用作返回处理

  • 页面逻辑: 如果是第一次展示,则跳转/pages/webview/webview,同时把url传过去,正常打开h5
  • 如果不是第一次展示,说明是从webview返回过来的,直接重定向到小程序首页

这个中转页:主要保证reLaunch到某h5页面后,用户仍然可以点击返回到小程序首页。

该方案通常用于:小程序中内嵌了多个业务线的h5页面这种场景。

一个内容发布场景

我们从首页进入发布页,完成发布后,跳转至商品详情页

那么对于一个新用户来讲,整个操作过程是这样的:

  • 首页(点击发布)
  • 进入发布页面(选择发布商品的分类)
  • 进入商品分类页(选择完成后)
  • 将分类id拼入url,进入新的发布页面(选取件地址)
  • 进入地址列表页(如果新用户是没有地址的,点击新增地址)
  • 进入新增地址页(添加完成后)
  • 将地址id拼如url,进入又一个新的发布页面(编辑完信息后点击发布)
  • 进入发布成功页(点击查看商品详情)
  • 进入商品详情页

这个场景就是同一个页面,里面不同的内容项需要跳转不同的页面去操作,然后再回到原来页面更新状态的问题。

假如商品详情页没有“回到首页”的入口,那么这个用户要想回到首页。。。需要按8次“返回” = =!

经过这个体验后,我想一般的用户是没有勇气再发布内容的。

当然也有另一种这种折中方案

就是商品提到的,在连接中加入某个标志位,比如在url中加入__isonshowrefresh=1,webview在打开连接时候,会去读取这个参数,如果有,则每次在onShow时候,重新加载url,通过刷新页面进行页面状态更新。

这个体验也不爽,就是在复杂的页面会多次刷新。

声明

我下面要讲的这个方案并不是停留在设想阶段,它已经在线上跑了

想看效果的朋友,可以在微信小程序中搜:

“转转二手交易网”-“0元免费领”-(底部)“送闲置赚星星”-进入到发布页后

分类(跳转h5,选中内容后返回,将参数传给之前的h5)

取件地址(跳转native原生地址选择,选中后返回,将参数传给之前的h5)

OK,我们进入今天的主题

小程序中h5页面onShow和跨页面通信的实现

首先想到的就是onShow方法的实现,之前有人提议用visibilitychange来实现onShow方法。

但调研过后,这种方式在ios中表现符合预期,但是在安卓手机里,是不能按预期触发的。所以该方案被我否了。

于是就有了下面的方案

原理介绍

这个方案需要h5和小程序的webview都做处理。

核心思想: 利用webview的hash特性

  • 小程序通过hash传参,页面不会更新(这个和浏览器一样)
  • h5可以通过hashchange捕获最新参数,进行自定义逻辑处理
  • 最后执行window.history.go(-1)

为什么要执行window.history.go(-1)

这一步是整个方案的精髓:

  • 因为hash变更会导致webview历史栈长度+1,用户需要多一次返回操作。但这一步明显是多余的。
  • 同时window.history.go(-1)后,会把webview在hash中添加的参数去掉,还能保证和之前的url一致。

 方案延伸(跨页面数据传递)

小程序里另个一常见的场景就是调用第三业务(或者己方业务),在做完某些操作后需要把选中的数据带回之前的页面。

如前面提到的例子:发布页,需要选择发布类型,然后返回,发布页发布类型局部更新

当然有些同学会说:我可以用setInterval,监控localStorage。在新页面选中内容后,设置localStorage,然后在返回不就可以了。

我这里说的是 通用方案 。如果页面都是由己方业务线维护的当然可以随便折腾。

但是一旦涉及到第三方业务线,尤其不同域名页面的业务调用,这种通信方式就尴尬了。

那我的方案怎么处理呢,我总结了一张图

我们来解读一下这张图:

  • webview1打开发布页面,h5绑定hashchange事件(因为webview通过hash传值时会触发该事件)
  • 将自定义的onShow方法缓存。在hashchange触发时,寻找指定参数,如果存在则触发
  • 用户点击跳转到类型选择页
  • 这时会打开一个新的webview2页面实例,打开类型选择页
  • 用户操作完成,调用wx.miniProgram.postMessage把数据发送给webview,并返回
  • webview由于绑定了bindmessage事件,在返回时会接收到h5发送的数据
  • 同时将接收到的数据缓存在一个全局的store中,webview2销毁,小程序执行返回
  • 从webview2返回到webview1,这时webview1的onShow钩子会触发
  • webview1读取全局的store,将要发送的参数取出,拼接h5链接的hash部分,并重新打开该链接
  • 虽然重新打开链接,由于仅仅是hash部分的变化,所以页面不会刷新
  • 但会触发h5页面的hashchange,此时调用用户自定义的onShow方法,读取hash参数,进行页面更新
  • h5页面在执行完onShow方法后,调用window.history.go(-1),恢复历史栈

整个过程就是这样

代码示意:

小程序

小程序webview要先做几方面考虑:

  • 出于平滑接入的考虑,不能上来搞一刀切,要保证现有页面再不做任何修改的情况下继续访问。
  • 新能力要通过额外参数区分,如:检测url中的query部分,带有__isonshowpro=1再进行通过hash方式传参。
  • 改造原有逻辑,让__isonshowpro=1时,hash处理逻辑优先级最高
  • 参数定义,在前面加入了两个下划线,目的是为了分区url中正常的参数

小程序端webview.wpy

<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事件
  • 接收到页面传来的消息之后,需要按照一定规则存起来(我是按照key存储的)
  • webview在触发onShow钩子时候,按照之前传过来的触发条件(condition),取出需要发送的消息数据
  • 将数据拼接到url的hash部分,并加入特有的标志位,重新加载url

h5端

h5端在做修改时也要考虑几点:

最好能把这些交互逻辑封装起来

让业务方比较简单方便的调用

这里我新定义了2个方法

onShow(callback)

  • 描述:这个和小程序onShow钩子一样,只不过是给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'}

condition String|Number 触发条件

  • String 指定url的路径,当webview打开指定的url触发onshow时,会发送该消息
  • Number 返回到指定的测试,类似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 {
  ...
 }
}

ok,我们来看看h5端的sdk是怎么实现的

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方法的实现

  • 处理传过来的数据
  • 处理该数据的触发条件:immediately表示最近的一次onShow触发,或者自己指定url
  • 通过wx.miniProgram.postMessage发送数据

ok,整个方案就介绍完了

结语

最早的方案并不完全是这样的,但原理是一样的。在我实现的过程中发现原始方案有很多问题

于是我又做了大量的改造和细节优化,于是形成了上面的最终方案。

这个方案属于侵入式改造方案,需要各业务方改造自己的代码。虽然有一定改造成本,但用户体验的收益非常明显。

ps:我们的QA在测试时都说“这用起来就爽多了”

注意:

采用这个方案需要注意几点:

  1. 如果采用这种方式通信,需要在当前页面url的query部分加入__isonshowpro=1,否则是不会通过hash通信的
  2. 同时要保证页面确实调用了onShow方法,否则页面也是不会刷新的
  3. 如果第三方业务需要传值,需要统一采用serviceDone方法通信

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 微信小程序页面间通信的5种方式

    PageModel(页面模型)对小程序而言是很重要的一个概念,从app.json中也可以看到,小程序就是由一个个页面组成的. 如上图,这是一个常见结构的小程序:首页是一个双Tab框架PageA和PageB,子页面pageB, PageC. 让我们假设这样一个场景:首页PageA有一个飘数,当我们从PageA新开PageC后,做一些操作,再回退到PageA的时候,这个飘数要刷新.很显然,这需要在PageC中做操作时,能通知到PageA,以便PageA做相应的联动变化. 这里的通知,专业点说就是页面

  • 微信小程序实现即时通信聊天功能的实例代码

    项目背景:小程序中实现实时聊天功能 一.服务器域名配置 配置流程 配置参考URL:https://developers.weixin.qq.com/miniprogram/dev/api/api-network.html 二.nginx中配置反向代理加密websocket(wss) upstream websocket{ hash $remote_addr consistent; server 127.0.0.1:9090 weight=5 max_fails=3 fail_timeout=30

  • 关于进程间通信的Linux小程序

    利用工作之余为小伙伴写了份作业,关于进程间通信的.题目如下: "父进程从键盘上接受1000个数据,对其求和sum1,子进程对这1000个数平方和sum2,结果传给父进程,父进程将sum1+sum2后,打印结果." 要求:用大小为10的共享区传递1000个数据:子进程用消息机制将sum2传给父进程. 主要利用共享内存实现进程间通信,使用管道实现进程间竞争关系,FreeBSD下测试通过.代码如下:时间有限,有可能有些不足,希望高手给予指点. #include <stdio.h>

  • 小程序数据通信方法大全(推荐)

    序 本文论述的是子或孙向父传递数据的情况,自下而上 相信大家平时在小程序开发中肯定遇到过页面或者组件之间的数据通信问题,那小程序数据通信都有哪些方式呢?如何选择合适的通信方式呢?这就是本文要讨论的重点. 关系划分 在讨论都有哪些数据通信方式之前,我们先来定义一下,小程序页面.组件之间都有哪些关系.我总结了一下,大概分为以下3类: 父子关系 兄弟关系 爷孙关系 不同的关系里面,不同角色之间有可能是页面,也有可能是组件,接下来我们就一个个来揭示如何进行数据通信. 父子关系 父子关系一般主要就是两种情

  • 微信小程序中多个页面传参通信的学习与实践

    前言 微信小程序越来越火,不少公司都在将原生代码转为微信小程序代码.在开发过程中,由于微信小程序wx.navigateBack方法并不支持返回传参,导致一些页面,尤其是从列表页面跳入详情页,用户在详情页改变了状态,返回后无论是否刷新页面,体验都不是很好.在android中,我们一般采用setresult方法来返回参数,或者直接使用rxjava框架或者eventbus框架来解决此类问题. 业务分析 此类需求大概意思是:A页面进入B页面,B页面返回并传值给A. 探索之路 刚开始我想采用一个比较偷懒的

  • java网络通信技术之简单聊天小程序

    本文实例为大家分享了java实现简单聊天小程序的具体代码,供大家参考,具体内容如下 再学习完java的通信技术后,做了一个简单的窗体聊天程序.程序非常简单,主要目的是当练习巩固自己所学的东西,在这里写出来记录以下.下面直接上代码. 首先是服务端代码: package ChatTwoPackage; import java.io.*; import java.net.*; public class ChatTwoServer { public ChatTwoServer(int port,Stri

  • 详解小程序中h5页面onShow实现及跨页面通信方案

    小程序webview的现状 h5页面在小程序中的交互(跳转)场景 h5跳转小程序native页面(如:调用小程序地址选择能力,然后返回对应的地址信息给h5页面) h5跳转己方业务线的h5页面(内部页面交互,方式比较多样) h5跳转其它业务线的h5页面(如:交易流程,相关页面可能有其他业务线提供) 主要痛点 在完成相关操作后, 页面状态需要更新 ,目前常见的更新方式有如下两种: 第一种:通过url传参(如:url中加入__isonshowrefresh=1,告诉webview再次onshow时候刷

  • 详解小程序横屏方案对比

    前言 随着小程序api开放的功能日渐增多,小程序可以做到的功能和展现形式也越来越多,其中横屏的展现形式就是其中的一种,而实现横屏的方案也有多种,但是每种方案都有一定的缺陷,恰巧最近也在横屏方案上踩了不少坑,接下来就来和大家分享一下小程序的不同横屏方案的优劣(踩坑心得) 组件自带横屏方法 小程序中的媒体组件一般都会提供全屏的方法,而且全屏方法中会提供一个direction的全屏参数,可以通过这全屏参数将小程序旋转90度横屏展示,这是小程序中最简单的横屏方法. 这个方法优点在于调用的组件全屏方法做的

  • 详解python程序中的多任务

    现实生活中,有很多场景中的事情是同时进行的,比如开车的时候,手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的. 以上这些可以理解为多任务.那在程序中怎么能做到多任务,它有什么好处? 接下来我们来看看没有多任务的程序是什么效果. import time def sing(): for i in range(5): print("正在唱...") time.sleep(1) def dance(): for i in range(5): print("正在跳...")

  • 详解小程序开发经验:多页面数据同步

    导语:本文主要介绍在小程序中,多页面之间如何保持数据同步 在很多的产品中,都会存在跨页面间需要数据同步,如下示例: 为了更好的理解该场景,我们再详细描绘一下: 本场景包括4个页面:动态广场.个人中心.我的动态.动态详情 首先,进入动态广场页,请求加载数据,展示动态列表,其中,我们用绿色内阴影区分该条动态是"我的",其他未加内阴影的表示是"别人的": 然后,进入个人中心页,请求加载数据,展示获赞数量: 点击我的动态,进入我的动态页,请求加载数据,展示我的动态列表: 点

  • 详解小程序不同页面之间通讯的解决方案

    小程序做开发的时候难免需要不同页面之间的通讯,比如首页打开新的页面搜索获取结果返回到首页,不同tab页面之间的数据交互等等.于是做了以下总结 当前页面打开新的页面 打开新的页面可以通过 navigator 组件来实现,通过url传参来实现,例如 <navigator url="../search/search?id=123" open-type="redirect">搜索</navigator> 在新的页面 onLoad 事件可以拿到传过来的

  • 详解小程序input框失焦事件在提交事件前的处理

    无论是小程序还是平常的Html页面,input文本框我们用到的次数都很多,这一篇文章主要是讲关于小程序中文本框聚焦自动拉起输入键盘的一些使用心得. 为什么我需要聚焦拉起键盘呢?为什么我弃用失焦事件呢? 因为在我的小程序文章中提供了评论文章的功能,昨天空闲下来我想将回复评论的功能也做一下,我想让回复和评论同时共用一个文本框如下图一,我来说明一下的操作:当我们进入文章界面的时候,输入框模式默认是评论文章的,但是如果我们点击回复就会出现图二的情况,在文本框出现回复XXX的评论,这里有个过程:当我们点击

  • 详解小程序循环require之坑

    1. 循环require 在JavaScript中,模块之间可能出现相互引用的情况,例如现在有三个模块,他们之间的相互引用关系如下,大致的引用关系可以表示为 A -> B -> C -> A,要完成模块A,它依赖于模块C,但是模块C反过来又依赖于模块A,此时就出现了循环require. // a.js const B = require('./b.js'); console.log('B in A', B); const A = { name: 'A', childName: B.nam

  • 详解小程序退出页面时清除定时器

    由于小程序页面退出时,定时器和长连接等不会自动清除断开,所以需要我们在生命周期函数中手动清除 但是定时器定义在函数中,我们无法在函数外清除,所以为了解决这个问题,我们需要把定时器定义在data中 比如:Loadingtime是一个定时器,在data中定义Loadingtime:''. 然后在time方法中使用 在onUnload监听页面卸载生命周期函数中清除 如此在退出该页面时,就可以清除该定时器了 以上所述是小编给大家介绍的小程序退出页面时清除定时器详解整合,希望对大家有所帮助,如果大家有任何

  • 详解小程序用户登录状态检查与更新实例

    这篇文章主要解决以下问题:用户每次登录小程序(包括第一次使用)及点击小程序的每个页面的时候,我们如何判断他当前的登录状态是否过期?如果过期,如何重新获取用户信息并发送至开发者服务器更新用户信息,以及设置新的用户登录状态? 将这个部分单独作为一篇文章有两个原因: ① wx.getUserInfo(OBJECT) 接口调整,废弃了以前直接获取用户信息的方法: ② 上篇文章授权.登录.session_key.unionId只梳理了登录流程而没有贴实际的代码,所以这篇文章以代码实现为主. 1. 代码逻辑

  • 详解小程序设置缓存并且不覆盖原有数据

    最近在写小程序的小项目,因为是刚上手小程序,这途中遇到了许多问题,所幸在自己的坚持不懈下基本都得到了解决,今天就记录一下怎么设置缓存数据并且不覆盖吧,如果有错误的地方麻烦大家指正,互相学习一下! 这是官方的关于缓存的说明,它会覆盖掉之前的内容.我还是直接上源码吧 这是writecomment.js文件 Page({ /** * 页面的初始数据 */ data: { }, submit: function (event) { var writecomment = event.detail.valu

随机推荐