美文网首页
taro开发小程序遇到的问题

taro开发小程序遇到的问题

作者: lemonzoey | 来源:发表于2021-06-01 15:29 被阅读0次

    一、技术栈

    taro + react + taro-ui + mobx
    最初因为内嵌有h5页面,h5是vue开发的,故选择了taro的vue版本进行开发。但是后续问了小程序开发经验丰富的同学,他说vue版的容易出问题,建议还是使用react版本的taro开发小程序。我就去网上查了下资料,还真是如此。官方虽然对vue,react语法都支持,但是其状态管理setData的设计思路明显更偏向于react,整理的设计思路更适合react。所以我们的项目最后将语法转换为了react版本的。

    二、遇到的问题

    1.页面报错:一个页面只能允许一个web-view

    image.png

    web-view类似于pc端的iframe,可以将其他站点的内容直接嵌套到本地项目中使用。但是web-view会全屏展开,它不允许和其他内容同时显示。
    最初,我把web-view和主页面的逻辑写一起,当没登录展示主页面逻辑,登录后,跳转到web-view页面

    render(){
      if(login){
        return  <WebView src='https://www.baidu.com' />
      }
      return <View>登录逻辑</View>
    }
    

    这样写页面有些逻辑更改,导致setState变更时候,会导致页面重新渲染,这样页面就报错一个页面只能插入一个web-view。
    于是转换思路,将web-view单独抽离出来写成一个页面,这个问题就迎刃而解啦,如下

    //login.js
     if(this.token){ //如果获取到了token就跳转到web-view页面
          Taro.redirectTo({
                url: `/pages/webView/index?token=${this.token}`
            })
     }
    //webView.js
    render(){
      return  <View>
                    <WebView src='https://www.baidu.com' />
              </View>
      }
    

    2.上线后web-view内容频繁白屏

    本地真机联调,预览和体验版本都没有问题,但是提交审核通过发布后的版本,会频繁白屏。这种本地没法复现的bug,只能通过其他途径解决了。
    第一:先看看出问题的手机型号及微信版本号,在到对应的小程序开发工具上调试到对应版本看看能否复现问题。


    image.png

    第二:看看web-view的src是否的拼接而成。
    若是拼接的,可能会导致部分手机白屏,我之前的src是用 src + token拼接而成的。解决思路如下:

    // store 
    import {observable } from "mobx";
    export class WXLoginClass {   
        @observable src=''
        @observable localSrc=''
        @observable isShowWebView=false
      
        changeSrc(src){
            this.src = src
            this.localSrc = src
            this.isShowWebView = true
        }
        hideWebView(){
            this.src = ''
            this.localSrc = ''
            this.isShowWebView = false
        }
    }
    export default new WXLoginClass();
    
    // webview.js
    import { Component } from 'react'
    import { WebView,View} from '@tarojs/components'
    import { observer,inject } from 'mobx-react'
    @inject('webViewStore')
    @observer
    class Index extends Component {
        myTimeId = null
    
        componentWillMount(){
            // 进页面前先清空数据
            this.props.webViewStore.hideWebView()
        }
    
        onLoad(options){ // 第一次进入这个页面时候 调用,第二次不会调用
            let src = `https://www.baidu.com`
            if(obj.token){ // obj.token是自己获取的token
                src = src + `?token=${obj.token}`
            }
            this.myTimeId = setTimeout(()=>{
                this.props.webViewStore.changeSrc(src)
            },0)        
        }
    
       componentWillUnmount(){ //组件销毁时候清空定时器
           clearTimeout(this.myTimeId)
        }
    
        render () {
            let {src,isShowWebView} = this.props.webViewStore
            return  <View>
                {
                    isShowWebView && 
                    <WebView src={src} onMessage={this.shareTheProject.bind(this)}/>
                }
            </View>
        }
    }
    export default Index
    

    这样就解决了安卓机上web-view内嵌页面白屏的bug。

    3.web-view内部h5页面触发右上角分享逻辑

    因为内嵌页面是H5页面,没法直接触发微信小程序的api,但是小程序里提供了web-view分享的方法。
    h5页面的入口index.html里引入js,router里写逻辑

    //index.html
     <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script>
    
    //h5里面的router.js
    router.beforeEach(async (to, from, next) => {
      wx.miniProgram.postMessage({data:{ url: to.path ,name:to.meta}})
      .....
      next()
    })
    

    web-view页面写分享的逻辑
    当web-view里的页面点击右上角的分享按钮时候,会触发onMessage 里的shareTheProject函数,这个函数能拿到当前转发的页面路径url和当前页面的名称name,然后触发webView.js里的分享逻辑onShareAppMessage,可以在这个函数里写转发的路径,和要传递的参数。若是有转发成功后要处理的逻辑,可以写在success这个回调里。

    // store
    import {observable } from "mobx";
    
    export class WXLoginClass {
        @observable shareObj = {} //分享的对象
        changeShareInfo(e){
            this.shareObj = e.detail.data[e.detail.data.length - 1]
            let url = this.shareObj.url
            // url路径里若是有token要去除
            url = url.includes('?token') ? url.split('?token')[0]:url
            this.shareObj.url = url
        }
    }
    export default new WXLoginClass();
    
    // webview.js
    
    @inject('webViewStore')
    @observer
    class Index extends Component {
       
        shareTheProject(e) {
            this.props.webViewStore.changeShareInfo(e)
        }
    
        onShareAppMessage(options) {   //转发时执行
            return {
                title: '转发的主题',
                path:'/pages/index/index?value=123&share=true',//小程序向h5传递参数
                success(e) {
                    wx.showShareMenu({
                        // 要求小程序返回分享目标信息
                        withShareTicket: true
                    });
                },
            }
        }
       
        render () {
            return  <View>
                    <WebView src='https://www.baidu.com' onMessage={this.shareTheProject.bind(this)}/>
            </View>
        }
    }
    export default Index
    

    4. getStorage首次获取数据不生效

    当你后面的操作必须依赖于storage的数据,这时候就需要使用存储的api。直接使用setStorage,第一次获取数据获取不到,就是因为这个api是异步的。若要每次都能获取到数据,则需要使用同步存储的api setStorageSync,使用方法如下:

    // 异步存储
    Taro.setStorage({
      key:"key",
      data:"value"
    })
    
    // 获取异步存储
    Taro.getStorage({
      key: 'key',
      success: function (res) {
        console.log(res.data)
      }
    })
    
    // 同步存储
    try {
      //同步和异步不同,同步是对象,异步直接是key,value
      Taro.setStorageSync('key', 'value')
    } catch (e) { }
    
    // 获取同步存储
    try {
      var value = Taro.getStorageSync('key')
      if (value) {
        // Do something with return value
      }
    } catch (e) {
      // Do something when catch error
    }
    

    两者的区别:
    异步就是不管保没保存成功,程序都会继续往下执行。同步是保存成功了,才会执行下面的代码.
    使用异步,性能会更好;而使用同步,数据会更安全

    5.小程序-高德地图定位的使用

    微信小程序|高德地图api官网
    第一步:去官网注册获取个key

    第二步:获取地址,按官网提示一步步来操作即可。


    获取地址
    
    class Index extends Component {
        state = {
            gaodeMapAds: '', // 高德地图的位置
        }
      
        componentWillMount(){
            // 自动获取定位
            this.onLoadAddress()
        }
       
         //高德地图获取 地理位置 
        onLoadAddress() {
            let that = this;
          //ADDRESS_KEY 是申请的高德地图的key
            let myAmapFun = new amapFile.AMapWX({key:ADDRESS_KEY});
            myAmapFun.getRegeo({
                success: function(data){  //成功回调
                    let res = data[0] && data[0].regeocodeData.addressComponent
                    res.formatted_address = data[0] && data[0].regeocodeData.formatted_address
                    let {province,city,district} = res
                    if(typeof city === 'object'){
                        city = province.substr(0,2) + '城区'
                    }
                    that.setState({
                        gaodeMapAds:`${province} ${city} ${district}`
                    })
                },
                fail: function(info){
                    // wx.onLocationChange
                    //失败回调
                    console.log(info)
                }
            })
        }
        
        render () {
            const {gaodeMapAds} = this.state
            return (
                <View className='index'>
                   <AtInput      
                        editable={false}
                        title='地理位置:'
                        value={gaodeMapAds}
                        > </AtInput> 
                </View>
            )
        }
    }
    
    export default Index
    
    

    调取获取定位的接口返回的结果如下:


    image.png

    如要获取省市区的code,直接从regeocode.addressComponent.adcode字段获取。

    // 省份的code
       provinceCode = parseInt(adcode / 1000) * 1000 + ''
    // 城市的code
       citysCode = parseInt(adcode / 100) * 100 + ''
    //区域的code
       areasCode = adcode
    

    6.picker省市区位置选择器

    微信小程序提供了省市区选择器picker,只需要设置picker的mode属性为'region',省市区的内容就会自动填充。

     <picker mode="region" bindchange="bindRegionChange" value="{{region}}" custom-item="{{customItem}}">
        <view class="picker">
          当前选择:{{region[0]}},{{region[1]}},{{region[2]}}
        </view>
      </picker>
    
    picker

    具体使用说明可参考微信小程序picker使用说明

    若对省市区没有特别要求,直接用微信提供的api即可。但是我们对省市区有特殊要求,需从接口获取省市区数据,因此这个mode='region' 属性不可用,需使用mode='multiSelector'属性。
    这里需要注意的是,我们限制了用户必须省市区都要选中才行,因为若不满足条件就给出提示,但是因为picker点击确定就会关闭弹框没有阻止弹框关闭的api,所以这里一定要记录你上次选中的数据,在数据不满足条件弹框关闭时候,根据上次记录的数据恢复省市区的数据和上次选中数据的坐标,确保用户下次再打开弹框时候,选中的是上次的数据。

    // store.js
    import Taro from "@tarojs/taro";
    import { observable } from "mobx";
    import { WXRequest } from '../pages/api/request';
    // 获取省市区接口的封装
    import {getProvinces, getCities, getDistrict} from '../pages/api/buss'
    
    export class WXLoginClass {
        @observable province = ''
        @observable city = ''
        @observable area = ''
        @observable provinces =[]
        @observable citys = []
        @observable areas = []
        @observable value = [0, 0, 0]   
        // 三级地址
        @observable.shallow ranges = [[], [], []]
        nextSureVal = [0, 0, 0]
    
        async addressChange() {
            let newarr = [],newNameArr =[]
            let res = await getProvinces();
            if (res.code == 200) {
                for (let i = 0; i < res.data.length; i++) {
                    const resItem = res.data[i]
                    const item = {
                        name: resItem.name,
                        value: resItem.citycode,
                    }
                    newarr.push(item)
                    newNameArr.push(resItem.name)
                }
                this.ranges[0] = [...newNameArr]
                this.provinces = [...newarr]
                const cityCode = res.data[0] && res.data[0].citycode
                if(!cityCode) {
                    this.citys = []
                    this.ranges[1] = []
                    this.areas = []
                    this.ranges[2] = []
                    return 
                }
                await this.getCities(cityCode)
            }
        }
    
        async getCities(id){
            if(!id) {
                this.ranges[1] = []
                return
            }
            let res = await getCities(id)
            let newarr = [],newNameArr =[]
            this.citys = []
            this.ranges[1] = []
            if (res.code == 200) {
                for (let i = 0; i < res.data.length; i++) {
                    const resItem = res.data[i]
                    const item = {
                        name: resItem.name,
                        value: resItem.citycode,
                    }
                    newarr.push(item)
                    newNameArr.push(resItem.name)
                }
                this.ranges[1] = ['请选择',...newNameArr]
                this.citys = [option,...newarr]
            }
        }
    
        async getDistrict(id){
            if(!id) {
                this.ranges[2] = []
                return
            }
            let res = await getDistrict(id)
            let newarr = [],newNameArr =[]
            this.areas = []
            this.ranges[2] = []
            if (res.code == 200) {
                for (let i = 0; i < res.data.length; i++) {
                    const resItem = res.data[i]
                    const item = {
                        name: resItem.name,
                        value: resItem.citycode,
                    }
                    newarr.push(item)
                    newNameArr.push(resItem.name)
                }
                this.ranges[2] = ['请选择',...newNameArr]
                this.areas = [option,...newarr]
            }
        }
    
        async columnChange(e) {
            const { provinces, citys, value } = this
            const column = e.detail.column
            const evalue = e.detail.value
            let provinceNum = value[0]
            let cityNum = value[1]
            let countyNum = value[2]
            // 记录上次的位置
            if (column == 0) { //选则了省份
                provinceNum = evalue
                const id = provinces[provinceNum].value
                await this.getCities(id)
                this.areas = []
                this.ranges[2] = []
                this.value = [provinceNum, 0, 0]
            } else if (column == 1) { //选则了城市
                cityNum = evalue
                const id = citys[cityNum].value
                this.getDistrict(id)
                this.value = [provinceNum, cityNum, 0]
            } else {
                // 滑动选择了区
                countyNum = evalue
                this.value = [provinceNum, cityNum, countyNum]
            }
        }
    
        // 不合乎规范或者取消时候,让rang和value的值都复原到上一次的状态
        async handlePickerHide(){
            const {provinces, citys, areas} = this
            this.value = this.nextSureVal
            const idCity =  provinces[this.value[0]].value 
            const eareCity =  citys[this.value[1]].value 
            idCity ? await this.getCities(idCity) : null
            eareCity ? await this.getDistrict(eareCity) : null
        }
    
        //  params true代表传递地址,false不传递
        async handlePickerShow(params: boolean) {
            const changeVal = params.detail.value
            const { provinces, citys, areas, value } = this
            if (params) {
                const p = provinces[value[0]] && provinces[value[0]].name
                const c = citys[value[1]] && citys[value[1]].name
                if(c==='请选择') { //city必须有
                    Taro.showToast({
                        title:'城市必须选择',
                        duration:2000, //   持续时间
                        icon:'none',//  'success'、'loading'、'none'
                        mask:false,  //是否显示透明蒙层,防止触摸穿透
                    })
                    this.value = this.nextSureVal
                    const idCity = this.provinces[this.value[0]].value 
                    if(idCity){
                        await this.getCities(idCity)
                    }
                    const eareCity = this.citys[this.value[1]].value
                    if(eareCity){
                        await this.getDistrict(eareCity)
                    }
                    return 
                   
                }
                let a = areas[value[2]] && areas[value[2]].name
                if(!a || a === '请选择') {
                    Taro.showToast({
                        title:'区域必须选择',
                        duration:2000, //   持续时间
                        icon:'none',//  'success'、'loading'、'none'
                        mask:false,  //是否显示透明蒙层,防止触摸穿透
                    })
                    this.value = this.nextSureVal
                    const eareCity = this.citys[this.value[1]].value 
                    if(eareCity){
                        this.getDistrict(eareCity)
                    }
                    return 
                }
                this.isAmapAddress = false //用户手动选择地址
                this.province = p || ''
                this.city = c || ''
                this.area = a || ''
                this.nextSureVal = this.value
            }
        }
    }
    export default new WXLoginClass();
    
    // picker.js
    @inject('Login')
    @observer
    class MyPicker extends Component {
      componentWillMount(){
          // 省份信息可以异步就获取
         this.props.Login.addressChange()
      }
        // 位置滑动触发
        columnChange(e) {
           this.props.Login.columnChange(e)
        }
           
        //  点击确定时候触发 params true代表传递地址,false不传递
        handlePickerShow(params: boolean) {
            this.props.Login.handlePickerShow(params)
        }
    
        handlePickerHide(){
            this.props.Login.handlePickerHide()
        }
    
        render() {
            let {ranges,value} = this.props
            ranges = ranges.slice()
            value = value.slice()
            return (
                <View className='mypicker' >
                    <picker 
                        mode='multiSelector' 
                        range={ranges} 
                        value = {value}
                        onChange={this.handlePickerShow.bind(this)} 
                        onColumnChange={this.columnChange.bind(this)}
                        onCancel = {this.handlePickerHide.bind(this)}
                    >
                        <View>
                            <AtIcon value='add-circle' size='20'></AtIcon>
                        </View>
                    </picker>
                </View>
            )
        }
    }
    export default MyPicker
    
    

    相关文章

      网友评论

          本文标题:taro开发小程序遇到的问题

          本文链接:https://www.haomeiwen.com/subject/dyswsltx.html