mpvue開(kāi)發(fā)小程序的難點(diǎn)重點(diǎn)總結(jié)
我們?cè)偈褂胢pvue開(kāi)發(fā)小程序會(huì)遇到許多困難,下面就一些困難做一個(gè)關(guān)于mpvue開(kāi)發(fā)小程序的難點(diǎn)重點(diǎn)總結(jié)
框架的選擇
原生的小程序我本人并沒(méi)有學(xué)習(xí)過(guò),更別提拿來(lái)開(kāi)發(fā)一款商用的小程序了,剛好還在前公司時(shí),當(dāng)時(shí)的前端團(tuán)隊(duì)在提到小程序的解決方案時(shí)有分享了mpvue,到了新公司之后技術(shù)老大也有提到mpvue,而我本人過(guò)去一年多也一直在寫(xiě)vue,對(duì)vue寫(xiě)法比較熟悉,而且新公司團(tuán)隊(duì)對(duì)小程序期待已久,希望盡快上架,所以選擇mpvue來(lái)開(kāi)發(fā)也是最快最合理的了!
項(xiàng)目的搭建
看了mpvue的官方文檔,項(xiàng)目的搭建自然也選擇了官方推薦的 vue-cli , 在看了五分鐘上手教程后,使用命令
vue init mpvue/mpvue-quickstart my-project生成了基本的項(xiàng)目,在后來(lái)的開(kāi)發(fā)中,項(xiàng)目的配置基本沒(méi)做改動(dòng),只是添加了less-loader。
目錄結(jié)構(gòu)
基本上是vue-cli生成的目錄結(jié)構(gòu),加了部分文件夾,主要是與后臺(tái)進(jìn)行數(shù)據(jù)交互所使用的框架flyio的配置文件夾(api文件夾),以及整個(gè)項(xiàng)目數(shù)據(jù)管理所使用的vuex(store文件夾),整體目錄結(jié)構(gòu)如下:
project └───build └───config └───dist └───node_modules └───src └───api | ajax.js // flyio請(qǐng)求與響應(yīng)攔截器的配置文件 | config.js // 請(qǐng)求的配置文件 | index.js // 生成請(qǐng)求api實(shí)例文件 | Server.js // 項(xiàng)目的數(shù)據(jù)請(qǐng)求統(tǒng)一管理文件 └───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 復(fù)制代碼
踩到的坑
相信很多使用過(guò)mpvue的同學(xué)都或多或少猜到了一些坑,我也是踩到了不少的坑浪費(fèi)了不少的寶貴時(shí)間,雖然網(wǎng)上關(guān)于mpvue的踩坑的文章一搜一籮筐,但我還是要寫(xiě)一下。。。下面就是我在本次小程序開(kāi)發(fā)過(guò)程中遇到的坑(們)以及針對(duì)它們的解決方案:
### tabBar圖標(biāo)問(wèn)題 復(fù)制代碼
在 配置小程序原生的底部tabBar 時(shí),遇到了第一個(gè)問(wèn)題:在將設(shè)計(jì)師給我的圖標(biāo)icon路徑設(shè)置正確的情況下, 開(kāi)發(fā)者工具上的tabBar的圖標(biāo)總是會(huì)很大,而且?guī)缀跽紳M了整個(gè)高度 ,相當(dāng)難看,搜了很多博客都沒(méi)有找到解決辦法,期間還嘗試了自己實(shí)現(xiàn)tabBar,但是在看到那令人嘔嘔嘔的效果之后,我還是放棄了,又回到原生的tabBar,然后靜下心來(lái)想了想,最后在對(duì)比github上的一些mpvue的項(xiàng)目之后,發(fā)現(xiàn)原來(lái)是圖標(biāo)icon的問(wèn)題,最后成功解決: 就是icon尺寸保持不變,然后四周留出合適的透明(?)空白 ...很簡(jiǎn)單有木有?就這浪費(fèi)我很多腦細(xì)胞,原諒我的愚鈍(智障臉)。。。當(dāng)然了,原生的tabBar其實(shí)還有一個(gè)問(wèn)題就是, tabBar的標(biāo)題文字在真機(jī)上會(huì)離底部特別特別近 ,這個(gè)我沒(méi)找到解決辦法,除了自己實(shí)現(xiàn)tabBar。。。
### 詳情頁(yè)數(shù)據(jù)保留之前舊數(shù)據(jù)的問(wèn)題 復(fù)制代碼
這個(gè)問(wèn)題我想很多同學(xué)都遇到過(guò)了,而且我看到mpvue github上的issues里面有很多人都遇到了這個(gè)問(wèn)題并且都在持續(xù)關(guān)注,足以說(shuō)明這是個(gè)痛點(diǎn)問(wèn)題,誰(shuí)讓它會(huì)影響小程序的用戶體驗(yàn)?zāi)?。。。到目前為止看到的比較統(tǒng)一的解決辦法就是:在(詳情)頁(yè)面onLoad的時(shí)候,將要在本頁(yè)面展示的數(shù)據(jù)初始化并且進(jìn)行新的賦值,舉:chestnut:如下:
<template> <html-text :text="htmltext"></html-text> </template> <script> import htmlText from xxxxx export default { components: { htmlText }, data () { return { htmltext: '' } }, onLoad () { this.htmltext = '' this.$http.get('xxxxxxxx').then((res) => { this.htmltext = res.htmltext }) } } </script> 復(fù)制代碼
其他數(shù)組或者對(duì)象類型的處理可能會(huì)麻煩一些,但是方法類似,在數(shù)據(jù)請(qǐng)求返回之前的這段時(shí)間內(nèi)不想留空白尬對(duì)用戶的話就自己做一些loading,總是要強(qiáng)過(guò)用戶先面對(duì)舊數(shù)據(jù)再一閃跳到新數(shù)據(jù)的體驗(yàn)。。。
### created鉤子函數(shù)在項(xiàng)目初始化時(shí)就全部執(zhí)行的問(wèn)題 復(fù)制代碼
這個(gè)我想應(yīng)該是mpvue的一個(gè)bug吧?該鉤子函數(shù)在頁(yè)面內(nèi)還是不要隨便用的好。。。
### 目前mpvue對(duì)于復(fù)雜富文本的支持目前性能較差的問(wèn)題 復(fù)制代碼
### 微信原生的路由跳轉(zhuǎn)navigateTo(),redirectTo(),navigateBack(),switchTab(),reLaunch()等,在真機(jī)上的表現(xiàn)較為怪異 復(fù)制代碼
對(duì)于參數(shù)的傳遞,我也遇到過(guò)類似于舊數(shù)據(jù)的問(wèn)題,最后不得已借助于vuex才得以解決。另外小程序的頁(yè)面棧個(gè)數(shù)實(shí)在有限,所以在開(kāi)發(fā)時(shí)一定要注意頁(yè)面棧的管理。
### onShow()的使用要注意 復(fù)制代碼
要記得該鉤子函數(shù)里的js代碼不只是剛進(jìn)入頁(yè)面時(shí)會(huì)執(zhí)行,在息屏后再次點(diǎn)亮后也將會(huì)執(zhí)行。
對(duì)于mpvue的坑突然能想起來(lái)的不多了,目前就先寫(xiě)這么多,后面想起來(lái)了再來(lái)更新吧。
Flyio的使用
在小程序的開(kāi)發(fā)中,并沒(méi)有使用小程序原生的wx.request()來(lái)進(jìn)行數(shù)據(jù)交互,而是選擇了mpvue文檔里推薦使用的Flyio,F(xiàn)lyio的介紹就不多做介紹,打架可以自己看文檔,這里我主要說(shuō)一下的 請(qǐng)求和響應(yīng)攔截器的構(gòu)造 :
文檔里其實(shí)有很詳細(xì)的介紹以及代碼,但是我根據(jù)代碼寫(xiě)下來(lái)之后在遇到登錄失效的問(wèn)題時(shí)并沒(méi)有按照預(yù)想的解決:先鎖住請(qǐng)求然后重新請(qǐng)求拿到新的cookie之后再重新進(jìn)行之前的請(qǐng)求,再和其他人討論之后使用promise解決了這一問(wèn)題,具體可見(jiàn)代碼:
src/api/ajax.js:
/** * http請(qǐng)求攔截器 */ 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 => {} }) }) } // 請(qǐng)求攔截器 fly.interceptors.request.use(request => { if (wx.getStorageSync('sessionid')) { request.headers.cookie = wx.getStorageSync('sessionid') } return request }) // 響應(yīng)攔截器 fly.interceptors.response.use( response => { // session已經(jīng)失效,需要重新登錄小程序 if (response.data.errCode === 100009) { // log('session失效,根據(jù)之前存儲(chǔ)在本地的用戶信息重新請(qǐng)求session...') // 鎖定響應(yīng)攔截器 fly.lock() return login().then(() => { fly.unlock() // log(`重新請(qǐng)求: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: '出現(xiàn)未知錯(cuò)誤', icon: 'none', duration: 3000 }) } } ) export default fly 復(fù)制代碼
vuex的使用
因?yàn)槭巧钯?gòu)物類小程序,涉及到 購(gòu)物車(chē) + 地址選擇 等較為復(fù)雜的邏輯,很多地方都需要數(shù)據(jù)共用,在本期項(xiàng)目中vuex起了很大的作用,因?yàn)槟K較多,如果將所有數(shù)據(jù)寫(xiě)在一個(gè)文件里無(wú)疑會(huì)為后期維護(hù)帶來(lái)巨大困難,所以將各模塊的數(shù)據(jù)單獨(dú)劃分寫(xiě)在各自的文件里,這樣整體流程就清晰了很多,下面是劃分模塊的主文件的代碼
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({ // 做模塊化處理,每個(gè)功能一個(gè)store.js文件,然后統(tǒng)一在這邊引入 modules: { modules1, modules2, modules3, ... } }) 復(fù)制代碼
src/store/modules/modules1.js:
import api from '@/api' // actions里請(qǐng)求用到 const state = { aaaa, ... } const getters = { aaaa (state) { return state.aaaa }, bbbb (state, getters, rootState) { return getters.aaaa }, ... } // actions里可進(jìn)行異步操作 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 } 復(fù)制代碼
在.vue文件中調(diào)用
src/pages/xxx.vue
<script> import { mapState, mapGetters } from 'vuex' export default { computed: { // 調(diào)用getters ...mapGetters('modules', [ 'aaaa', 'bbbb' ]) }, methods: { // 調(diào)用action funcA () { this.$store.dispatch('modules1/anExample', {params}).then(res => { ... }) }, // 調(diào)用mutation funcB () { this.$store.commit('modules1/setStateX', Y) } } } </script> 復(fù)制代碼
HiShop小程序工具提供多類型商城/門(mén)店小程序制作,可視化編輯 1秒生成5步上線。通過(guò)拖拽、拼接模塊布局小程序商城頁(yè)面,所看即所得,只需要美工就能做出精美商城。更多小程序商店請(qǐng)查看:小程序商店