第一個mpvue小程式開發完了,來總結下吧

飄逸丶麥子發表於2019-03-04

    歷經了接近兩個月的摸索滾爬,抓頭吃瓜,各種優化(單押X3),我主導開發的第一個小程式終於要上線了(SKR SKR!)!當然首先要感謝老闆沒有殺了我——因為在6月初我剛拿到小程式PRD的時候老闆問我多久可以做好,我看了看之後說“兩週”吧,咳咳,然後。。。一直到現在,我還能活著很Amazing有沒有???當然這其中也有一些為了追求“精品小程式”而一改再改所用的時間。好了,廢話還是不多說了,下面就開始總結下吧~

1.框架的選擇

    原生的小程式我本人並沒有學習過,更別提拿來開發一款商用的小程式了,剛好還在前公司時,當時的前端團隊在提到小程式的解決方案時有分享了mpvue,到了新公司之後技術老大也有提到mpvue,而我本人過去一年多也一直在寫vue,對vue寫法比較熟悉,而且新公司團隊對小程式期待已久,希望儘快上架,所以選擇mpvue來開發也是最快最合理的了!

2.專案的搭建

    看了mpvue的官方文件,專案的搭建自然也選擇了官方推薦的vue-cli, 在看了五分鐘上手教程後,使用命令
vue init mpvue/mpvue-quickstart my-project
生成了基本的專案,在後來的開發中,專案的配置基本沒做改動,只是新增了less-loader。

3.目錄結構

基本上是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  
複製程式碼

4.踩到的坑

    相信很多使用過mpvue的同學都或多或少猜到了一些坑,我也是踩到了不少的坑浪費了不少的寶貴時間,雖然網上關於mpvue的踩坑的文章一搜一籮筐,但我還是要寫一下。。。下面就是我在本次小程式開發過程中遇到的坑(們)以及針對它們的解決方案:

4-1.tabBar圖示問題

    在配置小程式原生的底部tabBar時,遇到了第一個問題:在將設計師給我的圖示icon路徑設定正確的情況下,開發者工具上的tabBar的圖示總是會很大,而且幾乎佔滿了整個高度,相當難看,搜了很多部落格都沒有找到解決辦法,期間還嘗試了自己實現tabBar,但是在看到那令人嘔嘔嘔的效果之後,我還是放棄了,又回到原生的tabBar,然後靜下心來想了想,最後在對比github上的一些mpvue的專案之後,發現原來是圖示icon的問題,最後成功解決:就是icon尺寸保持不變,然後四周留出合適的透明(?)空白…很簡單有木有?就這浪費我很多腦細胞,原諒我的愚鈍(智障臉)。。。當然了,原生的tabBar其實還有一個問題就是,tabBar的標題文字在真機上會離底部特別特別近,這個我沒找到解決辦法,除了自己實現tabBar。。。

4-2.詳情頁資料保留之前舊資料的問題

    這個問題我想很多同學都遇到過了,主要是因為mpvue中頁面跳轉後並沒有銷燬頁面例項,而是將其推入頁面棧中,所以會儲存之前的舊的資料,而且我看到mpvue github上的issues裡面有很多人都遇到了這個問題並且都在持續關注,足以說明這是個痛點問題,誰讓它會影響小程式的使用者體驗呢。。。到目前為止看到的比較統一的解決辦法就是:在(詳情)頁面onLoad的時候,將要在本頁面展示的資料初始化並且進行新的賦值,舉?如下:

<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>
複製程式碼

    其他陣列或者物件型別的處理可能會麻煩一些,但是方法類似,在資料請求返回之前的這段時間內不想留空白尬對使用者的話就自己做一些loading,總是要強過使用者先面對舊資料再一閃跳到新資料的體驗。。。

4-3.created鉤子函式在專案初始化時就全部執行的問題

    這個我想應該是mpvue的一個bug吧?該鉤子函式在頁面內還是不要隨便用的好。。。

4-4.目前mpvue對於複雜富文字的支援目前效能較差的問題

    這個問題不能甩鍋給mpvue,對於展示“相當複雜”的富文字(內容較長,且由多張圖片甚至多張動圖)的需求,一般不會有很多使用者會遇到,但是很不巧的是,我遇到了。。。誰讓我們致力於做一個有逼格的品牌呢?有需求了就要解決,光能展示遠遠不夠,還得展示的優雅,目前的mpvue-wxParse 其實已經能解決大部分問題了,也有一些github上的專案基於該專案開發得到了數百star,但是我用該專案做出來的效果老闆和技術老大都相當不滿意,圖片沒法優雅的載入,而且由於htmltext太長在圖片全部解析顯示出來之前有著相當長的白屏尷尬時間,所以最後還是放棄該方案。
    然後在github上找到了另一個在mpvue-wxParse 的基礎上改進的針對複雜富文字的專案mpvue-htmlParse,試了下稍微好了點,但離老闆的要求還是差很遠,最後不得已在此專案基礎上fork出一份程式碼針對老闆的需求親自來改,最終得以過關,專案地址mpvue-htmlParse,該專案裡主要針對圖片的載入做了改進,在第一張圖片載入完成後,通知主頁可以關閉preLoading效果,然後給每張圖片新增了菊花的載入效果,在圖片完全載入完成之前會顯示菊花圖,然後再根據裝置螢幕寬度和圖片資訊對圖片進行適當放大或者縮小,這樣整體下來的效果基本可以達到“破產版”微信公眾號推文的效果,該專案適用範圍有限,有需要的同學可以自己在此基礎上改進。

4-5.微信原生的路由跳轉navigateTo(),redirectTo(),navigateBack(),switchTab(),reLaunch()等,在真機上的表現較為怪異

    對於引數的傳遞,我也遇到過類似於舊資料的問題,最後不得已藉助於vuex才得以解決。另外小程式的頁面棧個數實在有限,所以在開發時一定要注意頁面棧的管理。

4-6.onShow()的使用要注意

    要記得該鉤子函式裡的js程式碼不只是剛進入頁面時會執行,在息屏後再次點亮後也將會執行。

對於mpvue的坑突然能想起來的不多了,目前就先寫這麼多,後面想起來了再來更新吧。

5.Flyio的使用

    在小程式的開發中,並沒有使用小程式原生的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: ajaxUrl,
  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
複製程式碼

// 建立api例項
src/api/index.js:

import Server from `./Server.js`

class Api {
  constructor () {
    Object.assign(this, ...Array.from(arguments))
  }
}

export default new Api(Server)

複製程式碼

6.vuex的使用

    因為是生活購物類小程式,涉及到購物車+地址選擇等較為複雜的邏輯,很多地方都需要資料共用,在本期專案中vuex起了很大的作用,因為模組較多,如果將所有資料寫在一個檔案裡無疑會為後期維護帶來巨大困難,所以將各模組的資料單獨劃分寫在各自的檔案裡,這樣整體流程就清晰了很多,下面是劃分模組的主檔案的程式碼
src/api/Server.js:

/**
 * 本模組主要用於與服務端進行互動
 */
import ajax from `./ajax.js`
async function requestFunction ({params}) {
    let res = await ajax.get(`/request/url/`, {params})
    ...
    return res.data
}
export default {
    requestFunction
}
複製程式碼

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>
複製程式碼

7.總結

    本次總結目前先寫這麼多吧,主要介紹了專案結構遇到的坑(專案中遇到的問題很多,但是寫的時候突然覺得那些都不是問題了?),Flyio的使用(重點為攔截器的配置),以及vuex的簡單介紹。其實專案開發完成之後想了想也沒那麼多東西,只是期間走了不少的彎路,做了很多“無用功”,其實說是無用功,但也從中收穫了相當多,畢竟自己從無到有從0到1構建一個專案,其中的煩惱很多,但是真的能讓人成長很多,也讓我覺得相當充實。由於小程式是公司商用的不是我個人的專案,所以專案程式碼就沒法開源了,如果有問題的話可以聯絡我,為防廣告之嫌這裡也不說明小程式的名字了,想來體驗下的可以私信我,也歡迎大家來指正!

    老闆來找我過第二期的需求了,Incoming!

相關文章