mpvue + vant + flyio 小程序項目總結


vant 的使用

  我開始是 npm 導入,然后 import,使用不了。

  找了各種方法,最后還是下載文件,然后找到 dist 文件夾,復制到項目里,我是放在 static 文件夾,文件名 dist 重命名為 vant。

  在 app.json 里添加需要的組件,這是全局添加的,可以全局使用這些組件

"usingComponents": {
    "van-button": "../static/vant/button/index",
    "van-cell": "../static/vant/cell/index",
    "van-cell-group": "../static/vant/cell-group/index",
    "van-radio": "../static/vant/radio/index",
    "van-radio-group": "../static/vant/radio-group/index",
    "van-transition": "../static/vant/transition/index"
  }

 

flyio 的使用

  以前用的 axios,也沒有仔細研究過,這次自己封裝頭痛不已。

  我們使用 token 做登錄信息(id 在 token 里面),做了一個單獨的登錄接口,獲取 token,token 存在本地,並且 token 會過期。

  接口自動判斷是否需要 token ,如果需要就去拿,放在 header 里發過去,header 里添加新字段,如果有特殊字符比如下划線等,就需要 nginx 配置,比較不麻煩,駝峰命名比較方便。

  如果本地沒有 token 數據,就需要先暫停請求,重新獲取 token,然后繼續之前的請求。

  如果響應返回 token 過期,就需要重新獲取 token ,並且重新自動發起之前的請求。

  請求與 token 之間的矛盾(fly 的暫停是使用攔截器的鎖死,攔截器都停了,獲取 token 的接口自然也就用不了了),我們需要在封裝的時候把獲取 token 的接口單獨使用 fly 對象

import Fly from 'flyio/dist/npm/wx'
import {
  _GetToken
} from "@/api/apiList";

const tokenFly = new Fly();
tokenFly.config.timeout = 10000; //設置超時
tokenFly.config.baseURL = process.env.API_ROOT; //填寫域名

// 添加請求攔截器
tokenFly.interceptors.request.use(request => {
  request.headers = {
    "X-Tag": "flyio",
    'Content-Type': "application/x-www-form-urlencoded",
  };
  return request
}, error => {
  Promise.reject(error)
})

// 添加響應攔截器
tokenFly.interceptors.response.use(response => {
  // 統一處理一些響應的code狀態
  if (response.data.data.loginToken) {
    wx.setStorage({
      key: "token",
      data: response.data.data.loginToken
    })
  }

  return response.data
}, err => {
  // 請求出錯,根據返回狀態碼判斷出錯原因
  if (err.status == 0) {
    wx.showToast({
      title: `網絡連接異常`,
      icon: 'none',
      duration: 5000
    })
  } else if (err.status == 1) {
    wx.showToast({
      title: `網絡連接超時`,
      icon: 'none',
      duration: 5000
    })
  } else if (err && err.response) {
    wx.showToast({
      title: `連接錯誤,請稍后再試! ${err.response.status}`,
      icon: 'none',
      duration: 5000
    })
  }
  return Promise.resolve(err)
})


const fly = new Fly()
fly.config.timeout = 10000; //設置超時
fly.config.baseURL = process.env.API_ROOT; //填寫域名

// 添加請求攔截器
fly.interceptors.request.use(request => {
  request.headers = {
    "X-Tag": "flyio",
    'Content-Type': "application/x-www-form-urlencoded",
  };

  let url = request.url;
  if (url.indexOf("login") > -1) {
    wx.removeStorageSync('token')
    request.headers.loginToken = "";
  }
  if (url.indexOf("user") > -1 && url.indexOf("login") < 0) {
    if (!wx.getStorageSync('token')) {
      console.log('沒有token,先請求token...');
      fly.lock(); //鎖定當前實例,后續請求會在攔截器外排隊 
      return getToken().then(() => {
          wx.hideLoading()
          request.headers.loginToken = wx.getStorageSync('token');
          return request
        })
        .finally(() => {
          fly.unlock() // 解鎖當前fly實例
        })
    } else {
      request.headers.loginToken = wx.getStorageSync('token');
    }
  }
  return request
}, error => {
  Promise.reject(error)
})

// 添加響應攔截器
fly.interceptors.response.use(response => {
  // 統一處理一些響應的code狀態
  if (response.data.data.loginToken) {
    wx.setStorage({
      key: "token",
      data: response.data.data.loginToken
    })
  }
  if (response.data.code == -2) {
    wx.showLoading({
      title: '本地登錄已失效'
    })
    console.log('本地已有token失效,即將請求新的token')
    fly.lock() // 鎖定當前fly實例
    return getToken().then(() => {
        wx.hideLoading()
        console.log('token已更新')
      })
      .finally(() => {
        fly.unlock() // 解鎖當前fly實例
      })
      .then(() => {
        console.log(`重新請求:path:${response.request.url},baseURL:${response.request.baseURL}`)
        return fly.request(response.request)
      })
  }
  return response.data
}, err => {
  // 請求出錯,根據返回狀態碼判斷出錯原因
  if (err.status == 0) {
    wx.showToast({
      title: `網絡連接異常`,
      icon: 'none',
      duration: 5000
    })
  } else if (err.status == 1) {
    wx.showToast({
      title: `網絡連接超時`,
      icon: 'none',
      duration: 5000
    })
  } else if (err && err.response) {
    wx.showToast({
      title: `連接錯誤,請稍后再試! ${err.response.status}`,
      icon: 'none',
      duration: 5000
    })
  }
  return Promise.resolve(err)
})

const getToken = () => {
  return new Promise((resolve) => {
    wx.getUserInfo({
      success: function (infoRes) {
        console.log(infoRes);
        wx.login({
          success(loginRes) {
            console.log(loginRes);
            if (loginRes.code) {
              wx.showLoading({
                title: '正在重新登錄'
              })
              tokenFly.post('/pub/api/login', {
                code: loginRes.code,
                encryptedData: infoRes.encryptedData,
                iv: infoRes.iv,
                parentId: wx.getStorageSync("fromId") || ""
              }).then((flyReq) => {
                console.log(flyReq);
                console.log("token請求成功")
                wx.showLoading({
                  title: '重新登錄已完成'
                })
                resolve()
              })
            } else {
              console.log("登錄失敗!" + loginRes.errMsg);
            }
          }
        });
      },
      fail: function (res) {
        console.log(res);
        wx.navigateTo({
          url: "../auth/main"
        });
      }
    });
  })
}

export default fly

 

mpvue 的使用

  1. 在 pages 里新建頁面,每個頁面都是單獨文件夾,里面有一個 index.vue 和 main.js。頁面寫在 index.vue 中,main.js 不需要更改。
  2. 全局 css 和 iconfont 圖標放在 assets 文件夾內,在 main.js 中導入就好了。
  3. 在 app.json 里,頁面的路徑應該寫成  "pages/xxx/main",在頁面中,路徑的跳轉相對路徑("../xxx/main")。

頁面之間的傳參

// 頁面跳轉帶參
      wx.navigateTo({
        url: `../exercise/main?levelId=${item.id}`
      });

// 頁面路徑參數獲取
this.$root.$mp.query.levelId

圖片的樣式

  height 不會自適應,我添加了原生小程序的 mode 屬性,實在不行就設置 height 吧。

按鈕 bind 綁定的寫法

  按鈕的原生屬性 bindxxxxxxxxx,bind 在 mpvue 中寫成 @ 就好了,

<button open-type="getUserInfo" @getuserinfo="getAuth" class="auth-btn">開始使用</button>

生命周期

  生命周期 destory 和 onUnlod,頁面之間的切換,如果之前的頁面有彈出框,切換到其他的頁面然后再返回,這個彈出框還是顯示的,之前的頁面的數據是沒被銷毀的。

登錄授權

  小程序改版之后,wx.getUserInfo 不會彈出授權框了,需要用戶手動觸發。我們做了一個授權頁,在首頁進入下一個頁面的時候,判斷本地是否有個人信息,如果沒有就去拿用戶信息,如果失敗就去授權頁。

// 判斷去授權頁
    goMajorPath() {
      // console.log(item);
      let that = this;
      if (this.wxUserInfo && this.xfxUserInfo) {
        wx.navigateTo({
          url: `../major/main`
        });
        return;
      }

      // 拿用戶信息,失敗就去授權
      wx.getUserInfo({
        success: function(infoRes) {
          console.log(infoRes);
          that.CURMAJOR(item);
          that.WXUSERINFO(infoRes.userInfo); // 存儲用戶信息

          wx.navigateTo({
            url: `../major/main`
          });
        },
        fail: function(res) {
          console.log(res);
          wx.navigateTo({
            url: "../auth/main"
          });
        }
      });
    }

 

// 授權邏輯
    getAuth(e) {
      let that = this;
      if (e.mp.detail.userInfo) {
        console.log("授權通過");
        wx.hideToast();
        this.WXUSERINFO(e.mp.detail.userInfo); // 存儲用戶信息
        this.action_XFXUSERINFO();
        wx.navigateBack({
          delta: 1
        });
      } else {
        console.log("拒絕授權");
        wx.showToast({
          title: "鑒於您拒絕授權,我們是無法保存您的答題數據的!",
          icon: "none",
          duration: 5000
        });
      }
    }

手機號獲取

  獲取手機號,也是單獨做了一個頁面,手動觸發獲取手機號

// 去手機號獲取頁
    goLevelPath(item) {
      console.log(item);
      console.log(this.xfxUserInfo.phone);
      let that = this;
      if (this.xfxUserInfo.phone) {
        that.CURMAJOR(item);
        wx.navigateTo({
          url: `../level/main?majorId=${item.id}`
        });
        return;
      }

      //沒有手機號跳轉請求頁面
      wx.navigateTo({
        url: "../phone/main"
      });
    }

 

// 手機號獲取
    getPhone(e) {
      // console.log(e);
      // console.log(e.mp.detail);
      if (e.mp.detail.errMsg == "getPhoneNumber:ok") {
        this.action_savePhone(e.mp.detail);
        this.action_XFXUSERINFO();
        wx.navigateBack({
          delta: 1
        });
      } else {
        console.log("拒絕授權");
        wx.showToast({
          title: "鑒於您拒絕快捷登錄,我們是無法保存您的答題數據的!",
          icon: "none",
          duration: 5000
        });
      }
    }

分享

  此功能實現傳播,也算是核心業務了。分為頂部按鈕分享和自定義按鈕分享。需要在 mounted 生命周期設置分享功能,在 onShareAppMessage 生命周期寫業務。自定義按鈕需設置 open-type="share"

  mounted() {
    wx.showShareMenu({
      withShareTicket: true
    });
  },
  onShareAppMessage: function(res) {
    console.log(res); //menu  沒有 tartget
    if (res.from === "button") {
      return {
        title: `恭喜${this.xfxUserInfo.username},挑戰[${
          this.curMajor.name
        }]專業-${this.curLevel.levelName}${
          this.coreNum >= 80 ? "成功!" : "完成,請再接再厲!"
        }`,
        path: `/pages/index/main?fromId=${this.xfxUserInfo.id}`
      };
    }
    if (res.from === "menu") {
      return {
        title: "一起打卡學習,每天進步一點點",
        path: `/pages/index/main?fromId=${this.xfxUserInfo.id}`
      };
    }
  }

  分享的傳播標記,分享的路徑是可以帶參的,這個參數就是分享傳播者的標記,當新用戶從這里進來,路徑中是有這個標記的,我們直接在 App.vue 的 onLaunch 生命周期中,存儲改標記到本地,當用戶授權的時候和用戶信息一起存起來。該生命周期還可以判斷場景值,我是直接判斷的參數。

  onLaunch(opt) {
    console.log(opt);
    if (opt.query.fromId) {
      wx.setStorage({
        key: "fromId",
        data: opt.query.fromId
      });
    }
  },

迭代更新

    onLaunch(opt) {
      //調用微信接口檢查是否有新版本
      const updateManager = wx.getUpdateManager();

      updateManager.onCheckForUpdate(function (res) {
        // 請求完新版本信息的回調
        console.log(res.hasUpdate);
      });

      updateManager.onUpdateReady(function () {
        wx.showModal({
          title: "更新提示",
          content: "新版本已經准備好,是否重啟應用?",
          success: function (res) {
            if (res.confirm) {
              // 新的版本已經下載好,調用 applyUpdate 應用新版本並重啟
              updateManager.applyUpdate();
            }
          }
        });
      });

      updateManager.onUpdateFailed(function () {
        // 新版本下載失敗
        wx.showModal({
          title: "更新提示",
          content: "新版本下載失敗",
          showCancel: false
        });
      });
    },

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM