记录使用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的话。

那么,兄弟,很遗憾的告诉你,你可能需要采用分包的形式了。 哈哈哈哈哈哈啊哈哈哈。

我忍不住笑出声了。因为,分包,又是一个需要小心操作的事。

反正,我没有继续弄下去了。有需要的话,就去百度查资料看文档吧。

猜你喜欢

转载自blog.csdn.net/qq_42991509/article/details/107917359