C端H5或小程序常见问题总结

开发C端时遇到的一些问题与解决方法,都记录下来,希望对你也有帮助。

1.部分机型拍照上传图片旋转问题:

直接从图片后面加参数‘?x-oss-process=image/auto-orient,1/resize,m_fixed’(加上参数之后会自动旋转回来,其他参数可参考阿里OSS官方文档https://help.aliyun.com/document_detail/44691.html?spm=a2c4g.11186623.6.1390.750ac1f6HTe92Q)

2.swiper3D两边留白效果:

(小程序h5均适用),代码如下,需要注意的是必须给swiper父级元素加overflow-x:hidden

.swiper-container {
  height: 100%;
  width: 600px;
  perspective: 1600px;
  overflow: visible;
  img {
    width: 100%;
    height: 720px;
    border-radius: 20px;
  }
}
.swiper-slide {
  padding: 0 24px;
  box-sizing: border-box;
  text-align: center;
  transition: transform 0.5s;
  transform-origin: 50% 50%;
  transform: scale(0.9);
  &.swiper-slide-active {
    transform: scale(1);
  }
}

3.小程序的跳转十层限制,不要一直用navigateTo,这样属于一直前进,如果需要跳转回首页或者其他可以使用reLaunch,逻辑复杂时可自己写顶部导航栏包括返回按钮。

4.保存海报到本地:推荐一波生成小程序海报的插件,非常简单方便

taro-plugin-canvas (Taro环境使用)与wxa-plugin-canvas(普通小程序使用)
tip:如果遇到非常规的情况,需要手写canvas并且宽高尺寸乘2保证清晰度。

5.input光标永远停留在最后:

var el = document.getElementById("IPTname");
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

6.小程序获取用户授权(用户信息/用户定位/用户相册访问权限等)时需要处理拒绝时的情况。生成海报到自己相册的时候如果用户在授权的时候点击了拒绝,如果不兼容拒绝授权则直接生成失败。

7.监听移动端键盘弹出和收起事件。

在ios设备上,操作表单输入时,键盘弹出时会把页面顶起来,键盘收起时并不会把页面恢复成原来的样子,造成页面样式错乱,利用focusout和focusin事件,可以间接地监听到键盘的收起和弹出事件,然后操作页面滚动到想要的位置.

focusout触发时机: 当元素即将失去焦点时,focusout 事件被触发。focusout 事件和 blur 事件之间的主要区别在于后者不会冒泡。

focusin触发时机: 当元素即将接收 focus 事件时,focusin 事件被触发。 这个事件和 focus 事件的主要区别在于后者不会冒泡。

监听方式:

let scrollTop = 0
//键盘收起
document.body.addEventListener('focusout',()=>{
   document.body.scrollTop=document.documentElement.scrollTop=scrollTop
})
//键盘弹出
document.body.addEventListener('focusin',()=>{
   scrollTop = document.body.scrollTop || document.documentElement.scrollTop
})

8.移动端代码返回上一页(window.location.back()失效)

尼玛。。移动端返回上一页有时候用window.location.back()不好使,无解。可以使用window.location.href = document.referrer。

9.单页面应用跳转路由之后再次使用echarts时不展示

首次使用不展示,可以延迟加载,但是当路由跳转回来之后延迟都不管用,那么需要在init Echarts之前动态设置容器宽高,并且清空父元素,因为再次回来的时候之前的echarts实例已经是存在的了,可在调试中查看详情。

10. 视频黑边问题

给video标签(原生video标签)加一个object-fit:fill属性即可解决。要强调的是,千万不要使用Taro的Video标签,很坑,解决了黑边,全屏时又会导致视频变形,不用object:fill的话也能解决黑边,但是全屏时视频的退出全屏样式又会被覆盖。

11. 手机锁屏时倒计时停止

window.addEventListener('visibilitychange',() => {
   if (
     //hidden为锁屏时或页面不展示时,可重新获取时间开始倒计时。
      document.visibilityState =='hidden'
   ) 
})

12.移动端实现省-市-区-街道四级联动

需求是要实现地区四级联动,但是vant的插件只支持三级地区联动,就很尴尬,只能自己写,但是自己写的话UI与交互体验会很差,所以就想办法去和后端对接。其实很简单,写出来只是提醒大家可以这么实现,减少思考与踩坑的时间。三级联动选择完之后会返回地区编码,eg:Array<{code:'110101',name:'东城区'}>,然后去请求后端的接口将地区编码传过去,就能得到其街道的相关信息,然后简单过滤一下,放在Picker选择器中就可以实现。接下来就可以写业务逻辑了。

13.ios内webview中vue项目会出现大块白色遮罩

问题描述:

A页面——>B页面——>ios自带的返回——>白屏出现(像个魔鬼)——>点击或滑动页面——>问题解决

原因是,经过排查,发现在ios 机器上使用webview 开发Vue项目时候,go history (-1),无法将body 的高度拉掉,使得遮住,触发轻点击,方可消除该遮罩

解决的理论:用于最重要的html 容器是container,然而因为设置html、body高度是100%,从而造成了 container 撑开父级,但浏览器默认滚动的scroll 并不是 container,而是body,某些因素,造成返回history 后,无法复原(ios 的锅),为此,将 container(app.vue中包裹的div,也可以是#app) 进行了绝对定位,并让它重新成为 scroll 的对象,从而解决问题。

html, body {

 width: 100%;

 height: 100%;
 min-height: 100vh;

 margin: 0;

 padding: 0;

 position: relative;

}

.container {

 width: 100%;

height: 100vh;
 min-height: 100vh;

 background: #fff;

 overflow: scroll;

 -webkit-overflow-scrolling: touch;

 position: absolute;

 left:0;

 top:0;

}

14.在ios上A页面分享朋友圈之后,跳转到 B 页面(或进行别的操作)

注:离开 app的 A 页面去微信,再次回到 app 的时候跳转到 B 页面

visibilitychange:当用户最小化窗口或者切换选项卡的时候,api 就会发送visibilitychange事件,让开发者知道页面状态已更改。你可以检测事件并执行某些操作或行为。

//这个方法在分享被触发的时候执行
initVisibilityChange() {
      if (typeof document.visibilityState != "undefined") {
        this.visibleChange = "visibilitychange";
        this.vibibleState = "visibilityState";
      } else if (
        typeof (document as any).webkitVisibilityState != "undefined"
      ) {
        this.visibleChange = "webkitvisibilitychange";
        this.vibibleState = "webkitVisibilityState";
      }
      if (this.visibleChange) {
        document.addEventListener(
          this.visibleChange,
          this.onVisibilityChanged,
          false
        );
      }
    },
      /**
     * 从微信返回app可以在这里面处理事情
     */
    onVisibilityChanged(event: any) {
      var hidden = event.target.webkitHidden;
      if ((document as any)[this.vibibleState] == "visible") {
          this.goRun(); // 做自己需要的逻辑处理
          this.removeVisibilityChange();
      }
    },
     // 移除app后台监听事件
    removeVisibilityChange() {
      document.removeEventListener(
        this.visibleChange,
        this.onVisibilityChanged,
        false
      );
    },

15. 从vue页面跳转到非vue页面,点击返回,ios设备上页面数据不刷新。

比如A页面定义了一个变量为true,点击跳转到非vue项目页面(php),点击返回,查看这个变量,在安卓上依然为ture,但是在ios上就变成了false。

解决方法:

window.onpageshow=function(e){
  if(e.persisted) {
    window.location.reload()
  }
};

16. app点击自带返回按钮关闭页面以及禁用返回的踩坑记录

http://note.youdao.com/s/4qUINhV0 (比较多,有道云链接发出来,希望对大家有帮助)

17. wx.chooseImage方法在安卓与ios上取到的base64格式不一样,安卓没有data:image/jpg;base64前缀,需要单独处理

18. 手机内h5点击input无法唤起自带键盘:可以给input添加click事件手动触发focus事件。

19. canvas绘制圆角矩形方法

// x、y、w、h与canvas的rect方法参数相同
//r代表圆角大小,1、2、3、4分别代表上左、上右、下右、下左
function drawRoundRect(ctx, x, y, w, h, r1, r2, r3, r4) {
  ctx.beginPath()
  ctx.moveTo(x + r1, y)
  ctx.arcTo(x + w, y, x + w, y + h, r2)
  ctx.arcTo(x + w, y + h, x, y + h, r3)
  ctx.arcTo(x, y + h, x, y, r4)
  ctx.arcTo(x, y, x + w, y, r1)
  ctx.closePath()
}

20. POST导出文件处理

  • fetch请求方法中,增加类型处理,预设请求传入字段控制,返回为:response.blob();
  • 返回值处理:
const blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"})
    const a = document.createElement("a");
    const filename ={
      exportDetail: '推广人员用户明细',
      exportRecord: '推广人员跟进记录明细'
    }
    a.href = URL.createObjectURL(data)
    a.download = filename[type] // 这里填保存成的文件名
    a.click()
    URL.revokeObjectURL(a.href)
   a.remove();

21. 解决css瀑布流只能竖向展示问题(非排名或排序时可用)

// 父元素
column-count: 2;
width: 100%;
// 子元素
break-inside: avoid;
const oldList = lists;
const newList = [].concat(
  ...Array.from(
    oldList.reduce(
      (total, cur, index) => {
        total[index % 2].push(cur);
        return total;
      },
      { 0: [], 1: [], length: 2 }
    )
  )
);

22. mpvue的坑

  • 一定要遵循mpvue官方文档建议,不要将v-for用在组件上面,或者在v-for内嵌套组件,这样成倍增加wx.setData的数据量

  • 在开发上拉加载更多的需求时不要使用小程序原生的scroll-view,直接使用page的onReachBottom事件

  • 使用swiper时,不要嵌套过多swiper-item,不要在swiper-item中放入太多元素,有瀑布流等无限加载列表需求时,禁止使用swiper,会导致小程序卡死闪退

  • slot只能使用匿名,具名slot及scoped slot不能使用

  • 在定义组件data时,不要将与view无关的数据定义到data中,直接定义到外部,特别时监听scroll事件的时候

  • wx对象的方法全部都挂在mpvue对象下,不需要直接调用wx的方法,特别是在兼容h5的时候

  • button open-type是获取用户手机号等需要绑定事件的类型时,不要使用click.stop或catch:tap,这样会导致不会触发获取用户手机号的回调方法

  • 所有组件的created生命周期只在小程序加载的时候统一执行,即使使用了v-if也不能在每次加载组件的时候触发

  • 如果要自定义tabbar最好自己实现一个,不要使用原生的custom定义,原生custom限制太多且不能使用mpvue实现

23. Taro hooks子组件条件渲染

若是希望子组件props未被重新赋值的情况下不渲染,使用memo包裹,它实现了class中PureComponent的效果,浅比较了一次。

// 子组件
import Taro, { memo } from "@tarojs/taro";
import { View } from "@tarojs/components";
 
const Index = ({ data }) => {
  return (
    <View>
      {console.log("Test -- rander2", data)}
      {data.map((item, index) => {
        return <View key={index}>{item}</View>;
      })}
    </View>
  );
};
 
export default memo(Index);

若是希望在某些特定的prop改变的时候更新子组件

// utils
import Taro from "@tarojs/taro";
 
/**
  * 子组件条件渲染
  * @param {Function} render - 要渲染的组件函数
  * @param {Array}  observe - 渲染依据,从props接收的数据key,
  */
export const compareRender = (render, observe=[]) => {
  const compare = (prevProps, nextProps) => {
    let keep = true;
    for (let i = 0; i < observe.length; i++) {
      if (prevProps[observe[i]] !== nextProps[observe[i]]) {
        keep = false;
        break;
      }
    }
    return keep;
  };
  return Taro.memo(render, compare);
};
// 子组件
import Taro from "@tarojs/taro";
import { View } from "@tarojs/components";
 
import { compareRender } from "@utils";
 
const Index = ({ data }) => {
  return (
    <View>
      {console.log("Test -- rander", data)}
      {data.map((item, index) => {
        return <View key={index}>{item}</View>;
      })}
    </View>
  );
};
 
export default compareRender(Index, ["data"]);

24. 关于在dva副作用中多个请求同步执行的优化

经测试:
const res1 = yield call(req1); const res2 = yield call (req2); const res3 = yield call (req3); 三个请求同步执行,平均耗时大约为120ms,页面加载、数据等待时间较长。
优化:
const [ res1, res2, res3] = yield [ call(req1), call(req2), call(req3) ], 并发请求,平均耗时大约为20ms