微信小程式歸結

HappyCodingTop發表於2022-12-18

是的,在這個框架滿天飛的年代,我既然有有幸使用了原生小程式開發專案,除了麻煩些,倒也不是一無所獲,耕耘總有收貨嘛,寫部落格本身不是為了炫技還是什麼,單純的是記性不好,有些知識點 自己是花了時間去查詢的,時間久了,下次會忘,所以僅做記錄的成份高一些,前言不搭後語莫怪。然後早上看到一句詩也不錯:追風趕月莫停留,平蕪盡頭是春山。
回正題吧:

上傳頭像

changeAvatar() {    
var that = this;    
wx.chooseImage({      
count: 1, // 最多可以選擇的圖片張數,預設9     
 sizeType: ['original', 'compressed'], 
// original 原圖,compressed 壓縮圖,預設二者都有     
 sourceType: ['album', 'camera'],
 // album 從相簿選圖,camera 使用相機,預設二者都有     
 success: async function (res) {       
 var avatar = res.tempFilePaths;       
 await that.filebBatchDeletes()        
let resImage = await uploadImage(avatar[0])       
 if (resImage.data.code == 200) {          
that.setData({            
avatar: avatar[0],           
 picId: resImage.data.result         
 })       
 }     
 },      
fail: function () {        
// console.log("上傳圖片沒有成功");    
  },      
complete: function () {       
 // complete      
}    })  },


export const uploadImage = (uploadFile: string) => { 
 return new Promise((resolve, reject) => {   
 wx.uploadFile({     
 url: config.otherBaseUrl + 'load/file-upload',  
    filePath: uploadFile,     
 header: {      
  "Content-Type": "multipart/form-data",     
   'token': wx.getStorageSync('accessToken'),   
   },    
  formData: {       
 'caseNum': wx.getStorageSync('userId'),      
  "caseType": 'image',     
 },     
 name: 'file',    
  success: (res) => {     
   const data = JSON.parse(res.data) 
       resolve({ data })    
  },      
fail: (err) => {     
   reject(err)      
}    
})  
})}

前端小程式拿到圖片流處理成Base64async

 readImageByDataIds(id: string) {   
 let res = await getPicStream(`/readImageByDataId/${id}`) 
   this.setData({     
 processPic: res  
  })  },


export const getPicStream = (url:string) => {
  return new Promise((resolve,reject) => {   
 wx.request({    
  url: config.baseUrl + url,    
  header: {        
'X-Access-Token': wx.getStorageSync('token'),  
      'X-TIMESTAMP': getDateTimeToString(),    
  },    
  responseType: 'arraybuffer',    
  success: res => {        
let url ='data:image/png;base64,'+ wx.arrayBufferToBase64(res.data)    
    resolve(url)    
  },     
 fail: err => {   
     reject(err)      
}    
}) 
 })}

返回上一個頁面並觸發上一個頁面的方法因為小程式不像瀏覽器會自動重新整理,所以需要手動重新整理

var pages = getCurrentPages();    
  var beforePage = pages[pages.length - 2];  
    wx.navigateBack({      
  delta: 1,        
 success: function () {     
     beforePage.getInfo(); // 執行前一個頁面的getInfo方法      
  }     
 })

Base64形式的檔案做預覽

preClick(event: any) {   
 if (event.detail.type == 'file') {  
    const fileSystemManager = wx.getFileSystemManager() 
  try {    
    let strPath = event.detail.url.substring(event.detail.url.indexOf(',')     + 1)  
fileSystemManager.writeFileSync(
  wx.env.USER_DATA_PATH + `/${event.detail.materialName}`, strPath,"base64");    
wx.openDocument({          
     filePath: wx.env.USER_DATA_PATH + `/${event.detail.materialName}`       
 })      
} catch (err) {       
 console.log("呼叫失敗", err);   
}   
 }  
  return true 
 }

v-button自定義透明用於繫結或獲取使用者資訊

<button bind:getuserinfo="onGetUserInfo" open-type='{{openType}}'  plain='{{true}}' class="container"> 
 <slot name="img"></slot>
</button>

Component({  /**   * 元件的屬性列表   */  
options: {  
  multipleSlots: true // 在元件定義時的選項中啟用多slot支援  
},  
// externalClasses: ['ex-btn-class'],  
properties: {    
openType: {    
  type: String 
   },   
 imageSrc: {   
   type: String   
 },  
  bindgetuserinfo: {   
   type: String    
}  }, 
 /**   * 元件的初始資料   */  
data: {  },  
  /**   * 元件的方法列表   */  
methods: {    
onGetUserInfo(event) { 
     this.triggerEvent('getuserinfo', event.detail, {})    
},  
}})
.container{  padding: 0 !important;  border:none !important;}

一個小程式跳轉到另一個小程式

async getPayInfos(item:any) {   
 const { buildId, id, estateId } = item
    let params = {    
  buildId,   
   estateId,  
    houseId: id, 
   }    
let infos = await getPayInfo(params) 
   let that = this   
 wx.navigateToMiniProgram({   
   appId: 'wxe7550',  //appid   
   path: `pages/index/index?token=${infos.data.payData}`,//path   
   success(res) {     
   that.data.isPay = true 
   that.data.mchOrderNo = infos.data.mchOrderNo   
   }    
}) 
 },

wxs

wxs是小程式的一套指令碼語言 ,結合WXML,可以構建出頁面的結構  wxs不依賴於執行時的基礎庫版本  可以在所有版本的小程式中執行wxs與javascript是不同的語言  有自己的語法 並不和javascript一致wxs的執行環境和其他javascript程式碼是隔離的  wxs不能呼叫其他JavaScript檔案中定義的函式  也不能呼叫小程式提供的APIwxs函式不能作為元件的事件回撥與es5相似,不能用es6的語法這裡作為單獨檔案使用
圖片

圖片

圖片
也可直接寫在wxml檔案中

<block wx:for="{{util.limit(comments,15)}}">  
      <tag-cmp class="tag" text="{{item.content}}">        
  <text class="num" slot="after">{{'+' + item.nums}}</text>       
 </tag-cmp>  
</block>
<wxs module="util"> 
 var limit = function(array, length) {  
  return array.slice(0, length)  
} 
 var format = function(text){ 
   if(!text){      return    }    
var reg = getRegExp('\\\\n','g')
    var text = text.replace(reg,'\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')  
  return text  }  
module.exports = {    limit: limit,    format:format  }
</wxs>

屬於wxs的正則
圖片

點選事件

  • bind事件 catch事件
  • bind事件繫結不會阻止事件冒泡   
  • catch事件可以阻止事件向上冒泡

    <view bind:tab="handleClick"></view>   
    <view catch:tab="handleClick"></view>wx:for遍歷 <block wx:for="{{data}}">  
    <v-item item="{{item}}"></v-item> 
    </block>

    表單校驗要自己寫表單,所以也要自己寫校驗

    使用的是WxValidate.jsWxValidate的驗證規則: 
    (1)required:true/false,是否為必填欄位。
    (2)email:true/false,是否遵守電子郵件格式。
    (3)tel:true/false,是否遵守11位手機號碼。
    (4)url:true/false,是否遵守域名格式。
    (5)idcarad:true/false,是否遵守18位身份證號格式。
    (6)digits: true/false,只能輸入數字。
    (7)min:數值,指定最小值。
    (8)max:數值,指定最大值。
    (9)range:[min,max],指定範圍。
    (10)minlength:數值,指定最少輸入的字元個數。
    (11)maxlength:數值,指定最多輸入的字元個數。
    (12)rangelength:[minlength,maxlength],指定輸入字元個數的範圍。
    (13)dateISO:true/false,說服遵守日期格式(yyyy-mm-dd、yyyy/mm/dd)。
    (14)equalTo:字串,指定必須輸入完全相同的內容。
    (15)contains:字串,指定必須輸入包含指定字串的內容。 

  • 方法就不說了,簡單且繁瑣,為什麼只寫規則,規則不好找?*

    小程式的跳轉

    小程式的跳轉分為兩種:頁面跳轉   保留當前頁面,跳轉到應用內的某個頁面

    wx.navigateTo({ url: "/pages/login/login" })

    關閉當前頁面,跳轉到應用內的某個頁面
    `wx.redirectTo({
    url: ''
    }`
    關閉所有頁面,開啟到應用內的某個頁面

    wx.reLaunch 方法則會清空當前的堆疊 透過wx.navigateBack沒有可返回的頁面了

    底部tab跳轉跳轉到 tabBar 頁面,並關閉其他所有非 tabBar 頁面
    `wx.switchTab({
    url: '/index'
    })`

    小程式的事件傳參

    data- 自定義屬性傳參,其中  代表的是引數的名字例子:

    <view class="homeItem" bindtap="goList" data-showType="add">

    事件內可透過e.currentTarget.dataset.showtype拿到值

    goList(e: any) {
    cosole.log(e.currentTarget.dataset.showtype) 
    //注意這裡showtype 可不是寫錯,而是小程式會把小駝峰大寫轉成小寫
    }

    onPullDownRefresh onReachBottom  下拉重新整理 上拉載入

    Page({ freshList(){    
    let myList = this.selectComponent("#yg-list")    
    myList.pullList()  
    },  
    onReachBottom: function() {    
    let myList = this.selectComponent("#yg-list")   
     myList.getList()  
    },  
    onPullDownRefresh:function(){   
     this.freshList()  
    },

    可在app.json配置在距離什麼位置觸發

    "window": {   
     "backgroundTextStyle": "light",   
     "navigationBarBackgroundColor": "#fff",    
    "navigationBarTitleText": "",    
    "navigationBarTextStyle": "black",   
     "enablePullDownRefresh": true,//開啟下拉重新整理    "onReachBottomDistance":100,//可在app.json配置在距離什麼位置觸發  
    },

    本來以為這個沒有什麼難的,但是還是踩坑了

  • 若頁面不小心寫了兩個onReachBottom, 這個事件是不會觸發的
  • 上拉載入,下拉重新整理只能用在page頁面元件中,在Component元件裡面不觸發的?所以在page頁面監聽到到達頁面的底部,如何通知列表元件的載入更多,引出下面的小程式中觸發子元件的方式

    小程式中觸發子元件的方法

    那這裡用上上個例子的程式碼

    <yg-list applyStatus="1" id="yg-list"/>
    onReachBottom: function() {
    //滾動到底部通知子元件載入新的資料    
    let myList = this.selectComponent("#yg-list")   
    myList.getList()  
    }

    小程式元件中子元件觸發父元件的的方法,

    在這裡觸發indexFunction函式

     <button bindTap="clickTap">點選</button>   
     clickTap:function(){      
    this.triggerEvent('indexFunction',{value:this.properties.count})    
    }

在父元件中

<yg-list bind:indexFunction="indexFunction"/>     
  Page({     
 indexFunction:function(e){        
console.log('父容器中的方法被呼叫了',e);     
 }  
  })

wx:if 與 hidden

*wx:if 與hidden都可以控制微信小程式中元素的顯示與否。
wx:if不滿足條件是不渲染,hidden是隱藏對於效能這一塊,
hidden頁面的隱藏代替頁面跳轉,不會重新觸發ready  或者created生命週期。
所以具體用法與vue的v-if和v-show相似*

小程式元件

如果寫小程式每個頁面純手寫重複的列表,表單,太臃腫,
所以肯定要寫元件,元件的使用方式:
外層建立components資料夾,在裡面寫元件,
如果其中一個頁面需要使用這個元件,可在當前頁面的json中配置

{  "usingComponents": {    "yg-list":"/components/yg-list/yg-list"  }}

元件傳值 properties 類似於vue的props

//在properties裡面定義我們要的屬性
 properties: {
    btText: {
      value: '預設值',//value表示預設值
      type: String   //type是我們定義的型別,這裡是String字串型別
    }
  },

元件自身的方法定義在methods,同vue一樣

methods: {
   showLog:function(){
   }
 }
元件使用插槽 
Comment({      
options:{        
muitipleSlots:true  //開啟插槽
}   
})


<view class="container">    
<slot name="before"></slot>
<text>{{text}}</text>  
 <slot name="after"></slot> 
</view>
使用
<v-tag> 
<text text="哈哈" slot="after">{{text}}</text>
</v-tag>

元件的外部樣式externalClasses的使用

*子元件定義一個外部樣式名字 
子元件的元素用上這個類名*

Comment({     
externalClasses:['my-class']   
 })


<view class="my-class">{{text}}</view>

在父元件使用這個元件的時候 是在父元件中定義的樣式

<yg-list my-class="yg-class"></yg-list>
.yg-class{color:red;}

*注意:若是當前子元件的元素上還有別的樣式,那元件內的樣式會覆蓋外部樣式,
所以在父容器編寫樣式的時候,後面可加上impotant,提升等級關係。*

元件的behaviorbehaviors

  • 是小程式中,用於實現元件間程式碼共享的特性,
  • 類似於 Vue.js 中的 “mixins”
  • 每個 behaviors 可以包含一組屬性、資料、生命週期函式和方法。
  • 元件引用它時,它的屬性、資料和方法會被合併到元件中,每個元件可以引用多個behaviors,- - behavior也可以引用其它behavior。
  • 在外層建立一個資料夾behavior,裡面可放各個用途的behavior js檔案,
    建立一個behavior檔案

    let classicBeh = Behavior({    
      properties:{         
     img:String,         
     content:String       
     },       
     data:{},    
      methods:{}   
     })    
    export {
    classicBeh
    }
    在元件中使用 
    import { classicBeh } from '../behaviors/classicBeh.js'   
     Comment({      
    behaviors: 
    [classicBeh]    
    })

    behavior與元件的優先順序若遇到重名的情況,元件會覆蓋behavior,
    但是如果是生命週期,會先執行behavior內的,再執行元件內的

    小程式中引入lodash

    在utils資料夾下新建lodash.js
    檔案把壓縮過的lodash.min.js檔案內容放進去直接引入會報錯
    再建立一個lodash-fix.js檔案

    /** * 修復 微信小程式中lodash 的執行環境 */
    global.Object = Object;
    global.Array = Array;
    // global.Buffer = Buffer
    global.DataView = DataView;
    global.Date = Date;
    global.Error = Error;
    global.Float32Array = Float32Array;
    global.Float64Array = Float64Array;
    global.Function = Function;
    global.Int8Array = Int8Array;
    global.Int16Array = Int16Array;
    global.Int32Array = Int32Array;
    global.Map = Map;
    global.Math = Math;
    global.Promise = Promise;
    global.RegExp = RegExp;
    global.Set = Set;
    global.String = String;
    global.Symbol = Symbol;
    global.TypeError = TypeError;
    global.Uint8Array = Uint8Array;
    global.Uint8ClampedArray = Uint8ClampedArray;
    global.Uint16Array = Uint16Array;
    global.Uint32Array = Uint32Array;
    global.WeakMap = WeakMap;
    global.clearTimeout = clearTimeout;
    global.isFinite = isFinite;
    global.parseInt = parseInt;
    global.setTimeout = setTimeout;
    使用import "../../utils/lodash-fix.js"
    import _ from "../../utils/lodash"

    使用

     getBuildList: _.throttle(function (str: any) {   
     const { cellId, searchCell } = this.data   
     lifePayModels.getBuild({ estateId: cellId, buildName: searchCell },
     (res => {     
     let arr = res.map(item => {    
      return {         
     label: item.buildName, 
           value: item.id       
     }      
    })     
     this.setData({   
       columns: arr   
     })   
     })) 
     }, 3000),

    第三方庫

    Vant  Weappvan-dialog
    van-field model:value支援雙向繫結
    van-dialog給裡面的輸入框做校驗的時候,它會關閉,所以這裡用上beforeClose阻止預設關閉事件

    <van-dialog use-slot 
      data-showType="rejectShow" 
      show="{{rejectShow}}"
      show-cancel-button
      confirm-button-open-type="toReject" 
      beforeClose="beforeClose"
      bind:close="closeDialog">    <van-field 
       model:value="{{rejectComment}}"
       label="" 
       type="textarea"   
       placeholder="駁回原因" 
       autosize   
       required/>
    </van-dialog>
    Page({     
     data: {        
      beforeClose (action) {          
      return new Promise(resolve => {            
      setTimeout(() => {              
      if (action === 'confirm') {               
     // 攔截確認操作                
      resolve(false)              
      } else {                
      resolve(true)             
       }            
      }, 0)         
       })        
      }    
    
    },    
     toReject(){ 
         if(!this.data.comment){
            tips('請填寫原因')         
           return      
    }        
      this.setData({     
       show:false     
     })     
       }   
     })

    微信支付

    圖片
    具體的做法:

  • 開啟某小程式,點選直接下單 wx.login獲取使用者臨時登入憑證code,傳送到後端伺服器換取
  • openId 在下單時,小程式需要將購買的商品Id,商品數量,以及使用者的openId傳送到伺服器
  • 在下單時,小程式需要將購買的商品Id,商品數量,以及使用者的openId傳送到伺服器
  • 伺服器在接收到商品Id、商品數量、openId後,生成服務期訂單資料,同時經過一定的簽名演算法,向微信支付傳送請求,獲取預付單資訊(prepay_id),同時將獲取的資料再次進行相應規則的簽名,向小程式端響應必要的資訊 
  • 小程式端在獲取對應的引數後,呼叫wx.requestPayment()發起微信支付,喚醒支付工作臺,進行支付  
    6 接下來的一些列操作都是由使用者來操作的包括了微信支付密碼,指紋等驗證,確認支付之後執行鑑權調起支付
    7鑑權調起支付:在微信後臺進行鑑權,微信後臺直接返回給前端支付的結果,前端收到返回資料後對支付結果進行展示
    8 推送支付結果:微信後臺在給前端返回支付的結果後,也會向後臺也返回一個支付結果,後臺透過這個支付結果來更新訂單的狀態
    9 其中後端響應資料必要的資訊則是wx.requestPayment方法所需要的引數,大致如下:``wx.requestPayment({
    // 時間戳
    timeStamp: '',
    // 隨機字串
    nonceStr: '',
    // 統一下單介面返回的 prepay_id 引數值
    package: '',
    // 簽名型別
    signType: '',
    // 簽名
    paySign: '',
    // 呼叫成功回撥
    success () {},
    // 失敗回撥
    fail () {},
    // 介面呼叫結束回撥
    complete () {}
    })``
    注意:以上資訊中timeStamp、nonceStr、prepay_id、signType、paySign各引數均建議必須都由服務端返回(這樣會盡最大可能性保證簽名資料一致性),小程式端不做任何處理
    官方支付文件地址https://pay.weixin.qq.com/wik...
    後端的操作 https://juejin.cn/post/706331...

    引入離線iconfont字型圖示

https://blog.csdn.net/qq_1506...

域名不合法的解決

小程式剛開始開發的時候測試環境一般都是開啟
不檢驗合法域名但是當我們發測試版本或者發生產環境的時候,
發的小程式是不請求介面的,這是因為我們沒有設定合法域名,
在這裡設定,注意域名必須是https的
圖片

掃二維碼調到小程式指定頁面首先微信平臺設定

設定位置: 開發管理 開發設定(最後生成連線釋出的時候要確定 程式碼已經線上上)
圖片

圖片
然後可以拿著這個url去生成二維碼
圖片
然後在當前頁面接受引數,判斷是否已繫結手機號,登入

onLoad(options: any) {  
  if (options.q) {     
 let url = decodeURIComponent(options.q)   
   let obj = this.getUrlParam(url)    
  this.initPhone()    
  this.setData({      
  cellName: obj.estateName,   
     cellId: obj.estateId,      
  isRead: true     
 })   
 }
},  
getUrlParam(url) {  
  let params = url.split("?")[1].split("&");  
  let obj = {};  
  params.map(v => (obj[v.split("=")[0]] = v.split("=")[1]));    return obj 
 }

wx.nextTick

延遲一部分操作到下一個時間片再執行
使用方法同vue的this.$nextTick

 getUrlParam(url) {
    let params = url.split("?")[1].split("&");
    let obj = {};
    params.map(v => (obj[v.split("=")[0]] = v.split("=")[1]));
    return obj
  },
onLoad(options: any) {
    this.initPhone()
    if (options.q) {
      let url = decodeURIComponent(options.q)
      console.log("url", url);
      let obj = this.getUrlParam(url)
      if (Reflect.ownKeys(obj).length > 0) {
        wx.nextTick(() => {
          this.setData({
            cellName: obj.estateName,
            cellId: obj.estateId,
            isRead: true,
          })
        })
      }
    }
  },

最後如果當前文章給了你提示和幫助,還希望點贊和關注,鼓勵,謝謝啦

相關文章