我们再使用mpvue开发小程序会遇到许多困难,下面就一些困难做一个关于mpvue开发小程序的难点重点总结
原生的小程序我本人并没有学习过,更别提拿来开发一款商用的小程序了,刚好还在前公司时,当时的前端团队在提到小程序的解决方案时有分享了mpvue,到了新公司之后技术老大也有提到mpvue,而我本人过去一年多也一直在写vue,对vue写法比较熟悉,而且新公司团队对小程序期待已久,希望尽快上架,所以选择mpvue来开发也是最快最合理的了!
看了mpvue的官方文档,项目的搭建自然也选择了官方推荐的 vue-cli , 在看了五分钟上手教程后,使用命令
vue init mpvue/mpvue-quickstart my-project生成了基本的项目,在后来的开发中,项目的配置基本没做改动,只是添加了less-loader。
基本上是vue-cli生成的目录结构,加了部分文件夹,主要是与后台进行数据交互所使用的框架flyio的配置文件夹(api文件夹),以及整个项目数据管理所使用的vuex(store文件夹),整体目录结构如下:
project └───build └───config └───dist └───node_modules └───src └───api | ajax.js // flyio请求与响应拦截器的配置文件 | config.js // 请求的配置文件 | index.js // 生成请求api实例文件 | Server.js // 项目的数据请求统一管理文件 └───components └───pages └───store └───modules // vuex模块文件夹 | index.js // vuex处理文件 | App.vue | config.js | main.js └───static └───images └───lib └───weui │ README.md │ package.json │ package-lock.json 复制代码
相信很多使用过mpvue的同学都或多或少猜到了一些坑,我也是踩到了不少的坑浪费了不少的宝贵时间,虽然网上关于mpvue的踩坑的文章一搜一箩筐,但我还是要写一下。。。下面就是我在本次小程序开发过程中遇到的坑(们)以及针对它们的解决方案:
### tabBar图标问题 复制代码
在 配置小程序原生的底部tabBar 时,遇到了第一个问题:在将设计师给我的图标icon路径设置正确的情况下, 开发者工具上的tabBar的图标总是会很大,而且几乎占满了整个高度 ,相当难看,搜了很多博客都没有找到解决办法,期间还尝试了自己实现tabBar,但是在看到那令人呕呕呕的效果之后,我还是放弃了,又回到原生的tabBar,然后静下心来想了想,最后在对比github上的一些mpvue的项目之后,发现原来是图标icon的问题,最后成功解决: 就是icon尺寸保持不变,然后四周留出合适的透明(?)空白 ...很简单有木有?就这浪费我很多脑细胞,原谅我的愚钝(智障脸)。。。当然了,原生的tabBar其实还有一个问题就是, tabBar的标题文字在真机上会离底部特别特别近 ,这个我没找到解决办法,除了自己实现tabBar。。。
### 详情页数据保留之前旧数据的问题 复制代码
这个问题我想很多同学都遇到过了,而且我看到mpvue github上的issues里面有很多人都遇到了这个问题并且都在持续关注,足以说明这是个痛点问题,谁让它会影响小程序的用户体验呢。。。到目前为止看到的比较统一的解决办法就是:在(详情)页面onLoad的时候,将要在本页面展示的数据初始化并且进行新的赋值,举:chestnut:如下:
<template> <html-text :text="htmltext"></html-text> </template> <script> import htmlText from xxxxx export default { components: { htmlText }, data () { return { htmltext: '' } }, onl oad () { this.htmltext = '' this.$http.get('xxxxxxxx').then((res) => { this.htmltext = res.htmltext }) } } </script> 复制代码
其他数组或者对象类型的处理可能会麻烦一些,但是方法类似,在数据请求返回之前的这段时间内不想留空白尬对用户的话就自己做一些loading,总是要强过用户先面对旧数据再一闪跳到新数据的体验。。。
### created钩子函数在项目初始化时就全部执行的问题 复制代码
这个我想应该是mpvue的一个bug吧?该钩子函数在页面内还是不要随便用的好。。。
### 目前mpvue对于复杂富文本的支持目前性能较差的问题 复制代码
### 微信原生的路由跳转navigateTo(),redirectTo(),navigateBack(),switchTab(),reLaunch()等,在真机上的表现较为怪异 复制代码
对于参数的传递,我也遇到过类似于旧数据的问题,最后不得已借助于vuex才得以解决。另外小程序的页面栈个数实在有限,所以在开发时一定要注意页面栈的管理。
### onShow()的使用要注意 复制代码
要记得该钩子函数里的js代码不只是刚进入页面时会执行,在息屏后再次点亮后也将会执行。
对于mpvue的坑突然能想起来的不多了,目前就先写这么多,后面想起来了再来更新吧。
在小程序的开发中,并没有使用小程序原生的wx.request()来进行数据交互,而是选择了mpvue文档里推荐使用的Flyio,Flyio的介绍就不多做介绍,打架可以自己看文档,这里我主要说一下的 请求和响应拦截器的构造 :
文档里其实有很详细的介绍以及代码,但是我根据代码写下来之后在遇到登录失效的问题时并没有按照预想的解决:先锁住请求然后重新请求拿到新的cookie之后再重新进行之前的请求,再和其他人讨论之后使用promise解决了这一问题,具体可见代码:
src/api/ajax.js:
/** * http请求拦截器 */ const Fly = require('flyio/dist/npm/wx') const config = require('./config') const ajaxUrl = process.env.NODE_ENV === 'development' ? config.Host.development : process.env.NODE_ENV === 'production' ? config.Host.production : config.Host.test let fly = new Fly() let loginFly = new Fly() // 定义公共headers const headers = { ... } Object.assign(fly.config, { headers: headers, baseURL: 'xxxxxx', timeout: 10000, withCredentials: true }) loginFly.config = fly.config // session失效后本地重新登录 const login = () => { return new Promise((resolve, reject) => { wx.login({ success: res => { let loginParams = { ... } loginFly.post('/api/locallogin/url', loginParams).then(d => { if (d.headers && typeof d.headers['set-cookie'] !== 'undefined') { // 更新session wx.setStorageSync('sessionid', d.headers['set-cookie']) } resolve() }).catch(error => { log(error) reject(res.data) }) }, fail: res => { console.error(res.errMsg) }, complete: res => {} }) }) } // 请求拦截器 fly.interceptors.request.use(request => { if (wx.getStorageSync('sessionid')) { request.headers.cookie = wx.getStorageSync('sessionid') } return request }) // 响应拦截器 fly.interceptors.response.use( response => { // session已经失效,需要重新登录小程序 if (response.data.errCode === 100009) { // log('session失效,根据之前存储在本地的用户信息重新请求session...') // 锁定响应拦截器 fly.lock() return login().then(() => { fly.unlock() // log(`重新请求:path:${response.request.url},baseURL:${response.request.baseURL}`) return fly.request(response.request) }).catch(err => { log(err) }) } else { return response.data.data } }, err => { log('error-interceptor', err) if (err.status) { wx.showToast({ title: '出现未知错误', icon: 'none', duration: 3000 }) } } ) export default fly 复制代码
因为是生活购物类小程序,涉及到 购物车 + 地址选择 等较为复杂的逻辑,很多地方都需要数据共用,在本期项目中vuex起了很大的作用,因为模块较多,如果将所有数据写在一个文件里无疑会为后期维护带来巨大困难,所以将各模块的数据单独划分写在各自的文件里,这样整体流程就清晰了很多,下面是划分模块的主文件的代码
src/store/index.js:
import Vue from 'vue' import Vuex from 'vuex' import modules1 from './modules/modules1' import modules2 from './modules/modules2' import modules3 from './modules/modules3' ... Vue.use(Vuex) export default new Vuex.Store({ // 做模块化处理,每个功能一个store.js文件,然后统一在这边引入 modules: { modules1, modules2, modules3, ... } }) 复制代码
src/store/modules/modules1.js:
import api from '@/api' // actions里请求用到 const state = { aaaa, ... } const getters = { aaaa (state) { return state.aaaa }, bbbb (state, getters, rootState) { return getters.aaaa }, ... } // actions里可进行异步操作 const actions = { async anExample ({state, getters, dispatch, commit}, {params}) { let res = await api.requestFunction({params}) ... return res }, ... } const mutations = { setStateX (state, Y) { state.X = Y }, ... } export default { namespaced: true, // 很重要 state, getters, actions, mutations } 复制代码
在.vue文件中调用
src/pages/xxx.vue
<script> import { mapState, mapGetters } from 'vuex' export default { computed: { // 调用getters ...mapGetters('modules', [ 'aaaa', 'bbbb' ]) }, methods: { // 调用action funcA () { this.$store.dispatch('modules1/anExample', {params}).then(res => { ... }) }, // 调用mutation funcB () { this.$store.commit('modules1/setStateX', Y) } } } </script> 复制代码
小程序工具提供多类型商城/门店小程序制作,可视化编辑 1秒生成5步上线。通过拖拽、拼接模块布局小程序商城页面,所看即所得,只需要美工就能做出精美商城。更多小程序商店请查看:小程序商店
全国7x24小时客服热线
所有故障均24小时内解决
项目一次性收费安心
技术人员均从业5年以上
通过技术营销传播企业服务价值
丰富的行业实战经验积累
基于需求研发多款产品
针对需求提供精细化服务