记录使用mpvue开发小程序常常遇到的坑
记录使用mpvue开发小程序常常遇到的坑 1、保存全局状态的问题 我们在vue项目中,一般都会使用vuex。在react中,一般都会使用redux。那在小程序中呢? 答案是,还是可以使用vuex。 mupve初始化项目后,有一个页面模板就是使用了vuex。 但是,我一般来说,都是需要全局的store,而不是局部页面的。那如何配置解决呢? 新建一个store后,然后在主入口,main.js中来关联配置。 import Vue from 'vue' import App from './App' // api请求接口 import { userLogin, wxloginForBind, userLogout} from '@/api/user' // 引入store import store from './store/index' // 把store挂载到全局 Vue.prototype.$store = store Vue.config.productionTip = false App.mpType = 'app' const app = new Vue(App) app.$mount() /store/index.js内文件长这样: import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: { sysLogined: false, token: '' }, mutations: { SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: (state, name) => { state.name = name }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }, }, actions: { // 微信登录 loginByWx ({commit}, userData) { return new Promise((resolve, reject) => { wxloginForBind(userData).then(res => { const { data } = res commit('SET_TOKEN', data.token) commit('SET_NAME', data.username) commit('SET_AVATAR', data.iconUrl) commit('UPDATE_LOGIN', true) // 保存本地 wx.setStorageSync('sysLogined', true) wx.setStorageSync('token', data.deviceToken) wx.setStorageSync('name', data.displayName) wx.setStorageSync('avatar', data.iconUrl) resolve() }).catch(error => { reject(error) }) }) }, // 用户主动退出,需要请求后台退出接口 logout ({ commit, state }) { return new Promise((resolve, reject) => { userLogout({deviceToken: state.token}).then(() => { commit('SET_TOKEN', '') commit('SET_NAME', '') commit('SET_AVATAR', '') commit('UPDATE_LOGIN', false) // 移除本都存储 wx.setStorageSync('sysLogined', false) wx.removeStorageSync('token') wx.removeStorageSync('name') wx.removeStorageSync('avatar') resolve() }).catch(err => { reject(err) }) resolve() }) }, // 移除 token resetToken ({ commit }) { return new Promise((resolve) => { commit('SET_TOKEN', '') commit('SET_NAME', '') commit('SET_AVATAR', '') commit('UPDATE_LOGIN', false) // 移除本都存储 wx.setStorageSync('sysLogined', false) wx.removeStorageSync('token') wx.removeStorageSync('name') wx.removeStorageSync('avatar') resolve() }) } }) export default store 必须注意的是,vuex中保存的数据,需要同步保存到微信的本地存储中。不然,退出去之类的,状态可能就没了。 然后在app.vue中,入口的地方,需要做判断,把本地保存的数据,在小程序加载的时候,重新写入到vuex中去。保持vuex中的数据是对的。 <script> import { defaultCity } from '@/utils/config.js' export default { created () { // 调用API从本地缓存中获取数据 /* * 平台 api 差异的处理方式: api 方法统一挂载到 mpvue 名称空间, 平台判断通过 mpvuePlatform 特征字符串 * 微信:mpvue === wx, mpvuePlatform === 'wx' * 头条:mpvue === tt, mpvuePlatform === 'tt' * 百度:mpvue === swan, mpvuePlatform === 'swan' * 支付宝(蚂蚁):mpvue === my, mpvuePlatform === 'my' */ }, mounted () {}, onLaunch () { this.initData() }, methods: { initData () { if (wx.getStorageSync('sysLogined')) { // 登录状态 let token = wx.getStorageSync('token') let name = wx.getStorageSync('name') ? wx.getStorageSync('name') : '' let role = wx.getStorageSync('role') ? wx.getStorageSync('role') : '' let avatar = wx.getStorageSync('avatar') ? wx.getStorageSync('avatar') : '' this.$store.commit('UPDATE_LOGIN', true) this.$store.commit('SET_TOKEN', token) this.$store.commit('SET_NAME', name) this.$store.commit('SET_AVATAR', avatar) } }, } } </script> <style> /* 此处定义的是全局公共样式 */ </style> 2、引入第三方小程序UI框架的问题。 不能像官方文档说的那样去安装引入,官方文档适应的是原生开发,不是mpvue 那如何使用呢?把官方的github拉取下来,拿到dist目录下的打包好了的文件。全部拷贝到我们项目的static目录下。当做静态资源引入。我把vant-ui的这个包名,该为了weapp目录 然后在需要使用的页面的main.json中,引入一下。 { "navigationBarTitleText": "记录记录", "usingComponents": { "van-button": "/static/weapp/button/index", "van-search": "/static/weapp/search/index", "van-tabs": "/static/weapp/tabs/index", "van-tab": "/static/weapp/tab/index", "van-dialog": "/static/weapp/dialog/index", "van-sticky": "/static/weapp/sticky/index" } } 然后的对于的.vue文件中,直接使用vant-button这样的标签。 有时候需要修改vant的样式怎么办? 直接在外面套一层自定义类名,然后相当于加了权重,就会覆盖掉默认的样式了。 类似如下: .resetpsd-wrap是我自定义的类目。 <style lang="less"> .resetpsd-wrap { .van-cell { padding-top: 48rpx; padding-bottom: 32rpx; } } </style> 3、封装请求的问题 小程序本身是有自己的调用服务器的api请求方法的。但是,使用过了axios后,再也不想回到写回调函数的时候了。 怎么办? 小程序本身是不能继续使用axios了。好消息是,可以使用flyio.js这个库,本身非常小,语法和axios,简直一模一样。 我使用flyio.js 封装的请求方法: /** * 网络请求封装 * fly.js文档参考: https://github.com/wendux/fly */ import store from '@/store' import { serverUrl, apikey, secret, AppId } from './config' import MD5 from 'js-md5' const Fly = require('flyio/dist/npm/wx') const service = new Fly() service.config.timeout = 60000 service.config.baseURL = serverUrl service.interceptors.request.use( config => { // wx.showLoading({ // title: '加载中', // mask: true // }) // 自定义请求头。我这里添加了token,你还可以设置其他的公司需要的请求头。 config.headers['token'] = store.state.token return config }, error => { console.log(error) Promise.reject(error) } ) service.interceptors.response.use( response => { // wx.hideLoading() // 这里,根据后台接口的实际情况来写。这里,返回的都是状态码200的数据。 // 返回结果成功代码为0001,只将请求结果的data字段返回 if (response.data.code === '0001') { return response.data } if (response.data.code === '0004') { // token失效 let token = wx.getStorageSync('token') if (token) { // 已登录状态下token失效让用户重新登录 // wx.setStorageSync('sysLogined', false) // wx.removeStorageSync('token') // wx.removeStorageSync('userId') store.dispatch('resetToken').then(() => { wx.showToast({ title: '当前登录失效,请重新登录', icon: 'none' }) }).catch(() => {}) } return response } else { wx.hideLoading() wx.showToast({ title: response.data.message || '请求出错', icon: 'none', duration: 3000 }) return Promise.reject(response) } }, error => { wx.hideLoading() console.log('err' + error) wx.showToast({ title: '网络错误', icon: 'none', duration: 3000 }) return Promise.reject(error) } ) export function getRequest (url, param) { return service.get(url, param || {}) } /** * * @param {*} url 请求接口 * @param {*} param 请求参数 * @param {*} contentType 请求类型,默认application/json, 也可以传入application/x-www-form-urlencoded */ export function postRequest (url, param, contentType = 'application/json') { return service.post(url, param || {}, {headers: { 'content-type': contentType }}) } export default service 4、小程序中写css的单位问题(px rpx的问题) 小程序,本身有一个rpx单位,当然,也可以使用px单位。 rpx单位,小程序是做了适配的。基本上,都会使用这个做布局单位。 但是,但是,mpvue,默认就会把px单位,转换为rpx单位。 有时候,有些字体大小之类的,我不想转,怎么办? 答案是 写成PX,就不会转了。写什么忽视之类的,不好意思,我没有测试成功过。有可以的,麻烦告诉我一身。 vant-ui, 他的默认但是,其实还是px。布局也还是px单位。 如何把他也改成rpx单位呢? 请参考这篇文章配置一下就可以了:https://blog.csdn.net/qq_36710522/article/details/99409054 设置后不生效,重新打包,把dist目录下的内容删掉,重新开微信开发者工具。 5、vant-filed, 小程序input 表单输入的问题 这种项目,表单输入,肯定会有的。怎么搞? 不要在mpvue中使用v-model,双向数据绑定。光标闪的问题。 没有双向绑定?还写不了代码了吗? <van-field :value="username" input-align="right" clearable placeholder="请输入姓名" @change="onChangeName"> <text slot="label" class="c-grey">客户姓名</text> </van-field> :value="username"使用这种方式来替换v-model; 绑定@change事件,然后,在e事件对象中,通过e.mp.dettail拿到改后的值,重新赋值给data中的数据就行了。 onChangeName (e) { this.username = e.mp.detail }, 注意,如果表单,使用的是小程序的原生input这种。就需要注意了,不要监听@change,虽然模拟器是正常的,真机是有bug的哦。怎么办? 直接搞小程序@input原生事件。 <input :value="tel" class="input-a" placeholder=" " type="number" maxlength="4" @input="onChangeTel"/> onChangeTel (e) { this.tel = e.mp.detail.value }, 5、小程序背景图的问题 小程序背景图片,是不支持本地图片的,只能放到服务器上的url地址。如果一定要搞本地背景图,只有以下几种方法: A: 把图片转换为base64格式的字符串。百度搜索网站,上传图片上去,拿到base64格式字符串。 然后写样式的时候,就是这样了。 .mine-login-item { height: 247rpx; width: 750rpx; background-image: url(“data:image/png;base64,DAIF…很长的网上转换后的字符串”); } B: 换方式,把背景图的位置,换成 标签,当做背景图。然后里面的内容,就只能靠浮动之后,覆盖在上面。改z-index什么的,把内容显示到图片上面。 6、wx.hideLoading()与wx.showToast() 不处理好,就有bug 如果先wx.showToast() 再 wx.hideLoading(),可能导致showToast的时间,很短,如论设置一万年,都不行,因为hideLoading把它关了。 所有,先hideLoading() 后,再调用showToast() 这个问题,哪怕模拟器没有问题,真机上也还是有问题。真的被坑过。 7、小程序一个包大小限制2M,超过了,打包都不然上传预览测试的。 怎么办啊怎么办? 第一步,你可以先安装一个webpack-bundle-analyzer这个包,查一下各个包的大小。看是不是有很大的。 一般来说,如果你没有做什么不可描述的事情的话,不会很大的。 我就做了不可描述的事情,导致包超大。 具体什么事情呢?正常,密码加密都是MD5,是很小的。但是,我们的后台,密码加密,需要的是 AES这种加密方式。 这个不好解决么? CryptoJS不就是可以干这个么? 一顿操作猛如虎,接口通了。很满足,很拽,很牛逼。 一打包,我操,什么鬼?那么大?120kb的压缩js包,一下子变成了800+kb。 一下子就不知道咋办了。 最后的办法就是,把AES 加密的方式,从CryptoJS拷出来,不要CryptoJS了。 如果,不上引入了第三方,太大的js包的话,还有一种。 就是static目录放的静态资源图片文件太大了。 我们的UI给我们搞了几个200-300kb的背景图。 总共2M的空间,图片占了一大半? 把图片,压缩。百度搜索在线压缩图片的网站。把图片上传上去,压缩。把几个比较大的,压缩一下。 基本上,每个图片,能够减少70%的大小。 最后,一顿操作后。就只有1.5M的包的大小了。 如果上面的操作,还是不能把包减小到2M的话。 那么,兄弟,很遗憾的告诉你,你可能需要采用分包的形式了。 哈哈哈哈哈哈啊哈哈哈。 我忍不住笑出声了。因为,分包,又是一个需要小心操作的事。 反正,我没有继续弄下去了。有需要的话,就去百度查资料看文档吧。 8、小程序原生textarea 组件,在部分安卓手机上会穿透显示在弹窗组件。 原因是,原生的textarea input组件的曾经,会高于我们自己写的弹窗组件。我们自己写的弹窗组件,本质还是view, 无论你怎么设置 z-index 也是徒劳的。 解决办法是。写一个和textarea一样的view。样式也差不多。看起来是一个真的textarea。在弹窗显示时,隐藏真的textarea,显示假的。关闭时,把真的显示出来,把假的隐藏。 9、修改自定义原生的botton 样式。 <button class="bot-btn" >按钮</button> .bot-btn { color: #fff; border-radius: 8rpx; background:rgba(43,202,217,1); font-size: 28rpx; height: 84rpx; line-height: 84rpx; } .bot-btn::after { border-radius: 8rpx; border: 0; } 通过.bot-btn::after来清楚掉默认的边框。 10、在下拉菜单、弹窗等组件打开时,底部的内容居然还可以滑动。 本质,应该是很好解决的。但是在mpvue中,好坑。 网上有很多方法,大概有以下几种 1、@touchmove.stop.prevent=“moveHandle” 2、catchtouchmove=“ture” 3、直接把根标签,固定定位。 我尝试了很多次,最后的解决方案: 1、在顶部的搜索栏,使用@touchmove.stop.prevent=“moveHandle”,来阻止滑动。moveHandle是一个空函数 moveHandle () {} 1 2、在弹窗打开时,添加一个样式,把跟标签固定定位。关闭时,移除这个标签。 <template> <view class="chooseBuilt" :class="{ unscoll: showMask }"> </view> </template> showMask 为true时,添加unscoll样式在根标签上。 .unscoll { position: fixed; top: 0; height: 100%; overflow: hidden; } 某些情况下,真机,会出现显示问题。在你和vant-weapp 中的下拉菜单之类的组件使用的时候,会发现根标签的宽度,变窄导致。 如果出现这样的问题 .unscoll { position: fixed; top: 0; height: 100%; overflow: hidden; width:100vw; } 添加一个width:100vw;就可以了。 如果,你的页面,还有下来加载更多操作。 就需要在下来函数做个判断。弹窗打开了,就不执行下来加载更多 onPullDownRefresh: function () { if (this.showMask) { wx.stopPullDownRefresh return false } wx.showToast({ title: '加载中..', icon: 'loading' }) this.fetchlist() wx.stopPullDownRefresh() }, 当然,这个方案的副作用,就是关闭的时候,列表会回到顶部。我没有继续修复这个问题。如果要保留原来的高度。通过js来控制,记录前后的top值,最后加回去。 ———————————————— 版权声明:本文为CSDN博主「一键写代码」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_42991509/article/details/107917359