uniapp技巧进阶-01

116 阅读1分钟

一、跨页面方法调用

需求:在当前页面调用上一个页面的方法。 例如:我要实现一个搜索功能,当我点进搜索详情输入关键词后,我要返回列表页面并触发一次搜索方法,如下图:

image.png

searchFunc(){
  let pages = getCurrentPages()
  let page = pages[pages.length - 2]
  page.$vm.searchText = this.search.text
  page.$vm.refresh()
  this.router.back()
}

getCurrentPages() 是一个全局函数,用于获取当前页面栈的实例,以数组形式按栈的顺序给出。当前页面就是数组最后一项,那么上一个页面就是pages[pages.length - 2]。记得加上$vm属性,因为在uniapp中数据和方法是挂载这个实例上的。我们可以通过这个示例访问到对应页面的所有数据和方法!

二、ios固定输入框字体移动bug

问题描述

在一个固定于页面中间的滚动容器内放了一个表单,在安卓端测试功能完好,在IOS端有一个bug。当输入框在输入后没有点击其他位置使输入框失焦的话,如果滚动窗口内部的字体也会跟着滚动下面使解决方法

//原本的输入框
<input></input>

解决方法

使用这个方法更改后,不仅布局的样式不会改变,而且字体随着固定的滚动窗口一起滚动的bug也会解决

// 更改后的输入框
<textarea fixed="true" auto-height="true" ></textarea>

三、真机时间错误BUG

问题描述

在小程序中使用new Date().toLocaleDateString() api获取时间的时候,在开发工具中显示为当前时间,而在真机中显示为其他地区的时间

BUG产生原因

toLocaleDateString()方法依赖于底层操作系统在格式化日期上。 例如,在美国,月份出现在日期(06/22/2018)之前,而在印度,日期出现在月份(22/06/2018)之前。

解决方案

使用new Date()构造函数来获取年月日后拼接

如果没有输入任何参数,则Date的构造器会依据系统设置的当前时间来创建一个Date对象。

DatetoLocaleDateString()的区别在于一个是获取系统当前设置的时间,一个则是底层操作系统来格式化时间

//具体代码如下
let date = new Date()
date = date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate()
date = date.split('/')
if (date[1] < 10) {
		date[1] = '0' + date[1]
	}
if (date[2] < 10) {
	date[2] = '0' + date[2]
	}
date = date.join('-')

四、跨域的问题

uni-app官方介绍了一些解决跨域问题的方法,比如服务端开启CORS,给浏览器安装跨域插件等,详见uni-app的H5版使用注意事项

更方便的解决方案

根据官方文档的描述,devServer配置被要求在manifest.json去配置,并且由于这个配置文件是json格式的,所以只能对简单类型进行配置。但对于proxy这项配置来说也是足够了的。直接如下方式配置即可解决:

// manifest.json
{
    "h5": {
        "devServer": {
            "proxy": {
                "/prefix/api/user/list": {
                    "target": "https://api-remote.xxxx.com",
                    "pathRewrite": {
                        "^/prefix": ""
                    }
                }
            }
        }
    }
}

另一种解决方案

直接创建一个vue.config.js文件,并在里面配置devServer,直接上代码

// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      '/prefix/api/user/list': {
        target: 'https://api-remote.xxxx.com',
        pathRewrite: {
          '^/prefix': ''
        }
      }
    },
  }
}

这种办法的好处显而易见,用js而非json去配置会更加的灵活,需要注意的是以上两种方案不能同时使用,第一种会覆盖第二种方案。

五、 static 目录的条件编译

在不同平台,引用的静态资源可能也存在差异,通过 static 的的条件编译可以解决此问题,static 目录下新建不同平台的专有目录(目录名称同 %PLATFORM% 值域,但字母均为小写),专有目录下的静态资源只有在特定平台才会编译进去。

如以下目录结构,a.png 只有在微信小程序平台才会编译进去,b.png 在所有平台都会被编译。

┌─static                
│  ├─mp-weixin
│  │  └─a.png     
│  └─b.png
├─main.js        
├─App.vue      
├─manifest.json 
└─pages.json 

六、 flex布局

尽量使用flex布局,因为全平台都支持

七、 尺寸单位

  1. uniapp通用单位 px , rpx
  2. vue页面中支持 rem 、vhvw
  3. nvue 不支持 百分比单位

八、 css变量

CSS 变量描述App小程序H5
--status-bar-height系统状态栏高度系统状态栏高度 (opens new window)、nvue 注意见下25px0
--window-top内容区域距离顶部的距离00NavigationBar 的高度
--window-bottom内容区域距离底部的距离00TabBar 的高度

九、 背景图片

  • 支持 base64 格式图片。

  • 支持网络路径图片。

  • 小程序不支持在 css 中使用本地文件。需以 base64 方式方可使用。

  • 使用本地路径背景图片需注意:

    1. 为方便开发者,在背景图片小于 40kb 时,uni-app 编译到不支持本地背景图的平台时,会自动将其转化为 base64 格式;
    2. 本地背景图片的引用路径推荐使用以 ~@ 开头的绝对路径。
    .test-img {
    	background-image: url('~@/static/logo.png');
    }
    

十、静默授权

小程序目前已有官方规定明确禁止新用户初次打开小程序就唤起授权登录获取用户信息的弹框。

什么是静默授权呢,就是新用户进入你的小程序后,他可以无障碍的访问你的小程序,当他需要分享、进详情页、交互动作、点赞、购买...需要深一层次体验我们的小程序的时候,这个时候唤起授权弹窗让用户授权之后再继续下一步是合理的。

要调取用户的授权登录,必须调用微信自带的button控件才行,包括后面的分享功能模块也一样。必须要用到它里面自带的open-type属性。

1.引用微信自带的button当然会有很多丑陋的问题,比如样式,所以这里还是要重置下样式

<template>
    <view class="user-info-container">
        <button class="user-info-btn" open-type="getUserInfo" @getuserinfo="mpGetUserInfo" withCredentials="true">
            <slot></slot>
        </button>
    </view>
</template>

<script>
import {
    AppModel
} from '@/models/app.js'
const appModel = new AppModel();
export default {
    name: 'userInfoBtn',
    data() {
        return {
            userInfo: {}
        }
    },
    methods: {
        mpGetUserInfo(result) {
            //根据调用系统自带button携带的result信息判断是否有授权信息
            if (result.detail.errMsg !== 'getUserInfo:ok') {
                wx.showToast({
                    title: '取消授权',
                    icon: 'none',
                    duration: 2000
                })
                //hasUserInfo为存储用户是否授权信息
                uni.setStorageSync('hasUserInfo', false)
                return;
            }
            wx.checkSession({
                success() {
                    console.log('有效');
                    //因为用户授权后需求获取用户授权信息,所以这里必传encryptedData、iv两个参数获取用户头像和昵称信息
                    appModel.login({
                        code: uni.getStorageSync('wxCode'),
                        encryptedData: result.detail.encryptedData,
                        iv: result.detail.iv
                    }).then(response => {
                        this.$store.dispatch('setUserInfo', response.data)
                        uni.setStorageSync('AuthTokens', response.data.token)
                        uni.setStorageSync('hasUserInfo', true)
                        this.$emit('onClickBtn')
                        this.userInfo = result.detail.userInfo
                        uni.login({
                            success(response) {
                                uni.setStorageSync('wxCode', response.code);
                            }
                        })
                    })
                },
                fail() {
                    console.log('失效');
                    uni.login({
                        success: function(res) {
                            if (res.code) {
                                uni.setStorageSync('wxCode', res.code);
                                const params = {
                                    code: res.code
                                }
                                appModel.login({
                                    code: uni.getStorageSync('wxCode'),
                                    encryptedData: result.detail.encryptedData,
                                    iv: result.detail.iv
                                }).then(response => {
                                    uni.login({
                                        success(response) {
                                            uni.setStorageSync('wxCode', response.code);
                                        }
                                    })
                                    this.$store.dispatch('setUserInfo', response.data)
                                    uni.setStorageSync('AuthTokens', response.data.token)
                                    uni.setStorageSync('hasUserInfo', true)
                                    this.$emit('onClickBtn')
                                    this.userInfo = result.detail.userInfo
                                })
                            } else {
                                console.log('获取用户登录态失败!' + res.errMsg);
                            }
                        },
                        fail: function() {
                            uni.showToast({
                                title: '微信登录失败',
                                icon: 'none'
                            })
                        }
                    });
                }
            })
        },
    }
}
</script>

// 样式重置,这里的样式坑是必踩的
<style lang="less" scoped>
    .user-info-btn::after {
        border: 0;
    }
    
    .user-info-btn {
        background-color: transparent;
        line-height: 0;
        padding: 0;
        font-size: 0;
    }
</style>

2.引用上面的封装授权按钮

<header>
	<view class="view-header_root" v-if="hasAuthorUrserInfo">
		<view>
			<image class="avatar-img" :src="userInfo.avatarUrl | formatAvatarUrl"></image>
			<image class="avatar-img2" :src="starUrl" v-if="userInfo.starId > 0"></image>
		</view>
		<text>{{userInfo.userName | formatUserName}}</text>
	</view>
	<view class="view-header_root" v-else>
		<userInfoBtn @onClickBtn="onGetAuthData" >
			<section class="un-auth">
				<view>
					<image class="avatar-img" :src="userInfo.avatarUrl | formatAvatarUrl"></image>
					<image class="avatar-img2" :src="starUrl" v-if="userInfo.starId > 0"></image>
				</view>
				<text>uni-小程序</text>
			</section>
		</userInfoBtn>
	</view>
</header>

methods: {
    onGetAuthData() {
    	//授权成功后的回调事件
    	console.log('success')
    }
}

vue项目中引用第三方UI框架时,肯定会涉及到修改样式的问题。由于我们在Vue文件中加入了scoped属性,就是为了防止页面之间的样式污染,所以你想要修改样式的话,有两种方式,第一种是新起一个style不加scoped,然后严格遵循BEM命名模式修改样式,尽量避免样式污染其它页面。第二种就是引用css/deep/,如

<style lang="less" scoped>
/deep/ .user-info-btn {
    background-color: red;
}
</style>